Profiler.h 8.8 KB

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