Path: blob/master/thirdparty/jolt_physics/Jolt/Core/Profiler.h
9906 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56JPH_SUPPRESS_WARNINGS_STD_BEGIN7#include <mutex>8#include <chrono>9JPH_SUPPRESS_WARNINGS_STD_END1011#include <Jolt/Core/NonCopyable.h>12#include <Jolt/Core/TickCounter.h>13#include <Jolt/Core/UnorderedMap.h>1415#if defined(JPH_EXTERNAL_PROFILE)1617JPH_NAMESPACE_BEGIN1819#ifdef JPH_SHARED_LIBRARY20/// Functions called when a profiler measurement starts or stops, need to be overridden by the user.21using ProfileStartMeasurementFunction = void (*)(const char *inName, uint32 inColor, uint8 *ioUserData);22using ProfileEndMeasurementFunction = void (*)(uint8 *ioUserData);2324JPH_EXPORT extern ProfileStartMeasurementFunction ProfileStartMeasurement;25JPH_EXPORT extern ProfileEndMeasurementFunction ProfileEndMeasurement;26#endif // JPH_SHARED_LIBRARY2728/// Create this class on the stack to start sampling timing information of a particular scope.29///30/// For statically linked builds, this is left unimplemented intentionally. Needs to be implemented by the user of the library.31/// On construction a measurement should start, on destruction it should be stopped.32/// For dynamically linked builds, the user should override the ProfileStartMeasurement and ProfileEndMeasurement functions.33class alignas(16) ExternalProfileMeasurement : public NonCopyable34{35public:36/// Constructor37#ifdef JPH_SHARED_LIBRARY38JPH_INLINE ExternalProfileMeasurement(const char *inName, uint32 inColor = 0) { ProfileStartMeasurement(inName, inColor, mUserData); }39JPH_INLINE ~ExternalProfileMeasurement() { ProfileEndMeasurement(mUserData); }40#else41ExternalProfileMeasurement(const char *inName, uint32 inColor = 0);42~ExternalProfileMeasurement();43#endif4445private:46uint8 mUserData[64];47};4849JPH_NAMESPACE_END5051//////////////////////////////////////////////////////////////////////////////////////////52// Macros to do the actual profiling53//////////////////////////////////////////////////////////////////////////////////////////5455JPH_SUPPRESS_WARNING_PUSH56JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")5758// Dummy implementations59#define JPH_PROFILE_START(name)60#define JPH_PROFILE_END()61#define JPH_PROFILE_THREAD_START(name)62#define JPH_PROFILE_THREAD_END()63#define JPH_PROFILE_NEXTFRAME()64#define JPH_PROFILE_DUMP(...)6566// Scope profiling measurement67#define JPH_PROFILE_TAG2(line) profile##line68#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)6970/// Macro to collect profiling information.71///72/// Usage:73///74/// {75/// JPH_PROFILE("Operation");76/// do operation;77/// }78///79#define JPH_PROFILE(...) ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)8081// Scope profiling for function82#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)8384JPH_SUPPRESS_WARNING_POP8586#elif defined(JPH_PROFILE_ENABLED)8788JPH_NAMESPACE_BEGIN8990class ProfileSample;91class ProfileThread;9293/// Singleton class for managing profiling information94class JPH_EXPORT Profiler : public NonCopyable95{96public:97JPH_OVERRIDE_NEW_DELETE9899/// Constructor100Profiler() { UpdateReferenceTime(); }101102/// Increments the frame counter to provide statistics per frame103void NextFrame();104105/// Dump profiling statistics at the start of the next frame106/// @param inTag If not empty, this overrides the auto incrementing number in the filename of the dump file107void Dump(const string_view &inTag = string_view());108109/// Add a thread to be instrumented110void AddThread(ProfileThread *inThread);111112/// Remove a thread from being instrumented113void RemoveThread(ProfileThread *inThread);114115/// Singleton instance116static Profiler * sInstance;117118private:119/// Helper class to freeze ProfileSamples per thread while processing them120struct ThreadSamples121{122String mThreadName;123ProfileSample * mSamplesBegin;124ProfileSample * mSamplesEnd;125};126127/// Helper class to aggregate ProfileSamples128class Aggregator129{130public:131/// Constructor132Aggregator(const char *inName) : mName(inName) { }133134/// Accumulate results for a measurement135void AccumulateMeasurement(uint64 inCyclesInCallWithChildren)136{137mCallCounter++;138mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren;139mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren);140mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren);141}142143/// Sort descending by total cycles144bool operator < (const Aggregator &inRHS) const145{146return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren;147}148149/// Identification150const char * mName; ///< User defined name of this item151152/// Statistics153uint32 mCallCounter = 0; ///< Number of times AccumulateMeasurement was called154uint64 mTotalCyclesInCallWithChildren = 0; ///< Total amount of cycles spent in this scope155uint64 mMinCyclesInCallWithChildren = 0xffffffffffffffffUL; ///< Minimum amount of cycles spent per call156uint64 mMaxCyclesInCallWithChildren = 0; ///< Maximum amount of cycles spent per call157};158159using Threads = Array<ThreadSamples>;160using Aggregators = Array<Aggregator>;161using KeyToAggregator = UnorderedMap<const char *, size_t>;162163/// Helper function to aggregate profile sample data164static void sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator);165166/// We measure the amount of ticks per second, this function resets the reference time point167void UpdateReferenceTime();168169/// Get the amount of ticks per second, note that this number will never be fully accurate as the amount of ticks per second may vary with CPU load, so this number is only to be used to give an indication of time for profiling purposes170uint64 GetProcessorTicksPerSecond() const;171172/// Dump profiling statistics173void DumpInternal();174void DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators);175176std::mutex mLock; ///< Lock that protects mThreads177uint64 mReferenceTick; ///< Tick count at the start of the frame178std::chrono::high_resolution_clock::time_point mReferenceTime; ///< Time at the start of the frame179Array<ProfileThread *> mThreads; ///< List of all active threads180bool mDump = false; ///< When true, the samples are dumped next frame181String mDumpTag; ///< When not empty, this overrides the auto incrementing number of the dump filename182};183184// Class that contains the information of a single scoped measurement185class alignas(16) JPH_EXPORT_GCC_BUG_WORKAROUND ProfileSample : public NonCopyable186{187public:188JPH_OVERRIDE_NEW_DELETE189190const char * mName; ///< User defined name of this item191uint32 mColor; ///< Color to use for this sample192uint8 mDepth; ///< Calculated depth193uint8 mUnused[3];194uint64 mStartCycle; ///< Cycle counter at start of measurement195uint64 mEndCycle; ///< Cycle counter at end of measurement196};197198/// Collects all samples of a single thread199class ProfileThread : public NonCopyable200{201public:202JPH_OVERRIDE_NEW_DELETE203204/// Constructor205inline ProfileThread(const string_view &inThreadName);206inline ~ProfileThread();207208static const uint cMaxSamples = 65536;209210String mThreadName; ///< Name of the thread that we're collecting information for211ProfileSample mSamples[cMaxSamples]; ///< Buffer of samples212uint mCurrentSample = 0; ///< Next position to write a sample to213214#ifdef JPH_SHARED_LIBRARY215JPH_EXPORT static void sSetInstance(ProfileThread *inInstance);216JPH_EXPORT static ProfileThread *sGetInstance();217#else218static inline void sSetInstance(ProfileThread *inInstance) { sInstance = inInstance; }219static inline ProfileThread *sGetInstance() { return sInstance; }220221private:222static thread_local ProfileThread *sInstance;223#endif224};225226/// Create this class on the stack to start sampling timing information of a particular scope227class JPH_EXPORT ProfileMeasurement : public NonCopyable228{229public:230/// Constructor231inline ProfileMeasurement(const char *inName, uint32 inColor = 0);232inline ~ProfileMeasurement();233234private:235ProfileSample * mSample;236ProfileSample mTemp;237238static bool sOutOfSamplesReported;239};240241JPH_NAMESPACE_END242243#include "Profiler.inl"244245//////////////////////////////////////////////////////////////////////////////////////////246// Macros to do the actual profiling247//////////////////////////////////////////////////////////////////////////////////////////248249JPH_SUPPRESS_WARNING_PUSH250JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")251252/// Start instrumenting program253#define JPH_PROFILE_START(name) do { Profiler::sInstance = new Profiler; JPH_PROFILE_THREAD_START(name); } while (false)254255/// End instrumenting program256#define JPH_PROFILE_END() do { JPH_PROFILE_THREAD_END(); delete Profiler::sInstance; Profiler::sInstance = nullptr; } while (false)257258/// Start instrumenting a thread259#define JPH_PROFILE_THREAD_START(name) do { if (Profiler::sInstance) ProfileThread::sSetInstance(new ProfileThread(name)); } while (false)260261/// End instrumenting a thread262#define JPH_PROFILE_THREAD_END() do { delete ProfileThread::sGetInstance(); ProfileThread::sSetInstance(nullptr); } while (false)263264/// Scope profiling measurement265#define JPH_PROFILE_TAG2(line) profile##line266#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)267#define JPH_PROFILE(...) ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)268269/// Scope profiling for function270#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)271272/// Update frame counter273#define JPH_PROFILE_NEXTFRAME() Profiler::sInstance->NextFrame()274275/// Dump profiling info276#define JPH_PROFILE_DUMP(...) Profiler::sInstance->Dump(__VA_ARGS__)277278JPH_SUPPRESS_WARNING_POP279280#else281282//////////////////////////////////////////////////////////////////////////////////////////283// Dummy profiling instructions284//////////////////////////////////////////////////////////////////////////////////////////285286JPH_SUPPRESS_WARNING_PUSH287JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")288289#define JPH_PROFILE_START(name)290#define JPH_PROFILE_END()291#define JPH_PROFILE_THREAD_START(name)292#define JPH_PROFILE_THREAD_END()293#define JPH_PROFILE(...)294#define JPH_PROFILE_FUNCTION()295#define JPH_PROFILE_NEXTFRAME()296#define JPH_PROFILE_DUMP(...)297298JPH_SUPPRESS_WARNING_POP299300#endif301302303