Profiler.h 8.9 KB

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