Unify duplicate Breadth-First-Search traversing of the LayeredPainter and SmoothEleva...
[0ad.git] / source / ps / Profile.h
blobbcee5f8a4701f05a7b660924555c009d529353b3
1 /* Copyright (C) 2013 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * GPG3-style hierarchical profiler
22 #ifndef INCLUDED_PROFILE
23 #define INCLUDED_PROFILE
25 #include <vector>
27 #include "lib/adts/ring_buf.h"
28 #include "ps/Profiler2.h"
29 #include "ps/Singleton.h"
30 #include "ps/ThreadUtil.h"
32 #include <boost/flyweight.hpp>
33 #include <boost/flyweight/key_value.hpp>
34 #include <boost/flyweight/no_locking.hpp>
35 #include <boost/flyweight/no_tracking.hpp>
37 #define PROFILE_AMORTIZE_FRAMES 30
38 #define PROFILE_AMORTIZE_TURNS 1
40 class CProfileManager;
41 class CProfileNodeTable;
43 class CStr8;
44 class CStrW;
46 // To profile scripts usefully, we use a call hook that's called on every enter/exit,
47 // and need to find the function name. But most functions are anonymous so we make do
48 // with filename plus line number instead.
49 // Computing the names is fairly expensive, and we need to return an interned char*
50 // for the profiler to hold a copy of, so we use boost::flyweight to construct interned
51 // strings per call location.
53 // TODO: Check again how much the overhead for getting filename and line really is and if
54 // it has increased with the new approach after the SpiderMonkey 31 upgrade.
56 // Flyweight types (with no_locking because the call hooks are only used in the
57 // main thread, and no_tracking because we mustn't delete values the profiler is
58 // using and it's not going to waste much memory)
59 typedef boost::flyweight<
60 std::string,
61 boost::flyweights::no_tracking,
62 boost::flyweights::no_locking
63 > StringFlyweight;
65 class CProfileNode
67 NONCOPYABLE(CProfileNode);
69 friend class CProfileManager;
70 friend class CProfileNodeTable;
72 const char* name;
74 int calls_frame_current;
75 int calls_turn_current;
76 RingBuf<int, PROFILE_AMORTIZE_FRAMES> calls_per_frame;
77 RingBuf<int, PROFILE_AMORTIZE_TURNS> calls_per_turn;
79 double time_frame_current;
80 double time_turn_current;
81 RingBuf<double, PROFILE_AMORTIZE_FRAMES> time_per_frame;
82 RingBuf<double, PROFILE_AMORTIZE_TURNS> time_per_turn;
84 long mallocs_frame_current;
85 long mallocs_turn_current;
86 RingBuf<long, PROFILE_AMORTIZE_FRAMES> mallocs_per_frame;
87 RingBuf<long, PROFILE_AMORTIZE_TURNS> mallocs_per_turn;
89 double start;
90 long start_mallocs;
91 int recursion;
93 CProfileNode* parent;
94 std::vector<CProfileNode*> children;
95 std::vector<CProfileNode*> script_children;
96 CProfileNodeTable* display_table;
98 public:
99 typedef std::vector<CProfileNode*>::iterator profile_iterator;
100 typedef std::vector<CProfileNode*>::const_iterator const_profile_iterator;
102 CProfileNode( const char* name, CProfileNode* parent );
103 ~CProfileNode();
105 const char* GetName() const { return name; }
107 double GetFrameCalls() const;
108 double GetFrameTime() const;
109 double GetTurnCalls() const;
110 double GetTurnTime() const;
111 double GetFrameMallocs() const;
112 double GetTurnMallocs() const;
114 const CProfileNode* GetChild( const char* name ) const;
115 const CProfileNode* GetScriptChild( const char* name ) const;
116 const std::vector<CProfileNode*>* GetChildren() const { return( &children ); }
117 const std::vector<CProfileNode*>* GetScriptChildren() const { return( &script_children ); }
119 bool CanExpand();
121 CProfileNode* GetChild( const char* name );
122 CProfileNode* GetScriptChild( const char* name );
123 CProfileNode* GetParent() const { return( parent ); }
125 // Resets timing information for this node and all its children
126 void Reset();
127 // Resets frame timings for this node and all its children
128 void Frame();
129 // Resets turn timings for this node and all its children
130 void Turn();
131 // Enters the node
132 void Call();
133 // Leaves the node. Returns true if the node has actually been left
134 bool Return();
137 class CProfileManager : public Singleton<CProfileManager>
139 CProfileNode* root;
140 CProfileNode* current;
142 bool needs_structural_reset;
144 void PerformStructuralReset();
146 public:
147 CProfileManager();
148 ~CProfileManager();
150 // Begins timing for a named subsection
151 void Start( const char* name );
152 void StartScript( const char* name );
154 // Ends timing for the current subsection
155 void Stop();
157 // Resets all timing information
158 void Reset();
159 // Resets frame timing information
160 void Frame();
161 // Resets turn timing information
162 // (Must not be called before Frame)
163 void Turn();
164 // Resets absolutely everything, at the end of this frame
165 void StructuralReset();
167 inline const CProfileNode* GetCurrent() { return( current ); }
168 inline const CProfileNode* GetRoot() { return( root ); }
171 #define g_Profiler CProfileManager::GetSingleton()
173 class CProfileSample
175 public:
176 CProfileSample(const char* name)
178 if (CProfileManager::IsInitialised())
180 // The profiler is only safe to use on the main thread
181 ENSURE(ThreadUtil::IsMainThread());
183 g_Profiler.Start(name);
186 ~CProfileSample()
188 if (CProfileManager::IsInitialised())
189 g_Profiler.Stop();
193 class CProfileSampleScript
195 public:
196 CProfileSampleScript( const char* name )
198 if (CProfileManager::IsInitialised())
200 // The profiler is only safe to use on the main thread,
201 // but scripts get run on other threads too so we need to
202 // conditionally enable the profiler.
203 // (This usually only gets used in debug mode so performance
204 // doesn't matter much.)
205 if (ThreadUtil::IsMainThread())
206 g_Profiler.StartScript( name );
209 ~CProfileSampleScript()
211 if (CProfileManager::IsInitialised())
212 if (ThreadUtil::IsMainThread())
213 g_Profiler.Stop();
217 // Put a PROFILE("xyz") block at the start of all code to be profiled.
218 // Profile blocks last until the end of the containing scope.
219 #define PROFILE(name) CProfileSample __profile(name)
220 // Cheat a bit to make things slightly easier on the user
221 #define PROFILE_START(name) { CProfileSample __profile(name)
222 #define PROFILE_END(name) }
224 // Do both old and new profilers simultaneously (1+2=3), for convenience.
225 #define PROFILE3(name) PROFILE(name); PROFILE2(name)
227 // Also do GPU
228 #define PROFILE3_GPU(name) PROFILE(name); PROFILE2(name); PROFILE2_GPU(name)
230 #endif // INCLUDED_PROFILE