Irrlicht 3D Engine
IProfiler.h
Go to the documentation of this file.
1 // This file is part of the "Irrlicht Engine".
2 // For conditions of distribution and use, see copyright notice in irrlicht.h
3 // Written by Michael Zeilfelder
4 
5 #ifndef __I_PROFILER_H_INCLUDED__
6 #define __I_PROFILER_H_INCLUDED__
7 
8 #include "IrrCompileConfig.h"
9 #include "irrString.h"
10 #include "irrArray.h"
11 #include "ITimer.h"
12 #include <limits.h> // for INT_MAX (we should have a S32_MAX...)
13 
14 namespace irr
15 {
16 
17 class ITimer;
18 
21 {
22  friend class IProfiler;
23 
25  {
26  GroupIndex = 0;
27  reset();
28  }
29 
30  bool operator<(const SProfileData& pd) const
31  {
32  return Id < pd.Id;
33  }
34 
35  bool operator==(const SProfileData& pd) const
36  {
37  return Id == pd.Id;
38  }
39 
41  {
42  return GroupIndex;
43  }
44 
45  const core::stringw& getName() const
46  {
47  return Name;
48  }
49 
52  {
53  return CountCalls;
54  }
55 
58  {
59  return LongestTime;
60  }
61 
63  u32 getTimeSum() const
64  {
65  return TimeSum;
66  }
67 
68 private:
69 
70  // just to be used for searching as it does no initialization besides id
71  SProfileData(u32 id) : Id(id) {}
72 
73  void reset()
74  {
75  CountCalls = 0;
76  LongestTime = 0;
77  TimeSum = 0;
78  LastTimeStarted = 0;
79  StartStopCounter = 0;
80  }
81 
82  s32 Id;
83  u32 GroupIndex;
84  core::stringw Name;
85 
86  s32 StartStopCounter; // 0 means stopped > 0 means it runs.
87  u32 CountCalls;
88  u32 LongestTime;
89  u32 TimeSum;
90 
91  u32 LastTimeStarted;
92 };
93 
95 // Implementer notes:
96 // The design is all about allowing to use the central start/stop mechanism with minimal time overhead.
97 // This is why the class works without a virtual functions interface contrary to the usual Irrlicht design.
98 // And also why it works with id's instead of strings in the start/stop functions even if it makes using
99 // the class slightly harder.
100 // The class comes without reference-counting because the profiler instance is never released (TBD).
102 {
103 public:
105  IProfiler() : Timer(0), NextAutoId(INT_MAX)
106  {}
107 
108  virtual ~IProfiler()
109  {}
110 
112 
117  inline void add(s32 id, const core::stringw &name, const core::stringw &groupName);
118 
120 
124  inline s32 add(const core::stringw &name, const core::stringw &groupName);
125 
128  {
129  return ProfileDatas.size();
130  }
131 
133 
136  inline bool findDataIndex(u32 & result, const core::stringw &name) const;
137 
139 
141  {
142  return ProfileDatas[index];
143  }
144 
146 
148  inline const SProfileData* getProfileDataById(u32 id);
149 
151 
152  inline u32 getGroupCount() const
153  {
154  return ProfileGroups.size();
155  }
156 
158 
159  inline const SProfileData& getGroupData(u32 index) const
160  {
161  return ProfileGroups[index];
162  }
163 
165 
168  inline bool findGroupIndex(u32 & result, const core::stringw &name) const;
169 
170 
172 
175  inline void start(s32 id);
176 
178 
182  inline void stop(s32 id);
183 
185  inline void resetDataById(s32 id);
186 
188  inline void resetDataByIndex(u32 index);
189 
191  inline void resetGroup(u32 index);
192 
194 
195  inline void resetAll();
196 
198 
201  virtual void printAll(core::stringw &result, bool includeOverview=false,bool suppressUncalled=true) const = 0;
202 
204 
206  virtual void printGroup(core::stringw &result, u32 groupIndex, bool suppressUncalled) const = 0;
207 
208 protected:
209 
210  inline u32 addGroup(const core::stringw &name);
211 
212  // I would prefer using os::Timer, but os.h is not in the public interface so far.
213  // Timer must be initialized by the implementation.
217 
218 private:
219  s32 NextAutoId; // for giving out id's automatically
220 };
221 
223 
228 
230 
234 {
235 public:
237 
240  : Id(id), Profiler(getProfiler())
241  {
242  Profiler.start(Id);
243  }
244 
246 
250  CProfileScope(s32 id, const core::stringw &name, const core::stringw &groupName)
251  : Id(id), Profiler(getProfiler())
252  {
253  Profiler.add(Id, name, groupName);
254  Profiler.start(Id);
255  }
256 
258 
261  CProfileScope(const core::stringw &name, const core::stringw &groupName)
262  : Profiler(getProfiler())
263  {
264  Id = Profiler.add(name, groupName);
265  Profiler.start(Id);
266  }
267 
269  {
270  Profiler.stop(Id);
271  }
272 
273 protected:
276 };
277 
278 
279 // IMPLEMENTATION for in-line stuff
280 
282 {
283  s32 idx = ProfileDatas.binary_search(SProfileData(id));
284  if ( idx >= 0 && Timer )
285  {
286  ++ProfileDatas[idx].StartStopCounter;
287  if (ProfileDatas[idx].StartStopCounter == 1 )
288  ProfileDatas[idx].LastTimeStarted = Timer->getRealTime();
289  }
290 }
291 
293 {
294  if ( Timer )
295  {
296  u32 timeNow = Timer->getRealTime();
297  s32 idx = ProfileDatas.binary_search(SProfileData(id));
298  if ( idx >= 0 )
299  {
300  SProfileData &data = ProfileDatas[idx];
301  --ProfileDatas[idx].StartStopCounter;
302  if ( data.LastTimeStarted != 0 && ProfileDatas[idx].StartStopCounter == 0)
303  {
304  // update data for this id
305  ++data.CountCalls;
306  u32 diffTime = timeNow - data.LastTimeStarted;
307  data.TimeSum += diffTime;
308  if ( diffTime > data.LongestTime )
309  data.LongestTime = diffTime;
310  data.LastTimeStarted = 0;
311 
312  // update data of it's group
313  SProfileData & group = ProfileGroups[data.GroupIndex];
314  ++group.CountCalls;
315  group.TimeSum += diffTime;
316  if ( diffTime > group.LongestTime )
317  group.LongestTime = diffTime;
318  group.LastTimeStarted = 0;
319  }
320  else if ( ProfileDatas[idx].StartStopCounter < 0 )
321  {
322  // ignore additional stop calls
323  ProfileDatas[idx].StartStopCounter = 0;
324  }
325  }
326  }
327 }
328 
329 s32 IProfiler::add(const core::stringw &name, const core::stringw &groupName)
330 {
331  u32 index;
332  if ( findDataIndex(index, name) )
333  {
334  add( ProfileDatas[index].Id, name, groupName );
335  return ProfileDatas[index].Id;
336  }
337  else
338  {
339  s32 id = NextAutoId;
340  --NextAutoId;
341  add( id, name, groupName );
342  return id;
343  }
344 }
345 
346 void IProfiler::add(s32 id, const core::stringw &name, const core::stringw &groupName)
347 {
348  u32 groupIdx;
349  if ( !findGroupIndex(groupIdx, groupName) )
350  {
351  groupIdx = addGroup(groupName);
352  }
353 
354  SProfileData data(id);
355  s32 idx = ProfileDatas.binary_search(data);
356  if ( idx < 0 )
357  {
358  data.reset();
359  data.GroupIndex = groupIdx;
360  data.Name = name;
361 
362  ProfileDatas.push_back(data);
363  ProfileDatas.sort();
364  }
365  else
366  {
367  // only reset on group changes, otherwise we want to keep the data or coding CProfileScope would become tricky.
368  if ( groupIdx != ProfileDatas[idx].GroupIndex )
369  {
370  resetDataByIndex((u32)idx);
371  ProfileDatas[idx].GroupIndex = groupIdx;
372  }
373  ProfileDatas[idx].Name = name;
374  }
375 }
376 
378 {
379  SProfileData group;
380  group.Id = -1; // Id for groups doesn't matter so far
381  group.Name = name;
382  ProfileGroups.push_back(group);
383  return ProfileGroups.size()-1;
384 }
385 
386 bool IProfiler::findDataIndex(u32 & result, const core::stringw &name) const
387 {
388  for ( u32 i=0; i < ProfileDatas.size(); ++i )
389  {
390  if ( ProfileDatas[i].Name == name )
391  {
392  result = i;
393  return true;
394  }
395  }
396 
397  return false;
398 }
399 
401 {
402  SProfileData data(id);
403  s32 idx = ProfileDatas.binary_search(data);
404  if ( idx >= 0 )
405  return &ProfileDatas[idx];
406  return NULL;
407 }
408 
409 bool IProfiler::findGroupIndex(u32 & result, const core::stringw &name) const
410 {
411  for ( u32 i=0; i < ProfileGroups.size(); ++i )
412  {
413  if ( ProfileGroups[i].Name == name )
414  {
415  result = i;
416  return true;
417  }
418  }
419 
420  return false;
421 }
422 
424 {
425  s32 idx = ProfileDatas.binary_search(SProfileData(id));
426  if ( idx >= 0 )
427  {
428  resetDataByIndex((u32)idx);
429  }
430 }
431 
433 {
434  SProfileData &data = ProfileDatas[index];
435 
436  SProfileData & group = ProfileGroups[data.GroupIndex];
437  group.CountCalls -= data.CountCalls;
438  group.TimeSum -= data.TimeSum;
439 
440  data.reset();
441 }
442 
445 {
446  for ( u32 i=0; i<ProfileDatas.size(); ++i )
447  {
448  if ( ProfileDatas[i].GroupIndex == index )
449  ProfileDatas[i].reset();
450  }
451  if ( index < ProfileGroups.size() )
452  ProfileGroups[index].reset();
453 }
454 
456 {
457  for ( u32 i=0; i<ProfileDatas.size(); ++i )
458  {
459  ProfileDatas[i].reset();
460  }
461 
462  for ( u32 i=0; i<ProfileGroups.size(); ++i )
463  {
464  ProfileGroups[i].reset();
465  }
466 }
467 
472 #ifdef _IRR_COMPILE_WITH_PROFILING_
473  #define IRR_PROFILE(X) X
474 #else
475  #define IRR_PROFILE(X)
476 #endif // IRR_PROFILE
477 
478 } // namespace irr
479 
480 #endif // __I_PROFILER_H_INCLUDED__
bool operator<(const SProfileData &pd) const
Definition: IProfiler.h:30
virtual void printGroup(core::stringw &result, u32 groupIndex, bool suppressUncalled) const =0
Write the profile data of one group into a string.
u32 getTimeSum() const
Time spend between start/stop.
Definition: IProfiler.h:63
Used to store the profile data (and also used for profile group data).
Definition: IProfiler.h:20
Class where the objects profile their own life-time.
Definition: IProfiler.h:233
const core::stringw & getName() const
Definition: IProfiler.h:45
core::array< SProfileData > ProfileGroups
Definition: IProfiler.h:216
ITimer * Timer
Definition: IProfiler.h:214
#define IRRLICHT_API
Set FPU settings.
u32 getCallsCounter() const
Each time profiling for this data is stopped it increases the counter by 1.
Definition: IProfiler.h:51
IProfiler()
Constructor. You could use this to create a new profiler, but usually getProfiler() is used to access...
Definition: IProfiler.h:105
Everything in the Irrlicht Engine can be found in this namespace.
Definition: aabbox3d.h:12
u32 getProfileDataCount() const
Return the number of profile data blocks. There is one for each id.
Definition: IProfiler.h:127
Very simple string class with some useful features.
Definition: irrString.h:37
IRRLICHT_API IProfiler &IRRCALLCONV getProfiler()
Access the Irrlicht profiler object.
virtual void printAll(core::stringw &result, bool includeOverview=false, bool suppressUncalled=true) const =0
Write all profile-data into a string.
void resetDataById(s32 id)
Reset profile data for the given id.
Definition: IProfiler.h:423
IProfiler & Profiler
Definition: IProfiler.h:275
signed int s32
32 bit signed variable.
Definition: irrTypes.h:70
CProfileScope(s32 id)
Construct with an known id.
Definition: IProfiler.h:239
CProfileScope(const core::stringw &name, const core::stringw &groupName)
Object will create an id for the given name, groupName combination if they don't exist already.
Definition: IProfiler.h:261
unsigned int u32
32 bit unsigned variable.
Definition: irrTypes.h:62
void add(s32 id, const core::stringw &name, const core::stringw &groupName)
Add an id with given name and group which can be used for profiling with start/stop.
Definition: IProfiler.h:346
Interface for getting and manipulating the virtual time.
Definition: ITimer.h:14
u32 getGroupCount() const
Get the number of profile groups. Will be at least 1.
Definition: IProfiler.h:152
const SProfileData & getProfileDataByIndex(u32 index) const
Get the profile data.
Definition: IProfiler.h:140
#define IRRCALLCONV
bool operator==(const SProfileData &pd) const
Definition: IProfiler.h:35
virtual u32 getRealTime() const =0
Returns current real time in milliseconds of the system.
const SProfileData & getGroupData(u32 index) const
Get profile data for a group.
Definition: IProfiler.h:159
void start(s32 id)
Start profile-timing for the given id.
Definition: IProfiler.h:281
void resetDataByIndex(u32 index)
Reset profile data for the given index.
Definition: IProfiler.h:432
u32 getLongestTime() const
Longest time a profile call for this id took from start until it was stopped again.
Definition: IProfiler.h:57
Self reallocating template array (like stl vector) with additional features.
Definition: irrArray.h:22
const SProfileData * getProfileDataById(u32 id)
Get the profile data.
Definition: IProfiler.h:400
Code-profiler. Please check the example in the Irrlicht examples folder about how to use it.
Definition: IProfiler.h:101
void resetGroup(u32 index)
Reset profile data for a whole group.
Definition: IProfiler.h:444
void stop(s32 id)
Stop profile-timing for the given id.
Definition: IProfiler.h:292
bool findGroupIndex(u32 &result, const core::stringw &name) const
Find the group index by the group-name.
Definition: IProfiler.h:409
u32 getGroupIndex() const
Definition: IProfiler.h:40
void resetAll()
Reset all profile data.
Definition: IProfiler.h:455
core::array< SProfileData > ProfileDatas
Definition: IProfiler.h:215
virtual ~IProfiler()
Definition: IProfiler.h:108
bool findDataIndex(u32 &result, const core::stringw &name) const
Search for the index of the profile data by name.
Definition: IProfiler.h:386
CProfileScope(s32 id, const core::stringw &name, const core::stringw &groupName)
Object will create the given name, groupName combination for the id if it doesn't exist already.
Definition: IProfiler.h:250
u32 addGroup(const core::stringw &name)
Definition: IProfiler.h:377
string< wchar_t > stringw
Typedef for wide character strings.
Definition: irrString.h:1461