Yves
c02a7e1a7b
This upgrade also introduces exact stack rooting (see to the wiki: JSRootingGuide) and fixes problems with moving GC. This allows us to enable generational garbage collection (GGC). Measurements a few months ago have shown a performance improvement of a non-visual replay of around 13.5%. This probably varies quite a bit, but it should be somewhere between 5-20%. Memory usage has also been improved. Check the forum thread for details. Thanks to everyone from the team who helped with this directly or indirectly (review, finding and fixing issues, the required C++11 upgrade, the new autobuilder etc.)! Also thanks to the SpiderMonkey developers who helped on the #jsapi channel or elsewhere! Fixes #2462, #2415, #2428, #2684, #1374 Refs #2973, #2669 This was SVN commit r16214.
231 lines
6.5 KiB
C++
231 lines
6.5 KiB
C++
/* Copyright (C) 2013 Wildfire Games.
|
|
* This file is part of 0 A.D.
|
|
*
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* GPG3-style hierarchical profiler
|
|
*/
|
|
|
|
#ifndef INCLUDED_PROFILE
|
|
#define INCLUDED_PROFILE
|
|
|
|
#include <vector>
|
|
|
|
#include "lib/adts/ring_buf.h"
|
|
#include "ps/Profiler2.h"
|
|
#include "ps/Singleton.h"
|
|
#include "ps/ThreadUtil.h"
|
|
|
|
#include <boost/flyweight.hpp>
|
|
#include <boost/flyweight/key_value.hpp>
|
|
#include <boost/flyweight/no_locking.hpp>
|
|
#include <boost/flyweight/no_tracking.hpp>
|
|
|
|
#define PROFILE_AMORTIZE_FRAMES 30
|
|
#define PROFILE_AMORTIZE_TURNS 1
|
|
|
|
class CProfileManager;
|
|
class CProfileNodeTable;
|
|
|
|
class CStr8;
|
|
class CStrW;
|
|
|
|
// To profile scripts usefully, we use a call hook that's called on every enter/exit,
|
|
// and need to find the function name. But most functions are anonymous so we make do
|
|
// with filename plus line number instead.
|
|
// Computing the names is fairly expensive, and we need to return an interned char*
|
|
// for the profiler to hold a copy of, so we use boost::flyweight to construct interned
|
|
// strings per call location.
|
|
//
|
|
// TODO: Check again how much the overhead for getting filename and line really is and if
|
|
// it has increased with the new approach after the SpiderMonkey 31 upgrade.
|
|
//
|
|
// Flyweight types (with no_locking because the call hooks are only used in the
|
|
// main thread, and no_tracking because we mustn't delete values the profiler is
|
|
// using and it's not going to waste much memory)
|
|
typedef boost::flyweight<
|
|
std::string,
|
|
boost::flyweights::no_tracking,
|
|
boost::flyweights::no_locking
|
|
> StringFlyweight;
|
|
|
|
class CProfileNode
|
|
{
|
|
NONCOPYABLE(CProfileNode);
|
|
|
|
friend class CProfileManager;
|
|
friend class CProfileNodeTable;
|
|
|
|
const char* name;
|
|
|
|
int calls_frame_current;
|
|
int calls_turn_current;
|
|
RingBuf<int, PROFILE_AMORTIZE_FRAMES> calls_per_frame;
|
|
RingBuf<int, PROFILE_AMORTIZE_TURNS> calls_per_turn;
|
|
|
|
double time_frame_current;
|
|
double time_turn_current;
|
|
RingBuf<double, PROFILE_AMORTIZE_FRAMES> time_per_frame;
|
|
RingBuf<double, PROFILE_AMORTIZE_TURNS> time_per_turn;
|
|
|
|
long mallocs_frame_current;
|
|
long mallocs_turn_current;
|
|
RingBuf<long, PROFILE_AMORTIZE_FRAMES> mallocs_per_frame;
|
|
RingBuf<long, PROFILE_AMORTIZE_TURNS> mallocs_per_turn;
|
|
|
|
double start;
|
|
long start_mallocs;
|
|
int recursion;
|
|
|
|
CProfileNode* parent;
|
|
std::vector<CProfileNode*> children;
|
|
std::vector<CProfileNode*> script_children;
|
|
CProfileNodeTable* display_table;
|
|
|
|
public:
|
|
typedef std::vector<CProfileNode*>::iterator profile_iterator;
|
|
typedef std::vector<CProfileNode*>::const_iterator const_profile_iterator;
|
|
|
|
CProfileNode( const char* name, CProfileNode* parent );
|
|
~CProfileNode();
|
|
|
|
const char* GetName() const { return name; }
|
|
|
|
double GetFrameCalls() const;
|
|
double GetFrameTime() const;
|
|
double GetTurnCalls() const;
|
|
double GetTurnTime() const;
|
|
double GetFrameMallocs() const;
|
|
double GetTurnMallocs() const;
|
|
|
|
const CProfileNode* GetChild( const char* name ) const;
|
|
const CProfileNode* GetScriptChild( const char* name ) const;
|
|
const std::vector<CProfileNode*>* GetChildren() const { return( &children ); }
|
|
const std::vector<CProfileNode*>* GetScriptChildren() const { return( &script_children ); }
|
|
|
|
bool CanExpand();
|
|
|
|
CProfileNode* GetChild( const char* name );
|
|
CProfileNode* GetScriptChild( const char* name );
|
|
CProfileNode* GetParent() const { return( parent ); }
|
|
|
|
// Resets timing information for this node and all its children
|
|
void Reset();
|
|
// Resets frame timings for this node and all its children
|
|
void Frame();
|
|
// Resets turn timings for this node and all its children
|
|
void Turn();
|
|
// Enters the node
|
|
void Call();
|
|
// Leaves the node. Returns true if the node has actually been left
|
|
bool Return();
|
|
};
|
|
|
|
class CProfileManager : public Singleton<CProfileManager>
|
|
{
|
|
CProfileNode* root;
|
|
CProfileNode* current;
|
|
|
|
bool needs_structural_reset;
|
|
|
|
void PerformStructuralReset();
|
|
|
|
public:
|
|
CProfileManager();
|
|
~CProfileManager();
|
|
|
|
// Begins timing for a named subsection
|
|
void Start( const char* name );
|
|
void StartScript( const char* name );
|
|
|
|
// Ends timing for the current subsection
|
|
void Stop();
|
|
|
|
// Resets all timing information
|
|
void Reset();
|
|
// Resets frame timing information
|
|
void Frame();
|
|
// Resets turn timing information
|
|
// (Must not be called before Frame)
|
|
void Turn();
|
|
// Resets absolutely everything, at the end of this frame
|
|
void StructuralReset();
|
|
|
|
inline const CProfileNode* GetCurrent() { return( current ); }
|
|
inline const CProfileNode* GetRoot() { return( root ); }
|
|
};
|
|
|
|
#define g_Profiler CProfileManager::GetSingleton()
|
|
|
|
class CProfileSample
|
|
{
|
|
public:
|
|
CProfileSample(const char* name)
|
|
{
|
|
if (CProfileManager::IsInitialised())
|
|
{
|
|
// The profiler is only safe to use on the main thread
|
|
ENSURE(ThreadUtil::IsMainThread());
|
|
|
|
g_Profiler.Start(name);
|
|
}
|
|
}
|
|
~CProfileSample()
|
|
{
|
|
if (CProfileManager::IsInitialised())
|
|
g_Profiler.Stop();
|
|
}
|
|
};
|
|
|
|
class CProfileSampleScript
|
|
{
|
|
public:
|
|
CProfileSampleScript( const char* name )
|
|
{
|
|
if (CProfileManager::IsInitialised())
|
|
{
|
|
// The profiler is only safe to use on the main thread,
|
|
// but scripts get run on other threads too so we need to
|
|
// conditionally enable the profiler.
|
|
// (This usually only gets used in debug mode so performance
|
|
// doesn't matter much.)
|
|
if (ThreadUtil::IsMainThread())
|
|
g_Profiler.StartScript( name );
|
|
}
|
|
}
|
|
~CProfileSampleScript()
|
|
{
|
|
if (CProfileManager::IsInitialised())
|
|
if (ThreadUtil::IsMainThread())
|
|
g_Profiler.Stop();
|
|
}
|
|
};
|
|
|
|
// Put a PROFILE("xyz") block at the start of all code to be profiled.
|
|
// Profile blocks last until the end of the containing scope.
|
|
#define PROFILE(name) CProfileSample __profile(name)
|
|
// Cheat a bit to make things slightly easier on the user
|
|
#define PROFILE_START(name) { CProfileSample __profile(name)
|
|
#define PROFILE_END(name) }
|
|
|
|
// Do both old and new profilers simultaneously (1+2=3), for convenience.
|
|
#define PROFILE3(name) PROFILE(name); PROFILE2(name)
|
|
|
|
// Also do GPU
|
|
#define PROFILE3_GPU(name) PROFILE(name); PROFILE2(name); PROFILE2_GPU(name)
|
|
|
|
#endif // INCLUDED_PROFILE
|