123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
- // SPDX-License-Identifier: MIT
- #pragma once
- JPH_SUPPRESS_WARNINGS_STD_BEGIN
- #include <mutex>
- JPH_SUPPRESS_WARNINGS_STD_END
- #include <Jolt/Core/NonCopyable.h>
- #include <Jolt/Core/TickCounter.h>
- #include <Jolt/Core/UnorderedMap.h>
- #if defined(JPH_EXTERNAL_PROFILE)
- JPH_NAMESPACE_BEGIN
- /// Create this class on the stack to start sampling timing information of a particular scope.
- ///
- /// Left unimplemented intentionally. Needs to be implemented by the user of the library.
- /// On construction a measurement should start, on destruction it should be stopped.
- class alignas(16) ExternalProfileMeasurement : public NonCopyable
- {
- public:
- /// Constructor
- ExternalProfileMeasurement(const char *inName, uint32 inColor = 0);
- ~ExternalProfileMeasurement();
- private:
- uint8 mUserData[64];
- };
- JPH_NAMESPACE_END
- //////////////////////////////////////////////////////////////////////////////////////////
- // Macros to do the actual profiling
- //////////////////////////////////////////////////////////////////////////////////////////
- JPH_SUPPRESS_WARNING_PUSH
- JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
- // Dummy implementations
- #define JPH_PROFILE_THREAD_START(name)
- #define JPH_PROFILE_THREAD_END()
- #define JPH_PROFILE_NEXTFRAME()
- #define JPH_PROFILE_DUMP(...)
-
- // Scope profiling measurement
- #define JPH_PROFILE_TAG2(line) profile##line
- #define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
- /// Macro to collect profiling information.
- ///
- /// Usage:
- ///
- /// {
- /// JPH_PROFILE("Operation");
- /// do operation;
- /// }
- ///
- #define JPH_PROFILE(...) ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
- // Scope profiling for function
- #define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
- JPH_SUPPRESS_WARNING_POP
- #elif defined(JPH_PROFILE_ENABLED)
- JPH_NAMESPACE_BEGIN
- class ProfileSample;
- class ProfileThread;
- /// Singleton class for managing profiling information
- class Profiler : public NonCopyable
- {
- public:
- JPH_OVERRIDE_NEW_DELETE
- /// Increments the frame counter to provide statistics per frame
- void NextFrame();
- /// Dump profiling statistics at the start of the next frame
- /// @param inTag If not empty, this overrides the auto incrementing number in the filename of the dump file
- void Dump(const string_view &inTag = string_view());
- /// Add a thread to be instrumented
- void AddThread(ProfileThread *inThread);
- /// Remove a thread from being instrumented
- void RemoveThread(ProfileThread *inThread);
- /// Singleton instance
- static Profiler * sInstance;
-
- private:
- /// Helper class to freeze ProfileSamples per thread while processing them
- struct ThreadSamples
- {
- String mThreadName;
- ProfileSample * mSamplesBegin;
- ProfileSample * mSamplesEnd;
- };
- /// Helper class to aggregate ProfileSamples
- class Aggregator
- {
- public:
- /// Constructor
- Aggregator(const char *inName) : mName(inName) { }
-
- /// Accumulate results for a measurement
- void AccumulateMeasurement(uint64 inCyclesInCallWithChildren, uint64 inCyclesInChildren)
- {
- mCallCounter++;
- mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren;
- mTotalCyclesInChildren += inCyclesInChildren;
- mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren);
- mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren);
- }
- /// Sort descending by total cycles
- bool operator < (const Aggregator &inRHS) const
- {
- return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren;
- }
- /// Identification
- const char * mName; ///< User defined name of this item
-
- /// Statistics
- uint32 mCallCounter = 0; ///< Number of times AccumulateMeasurement was called
- uint64 mTotalCyclesInCallWithChildren = 0; ///< Total amount of cycles spent in this scope
- uint64 mTotalCyclesInChildren = 0; ///< Total amount of cycles spent in children of this scope
- uint64 mMinCyclesInCallWithChildren = 0xffffffffffffffffUL; ///< Minimum amount of cycles spent per call
- uint64 mMaxCyclesInCallWithChildren = 0; ///< Maximum amount of cycles spent per call
- };
- using Threads = Array<ThreadSamples>;
- using Aggregators = Array<Aggregator>;
- using KeyToAggregator = UnorderedMap<const char *, size_t>;
- /// Helper function to aggregate profile sample data
- static void sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator);
- /// Dump profiling statistics
- void DumpInternal();
- void DumpList(const char *inTag, const Aggregators &inAggregators);
- void DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators);
- std::mutex mLock; ///< Lock that protects mThreads
- Array<ProfileThread *> mThreads; ///< List of all active threads
- bool mDump = false; ///< When true, the samples are dumped next frame
- String mDumpTag; ///< When not empty, this overrides the auto incrementing number of the dump filename
- };
- // Class that contains the information of a single scoped measurement
- class alignas(16) ProfileSample : public NonCopyable
- {
- public:
- JPH_OVERRIDE_NEW_DELETE
- const char * mName; ///< User defined name of this item
- uint32 mColor; ///< Color to use for this sample
- uint8 mDepth; ///< Calculated depth
- uint8 mUnused[3];
- uint64 mStartCycle; ///< Cycle counter at start of measurement
- uint64 mEndCycle; ///< Cycle counter at end of measurement
- };
- /// Collects all samples of a single thread
- class ProfileThread : public NonCopyable
- {
- public:
- JPH_OVERRIDE_NEW_DELETE
- /// Constructor
- inline ProfileThread(const string_view &inThreadName);
- inline ~ProfileThread();
- static const uint cMaxSamples = 65536;
- String mThreadName; ///< Name of the thread that we're collecting information for
- ProfileSample mSamples[cMaxSamples]; ///< Buffer of samples
- uint mCurrentSample = 0; ///< Next position to write a sample to
- static thread_local ProfileThread *sInstance;
- };
- /// Create this class on the stack to start sampling timing information of a particular scope
- class ProfileMeasurement : public NonCopyable
- {
- public:
- /// Constructor
- inline ProfileMeasurement(const char *inName, uint32 inColor = 0);
- inline ~ProfileMeasurement();
-
- private:
- ProfileSample * mSample;
- ProfileSample mTemp;
- static bool sOutOfSamplesReported;
- };
- JPH_NAMESPACE_END
- #include "Profiler.inl"
- //////////////////////////////////////////////////////////////////////////////////////////
- // Macros to do the actual profiling
- //////////////////////////////////////////////////////////////////////////////////////////
- JPH_SUPPRESS_WARNING_PUSH
- JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
- /// Start instrumenting program
- #define JPH_PROFILE_START(name) do { Profiler::sInstance = new Profiler; JPH_PROFILE_THREAD_START(name); } while (false)
- /// End instrumenting program
- #define JPH_PROFILE_END() do { JPH_PROFILE_THREAD_END(); delete Profiler::sInstance; Profiler::sInstance = nullptr; } while (false)
- /// Start instrumenting a thread
- #define JPH_PROFILE_THREAD_START(name) do { if (Profiler::sInstance) ProfileThread::sInstance = new ProfileThread(name); } while (false)
- /// End instrumenting a thread
- #define JPH_PROFILE_THREAD_END() do { delete ProfileThread::sInstance; ProfileThread::sInstance = nullptr; } while (false)
-
- /// Scope profiling measurement
- #define JPH_PROFILE_TAG2(line) profile##line
- #define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
- #define JPH_PROFILE(...) ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
- /// Scope profiling for function
- #define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
-
- /// Update frame counter
- #define JPH_PROFILE_NEXTFRAME() Profiler::sInstance->NextFrame()
- /// Dump profiling info
- #define JPH_PROFILE_DUMP(...) Profiler::sInstance->Dump(__VA_ARGS__)
- JPH_SUPPRESS_WARNING_POP
- #else
- //////////////////////////////////////////////////////////////////////////////////////////
- // Dummy profiling instructions
- //////////////////////////////////////////////////////////////////////////////////////////
- JPH_SUPPRESS_WARNING_PUSH
- JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
- #define JPH_PROFILE_START(name)
- #define JPH_PROFILE_END()
- #define JPH_PROFILE_THREAD_START(name)
- #define JPH_PROFILE_THREAD_END()
- #define JPH_PROFILE(...)
- #define JPH_PROFILE_FUNCTION()
- #define JPH_PROFILE_NEXTFRAME()
- #define JPH_PROFILE_DUMP(...)
- JPH_SUPPRESS_WARNING_POP
- #endif
|