Overview#
Enable Stats from Command Line:
- StatCmds="startfile"
Reference From https://udn.unrealengine.com/questions/445587/long-initial-load-times.html
Description of stats: https://docs.unrealengine.com/udk/Three/StatsDescriptions.html
Stats System Diagram#
- FEventPayload
Common Functions & Stats#
Common options: [-ms=5.0][-root=None] [leaf=None][-depth=maxint] [-nodisplay]
stat groupname[+] - toggles displaying stats group, + enables hierarchical display
stat group list|listall|enable name|disable name|none|all|default - manages enabling/disabling recording of the stats groups. Doing stat [groupname] automatically enables that group
- Also supports [-group=groupname][-sortby=name] [-maxhistoryframes=60][-reset] [-maxdepth=4][-root=None] [-ms=0.2][-reset]
stat namedmarker #markername# - adds a custom marker to the stats stream
stat none - disables drawing all stats groups
stat display -font=small[tiny] - Changes stats rendering display options
stat slow [-ms=1.0][-depth=4] - toggles displaying the game and render thread stats
stat dumpframe - dumps a frame of stats
- stat dumpframe -ms=.001 -root=initviews
- stat dumpframe -ms=.001 -root=shadow
stat dumpave|dumpmax|dumpsum [-start | -stop | -num=30] - aggregate stats over multiple frames
stat dumphitches [-start | -stop | empty toggles] - toggles dumping hitches
stat dumpevents [-ms=0.2][-all] - dumps events history for slow events, -all adds other threads besides game and render
stat dumpcpu - dumps cpu stats
stat dumpnonframe [groupname] - dumps non-frame stats, usually memory stats
stat hier -group=groupname [-sortby=name][-maxhistoryframes=60] [-reset][-maxdepth=4] [-root=None][-ms=0.2] [-reset]
- groupname is a stat group like initviews or statsystem
- sortby can be name (by stat FName), callcount (by number of calls, only for scoped cycle counters), num(by total inclusive time)
- maxhistoryframes (default 60, number of frames used to generate the stats displayed on the hud)
- reset (reset the accumulated history)
- maxdepth (default 4, maximum depth for the hierarchy)
stat startfile - starts dumping a capture
stat stopfile - stops dumping a capture (regular, raw, memory)
stat startfileraw - starts dumping a raw capture
stat toggledebug - toggles tracking the most memory expensive stats
add -memoryprofiler in the command line to enable the memory profiling
stat stopfile - stops tracking all memory operations and writes the results to the file
stat testfile - loads the last saved capture and dumps first, middle and last frame
Details from Stats.h: Implementing custom stats or cycle counters#
/**
* Unreal Engine Stats system
*
* This is a preliminary version of the documentation, any comments are welcome :)
*
* This system allows you to collect various performance data and then the data can be used to optimize your game.
* There are a few methods how to achieve this. This quick tutorial will describe all of them.
* For stats commands check out method PrintStatsHelpToOutputDevice();
*
* Stats system in the UE4 supports following stats types:
* Cycle Counter - a generic cycle counter used to counting the number of cycles during the lifetime of the object
* Float/Dword Counter - a counter that is cleared every frame
* Float/Dword Accumulator - a counter that is not cleared every frame, persistent stat, but it can be reset
* Memory - a special type of counter that is optimized for memory tracking
*
* Each stat needs to be grouped, this usually corresponds with displaying the specified stat group i.e. 'stat statsystem' which displays stats' related data.
*
* To define a stat group you need to use one of the following methods:
* DECLARE_STATS_GROUP(GroupDesc,GroupId,GroupCat) - declares a stats group which is enabled by default
* DECLARE_STATS_GROUP_VERBOSE(GroupDesc,GroupId,GroupCat) - declares a stats group which is disabled by default
* DECLARE_STATS_GROUP_MAYBE_COMPILED_OUT(GroupDesc,GroupId,GroupCat) - declares a stats group which is disabled by default and may be stripped by the compiler
*
* where
* GroupDesc is a text description of the group
* GroupId is an UNIQUE id of the group
* GroupCat is reserved for future use
* CompileIn if set to true, the compiler may strip it out
*
* It can be done in the source or header file depending the usage scope.
*
* Examples:
* DECLARE_STATS_GROUP(TEXT("Threading"), STATGROUP_Threading, STATCAT_Advanced);
* DECLARE_STATS_GROUP_VERBOSE(TEXT("Linker Load"), STATGROUP_LinkerLoad, STATCAT_Advanced);
*
* Now, you can declare/define a stat.
* A stat can be used only in one cpp file, in the function scope, in the module scope or can be used in the whole project.
*
* For one file scope you need to use one of the following methods depending on the stat type.
* DECLARE_CYCLE_STAT(CounterName,StatId,GroupId) - declares a cycle counter stat
*
* DECLARE_SCOPE_CYCLE_COUNTER(CounterName,StatId,GroupId) - declares a cycle counter stat and uses it at the same time, it is limited to one function scope
* QUICK_SCOPE_CYCLE_COUNTER(StatId) - declares a cycle counter stat that will belong to stat group called 'Quick'
* RETURN_QUICK_DECLARE_CYCLE_STAT(StatId,GroupId) - returns a cycle counter, used by a few specialized classes, more information later
*
* DECLARE_FLOAT_COUNTER_STAT(CounterName,StatId,GroupId) - declares a float counter, technically speaking it's based on the double type, 8 bytes
* DECLARE_DWORD_COUNTER_STAT(CounterName,StatId,GroupId) - declared a dword counter, technically speaking it's based on the qword type, 8 bytes
* DECLARE_FLOAT_ACCUMULATOR_STAT(CounterName,StatId,GroupId) - declares a float accumulator
* DECLARE_DWORD_ACCUMULATOR_STAT(CounterName,StatId,GroupId) - declares a dword accumulator
* DECLARE_MEMORY_STAT(CounterName,StatId,GroupId) - declares a memory counter, same as the dword accumulator, but will be displayed with memory specific units
* DECLARE_MEMORY_STAT_POOL(CounterName,StatId,GroupId,Pool) - declares a memory counter with a pool
*
* If you want to have these stats accessible in the whole project/or wider range of files you need to use extern version.
* These methods are the same as the previously mentioned but with _EXTERN and the end of the name, here is the list:
* DECLARE_CYCLE_STAT_EXTERN(CounterName,StatId,GroupId, API)
* DECLARE_FLOAT_COUNTER_STAT_EXTERN(CounterName,StatId,GroupId, API)
* DECLARE_DWORD_COUNTER_STAT_EXTERN(CounterName,StatId,GroupId, API)
* DECLARE_FLOAT_ACCUMULATOR_STAT_EXTERN(CounterName,StatId,GroupId, API)
* DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(CounterName,StatId,GroupId, API)
* DECLARE_MEMORY_STAT_EXTERN(CounterName,StatId,GroupId, API)
* DECLARE_MEMORY_STAT_POOL_EXTERN(CounterName,StatId,GroupId,Pool, API)
*
* Then in the source file you need to define those stats.
* DEFINE_STAT(CounterName) - defines stats declared with _EXTERN
*
* where
* CounterName is a text description of the stat
* StatId is an UNIQUE id of the stat
* GroupId is an id of the group that the stat will belong to, the GroupId from DECLARE_STATS_GROUP*
* Pool is a platform specific memory pool, more details later
* API is the *_API of module, can be empty if the stat will be used only in that module
*
* Examples:
* Custom memory stats with pools
* First you need to add a new pool to enum EMemoryCounterRegion, it can be global or platform specific.
*
* enum EMemoryCounterRegion
* {
* MCR_Invalid, // not memory
* MCR_Physical, // main system memory
* MCR_GPU, // memory directly a GPU (graphics card, etc)
* MCR_GPUSystem, // system memory directly accessible by a GPU
* MCR_TexturePool,// presized texture pools
* MCR_MAX
* };
*
* This is an example that will allow using the pools every where, see CORE_API.
* THE NAME OF THE POOL MUST START WITH MCR_
* Header file.
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Physical Memory Pool [Physical]"), MCR_Physical, STATGROUP_Memory, FPlatformMemory::MCR_Physical, CORE_API);
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("GPU Memory Pool [GPU]"), MCR_GPU, STATGROUP_Memory, FPlatformMemory::MCR_GPU, CORE_API);
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Texture Memory Pool [Texture]"), MCR_TexturePool, STATGROUP_Memory, FPlatformMemory::MCR_TexturePool,CORE_API);
*
* Source file.
* DEFINE_STAT(MCR_Physical);
* DEFINE_STAT(MCR_GPU);
* DEFINE_STAT(MCR_TexturePool);
*
* This is a pool, so it needs to be initialized. Usually in the F*PlatformMemory::Init()
* SET_MEMORY_STAT(MCR_Physical, PhysicalPoolLimit);
* SET_MEMORY_STAT(MCR_GPU, GPUPoolLimit);
* SET_MEMORY_STAT(MCR_TexturePool, TexturePoolLimit);
*
* Now we have pools, so we can setup memory stats for those pools.
* Accessible everywhere.
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Index buffer memory"), STAT_IndexBufferMemory, STATGROUP_RHI, FPlatformMemory::MCR_GPU, RHI_API);
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Vertex buffer memory"), STAT_VertexBufferMemory, STATGROUP_RHI, FPlatformMemory::MCR_GPU, RHI_API);
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Structured buffer memory"), STAT_StructuredBufferMemory,STATGROUP_RHI, FPlatformMemory::MCR_GPU, RHI_API);
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Pixel buffer memory"), STAT_PixelBufferMemory, STATGROUP_RHI, FPlatformMemory::MCR_GPU, RHI_API);
*
* Accessible only in the module where defined.
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Pool Memory Size"), STAT_TexturePoolSize, STATGROUP_Streaming, FPlatformMemory::MCR_TexturePool, );
* DECLARE_MEMORY_STAT_POOL_EXTERN(TEXT("Pool Memory Used"), STAT_TexturePoolAllocatedSize, STATGROUP_Streaming, FPlatformMemory::MCR_TexturePool, );
*
* And the last thing, updating the memory stats.
* INC_MEMORY_STAT_BY(STAT_PixelBufferMemory,NumBytes) - increases a memory stat by the specified value
* DEC_MEMORY_STAT_BY(STAT_PixelBufferMemory,NumBytes) - decreases a memory stat by the specified value
* SET_MEMORY_STAT(STAT_PixelBufferMemory,NumBytes) - sets a memory stat to the specified value
*
* Regular memory stats, without pools
* DECLARE_MEMORY_STAT(TEXT("Total Physical"), STAT_TotalPhysical, STATGROUP_MemoryPlatform);
* DECLARE_MEMORY_STAT(TEXT("Total Virtual"), STAT_TotalVirtual, STATGROUP_MemoryPlatform);
* DECLARE_MEMORY_STAT(TEXT("Page Size"), STAT_PageSize, STATGROUP_MemoryPlatform);
* DECLARE_MEMORY_STAT(TEXT("Total Physical GB"), STAT_TotalPhysicalGB, STATGROUP_MemoryPlatform);
*
* Or DECLARE_MEMORY_STAT_EXTERN in the header file and then DEFINE_STAT in the source file.
* Updating the memory stats is done the same way as in the version with pools.
*
*
* Performance data using the cycle counters.
* First you need to add cycle counters.
* DECLARE_CYCLE_STAT(TEXT("Broadcast"), STAT_StatsBroadcast,STATGROUP_StatSystem);
* DECLARE_CYCLE_STAT(TEXT("Condense"), STAT_StatsCondense, STATGROUP_StatSystem);
*
* Or DECLARE_CYCLE_STAT_EXTERN in the header file and then DEFINE_STAT in the source file.
*
* Now you can grab the performance data.
*
* Stats::Broadcast()
* {
* SCOPE_CYCLE_COUNTER(STAT_StatsBroadcast);
* ...
* // a piece of code
* ...
* }
*
* and that's all.
* Sometimes you don't want to grab the stats every time the function is called, so you can use conditional cycle counter.
* It's not very common, but may be useful.
*
* Stats::Broadcast(bool bSomeCondition)
* {
* CONDITIONAL_SCOPE_CYCLE_COUNTER(STAT_StatsBroadcast,bSomeCondition);
* ...
* // a piece of code
* ...
* }
*
* If you want to grab the performance data from one function you can use following construction.
*
* Stats::Broadcast(bool bSomeCondition)
* {
* DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Broadcast"), STAT_StatsBroadcast, STATGROUP_StatSystem);
* ...
* // a piece of code
* ...
* }
*
* or
*
* Stats::Broadcast(bool bSomeCondition)
* {
* QUICK_SCOPE_CYCLE_COUNTER(TEXT("Stats::Broadcast"));
* ...
* // a piece of code
* ...
* }
*
* Mostly used for temporary stats.
*
* Those all cycle counters are used to generate the hierarchy. So you can get more detailed information about performance data.
* There is also an option to set flat cycle counter.
*
* Stats::Broadcast(bool bSomeCondition)
* {
* const uint32 BroadcastBeginTime = FPlatformTime::Cycles();
* ...
* // a piece of code
* ...
* const uint32 BroadcastEndTime = FPlatformTime::Cycles();
* SET_CYCLE_COUNTER(STAT_StatsBroadcast, BroadcastEndTime-BroadcastBeginTime);
* }
*
*
* A few tasks implemented in the UE4 use a different approach in terms of getting the performance data.
* They implement method GetStatId(). If there is no GetStatId(), the code will not compile.
* Here is an example.
*
* class FParallelAnimationCompletionTask
* {
* // ...
* // a piece of code
* FORCEINLINE TStatId GetStatId() const
* {
* RETURN_QUICK_DECLARE_CYCLE_STAT(FParallelAnimationCompletionTask, STATGROUP_TaskGraphTasks);
* }
* // a piece of code
* // ...
* };
*
*
* Generic data using the float or dword counters.
* First you need to add a few counters.
* DECLARE_FLOAT_COUNTER_STAT_EXTERN(STAT_FloatCounter,StatId,STATGROUP_TestGroup, CORE_API)
* DECLARE_DWORD_COUNTER_STAT_EXTERN(STAT_DwordCounter,StatId,STATGROUP_TestGroup, CORE_API)
* DECLARE_FLOAT_ACCUMULATOR_STAT_EXTERN(STAT_FloatAccumulator,StatId,STATGROUP_TestGroup, CORE_API)
* DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(STAT_DwordAccumulator,StatId,STATGROUP_TestGroup, CORE_API)
*
* Updating counters.
* INC_DWORD_STAT(StatId) - increases a dword stat by 1
* DEC_DWORD_STAT(StatId) - decreases a dword stat by 1
* INC_DWORD_STAT_BY(StatId,Amount) - increases a dword stat by the specified value
* DEC_DWORD_STAT_BY(StatId,Amount) - decreases a dword stat by the specified value
* SET_DWORD_STAT(StatId,Value) - sets a dword stat to the specified value
* INC_FLOAT_STAT_BY(StatId,Amount) - increases a float stat by the specified value
* DEC_FLOAT_STAT_BY(StatId,Amount) - decreases a float stat by the specified value
* SET_FLOAT_STAT(StatId,Value) - sets a float stat to the specified value
*
*
* A few helper methods
* GET_STATID(StatId) - returns an instance of the TStatId of the stat, ADVANCED
* GET_STATDESCRIPTION(StatId) - returns a description of the stat
*
*
* If you don't want to use the stats system and just log some performance data, there is functionality for this.
*
* SCOPE_SECONDS_COUNTER(double&Seconds) - captures time passed in seconds, adding delta time to passed in variable
*
* Stats::Broadcast()
* {
* double ThisTime = 0;
* {
* SCOPE_SECONDS_COUNTER(ThisTime);
* ...
* // a piece of code
* ...
* }
* UE_LOG(LogTemp, Log, TEXT("Stats::Broadcast %.2f"), ThisTime );
* }
*
* FScopeLogTime - utility class to log time passed in seconds, adding cumulative stats to passed in variable, print the performance data to the log in the destructor
*
* SCOPE_LOG_TIME(Name,CumulativePtr) - using the given name prints the performance data and gathers cumulative stats
* SCOPE_LOG_TIME_IN_SECONDS(Name,CumulativePtr) - the same as above, but prints in seconds
*
* SCOPE_LOG_TIME_FUNC() - using the funcion name prints the performance data, cannot be nested
* SCOPE_LOG_TIME_FUNC_WITH_GLOBAL(CumulativePtr), same as above, but gather cumulative stats
*
* A few examples.
*
* double GMyBroadcastTime = 0.0;
* Stats::Broadcast()
* {
* SCOPE_LOG_TIME("Stats::Broadcast", &GMyBroadcastTime );
* SCOPE_LOG_TIME_IN_SECONDS("Stats::Broadcast (sec)", &GMyBroadcastTime );
* ...
* // a piece of code
* ...
* }
*
* Stats::Condense()
* {
* SCOPE_LOG_TIME_FUNC(); // The name should be "Stats::Condense()", may differ across compilers
* SCOPE_LOG_TIME_FUNC_WITH_GLOBAL(&GMyBroadcastTime);
* ...
* // a piece of code
* ...
* }