Profiler.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <mutex>
  5. #include <unordered_map>
  6. #include <Core/NonCopyable.h>
  7. #include <Core/TickCounter.h>
  8. #if defined(JPH_EXTERNAL_PROFILE)
  9. namespace JPH {
  10. /// Create this class on the stack to start sampling timing information of a particular scope.
  11. ///
  12. /// Left unimplemented intentionally. Needs to be implemented by the user of the library.
  13. /// On construction a measurement should start, on destruction it should be stopped.
  14. class alignas(16) ExternalProfileMeasurement : public NonCopyable
  15. {
  16. public:
  17. /// Constructor
  18. ExternalProfileMeasurement(const char *inName, uint32 inColor = 0);
  19. ~ExternalProfileMeasurement();
  20. private:
  21. uint8 mUserData[64];
  22. };
  23. } // JPH
  24. //////////////////////////////////////////////////////////////////////////////////////////
  25. // Macros to do the actual profiling
  26. //////////////////////////////////////////////////////////////////////////////////////////
  27. // Dummy implementations
  28. #define JPH_PROFILE_THREAD_START(name)
  29. #define JPH_PROFILE_THREAD_END()
  30. #define JPH_PROFILE_NEXTFRAME()
  31. #define JPH_PROFILE_DUMP()
  32. // Scope profiling measurement
  33. #define JPH_PROFILE_TAG2(line) profile##line
  34. #define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
  35. /// Macro to collect profiling information.
  36. ///
  37. /// Usage:
  38. ///
  39. /// {
  40. /// JPH_PROFILE("Operation");
  41. /// do operation;
  42. /// }
  43. ///
  44. #define JPH_PROFILE(...) ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
  45. // Scope profiling for function
  46. #define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
  47. #elif defined(JPH_PROFILE_ENABLED)
  48. namespace JPH {
  49. class ProfileSample;
  50. class ProfileThread;
  51. /// Singleton class for managing profiling information
  52. class Profiler : public NonCopyable
  53. {
  54. public:
  55. /// Increments the frame counter to provide statistics per frame
  56. void NextFrame();
  57. /// Dump profiling statistics at the start of the next frame
  58. void Dump();
  59. /// Add a thread to be instrumented
  60. void AddThread(ProfileThread *inThread);
  61. /// Remove a thread from being instrumented
  62. void RemoveThread(ProfileThread *inThread);
  63. /// Singleton instance
  64. static Profiler sInstance;
  65. private:
  66. /// Helper class to freeze ProfileSamples per thread while processing them
  67. struct ThreadSamples
  68. {
  69. string mThreadName;
  70. ProfileSample * mSamplesBegin;
  71. ProfileSample * mSamplesEnd;
  72. };
  73. /// Helper class to aggregate ProfileSamples
  74. class Aggregator
  75. {
  76. public:
  77. /// Constructor
  78. Aggregator(const char *inName) : mName(inName) { }
  79. /// Accumulate results for a measurement
  80. void AccumulateMeasurement(uint64 inCyclesInCallWithChildren, uint64 inCyclesInChildren)
  81. {
  82. mCallCounter++;
  83. mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren;
  84. mTotalCyclesInChildren += inCyclesInChildren;
  85. mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren);
  86. mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren);
  87. }
  88. /// Sort descending by total cycles
  89. bool operator < (const Aggregator &inRHS) const
  90. {
  91. return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren;
  92. }
  93. /// Identification
  94. const char * mName; ///< User defined name of this item
  95. /// Statistics
  96. uint32 mCallCounter = 0; ///< Number of times AccumulateMeasurement was called
  97. uint64 mTotalCyclesInCallWithChildren = 0; ///< Total amount of cycles spent in this scope
  98. uint64 mTotalCyclesInChildren = 0; ///< Total amount of cycles spent in children of this scope
  99. uint64 mMinCyclesInCallWithChildren = 0xffffffffffffffffUL; ///< Minimum amount of cycles spent per call
  100. uint64 mMaxCyclesInCallWithChildren = 0; ///< Maximum amount of cycles spent per call
  101. };
  102. using Threads = vector<ThreadSamples>;
  103. using Aggregators = vector<Aggregator>;
  104. using KeyToAggregator = unordered_map<const char *, size_t>;
  105. /// Helper function to aggregate profile sample data
  106. static void sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator);
  107. /// Dump profiling statistics
  108. void DumpInternal();
  109. void DumpList(int inNumber, const Aggregators &inAggregators);
  110. void DumpChart(int inNumber, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators);
  111. mutex mLock; ///< Lock that protects mThreads
  112. vector<ProfileThread *> mThreads; ///< List of all active threads
  113. bool mDump = false; ///< When true, the samples are dumped next frame
  114. };
  115. // Class that contains the information of a single scoped measurement
  116. class alignas(16) ProfileSample : public NonCopyable
  117. {
  118. public:
  119. const char * mName; ///< User defined name of this item
  120. uint32 mColor; ///< Color to use for this sample
  121. uint8 mDepth; ///< Calculated depth
  122. uint8 mUnused[3];
  123. uint64 mStartCycle; ///< Cycle counter at start of measurement
  124. uint64 mEndCycle; ///< Cycle counter at end of measurement
  125. };
  126. /// Collects all samples of a single thread
  127. class ProfileThread : public NonCopyable
  128. {
  129. public:
  130. /// Constructor
  131. inline ProfileThread(const string &inThreadName);
  132. inline ~ProfileThread();
  133. static const uint cMaxSamples = 65536;
  134. string mThreadName; ///< Name of the thread that we're collecting information for
  135. ProfileSample mSamples[cMaxSamples]; ///< Buffer of samples
  136. uint mCurrentSample = 0; ///< Next position to write a sample to
  137. static thread_local ProfileThread *sInstance;
  138. };
  139. /// Create this class on the stack to start sampling timing information of a particular scope
  140. class ProfileMeasurement : public NonCopyable
  141. {
  142. public:
  143. /// Constructor
  144. inline ProfileMeasurement(const char *inName, uint32 inColor = 0);
  145. inline ~ProfileMeasurement();
  146. private:
  147. ProfileSample * mSample;
  148. ProfileSample mTemp;
  149. static bool sOutOfSamplesReported;
  150. };
  151. } // JPH
  152. #include "Profiler.inl"
  153. //////////////////////////////////////////////////////////////////////////////////////////
  154. // Macros to do the actual profiling
  155. //////////////////////////////////////////////////////////////////////////////////////////
  156. /// Start instrumenting a thread
  157. #define JPH_PROFILE_THREAD_START(name) ProfileThread::sInstance = new ProfileThread(name)
  158. /// End instrumenting a thread
  159. #define JPH_PROFILE_THREAD_END() do { delete ProfileThread::sInstance; ProfileThread::sInstance = nullptr; } while (false)
  160. /// Scope profiling measurement
  161. #define JPH_PROFILE_TAG2(line) profile##line
  162. #define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
  163. #define JPH_PROFILE(...) ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
  164. /// Scope profiling for function
  165. #define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
  166. /// Update frame counter
  167. #define JPH_PROFILE_NEXTFRAME() Profiler::sInstance.NextFrame()
  168. /// Dump profiling info
  169. #define JPH_PROFILE_DUMP() Profiler::sInstance.Dump()
  170. #else
  171. //////////////////////////////////////////////////////////////////////////////////////////
  172. // Dummy profiling instructions
  173. //////////////////////////////////////////////////////////////////////////////////////////
  174. #define JPH_PROFILE_THREAD_START(name)
  175. #define JPH_PROFILE_THREAD_END()
  176. #define JPH_PROFILE(...)
  177. #define JPH_PROFILE_FUNCTION()
  178. #define JPH_PROFILE_NEXTFRAME()
  179. #define JPH_PROFILE_DUMP()
  180. #endif