UE Performance Analysis and Debugging

UE Performance Analysis Tools and Approaches

1. Code Instrumentation Macros

By inserting stat macros into C++ code, you can mark specific code blocks for timing measurement. The data collected here feeds into both Unreal Insights and the stat console commands.

1.1 Cycle Counter Macros

Macro Purpose Notes
DECLARE_STATS_GROUP(GroupDesc, GroupName, StatType) Declare a stat group Declared at the top of a .cpp file to categorize related stats
DECLARE_CYCLE_STAT(StatName, StatId, GroupName) Declare a cycle counter stat Typically declared at file scope
SCOPE_CYCLE_COUNTER(StatId) Measure the time spent in the current scope Automatically recorded when the scope exits
QUICK_SCOPE_CYCLE_COUNTER(StatId) Declare and measure in one step No separate DECLARE needed — one-liner
SCOPED_NAMED_TIMER(Text, Flags) Quick named timer No pre-declaration required; identifies by string

Typical usage:

1
2
3
4
5
6
7
8
9
10
11
12
// Declarations at the top of the file
DECLARE_STATS_GROUP(TEXT("MyGame"), STATGROUP_MyGame, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("ProcessAI"), STAT_ProcessAI, STATGROUP_MyGame);

void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Measure the entire function body
SCOPE_CYCLE_COUNTER(STAT_ProcessAI);

// ... AI logic ...
}

1.2 Quick Timing (No Pre-Declaration Needed)

1
2
3
4
5
6
7
8
9
10
11
12
13
// Option 1: QUICK_SCOPE_CYCLE_COUNTER (all-in-one)
void MyFunction()
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_MyFunction_LoadData);
// ... data loading logic ...
}

// Option 2: SCOPED_NAMED_TIMER (string identifier)
void MyFunction()
{
SCOPED_NAMED_TIMER("MyFunction_LoadData", FStatGroup::Get_STATGROUP_Threads());
// ... data loading logic ...
}

1.3 Counter Stats Macros

Macro Purpose
DECLARE_DWORD_ACCUMULATOR_STAT(StatName, StatId, GroupName) Declare an accumulator counter
INC_DWORD_STAT(StatId) Increment counter by 1
DEC_DWORD_STAT(StatId) Decrement counter by 1
SET_DWORD_STAT(StatId, Value) Set counter to a specific value
1
2
3
4
5
6
DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("ActiveEnemies"), STAT_ActiveEnemies, STATGROUP_MyGame);

// When spawning an enemy
INC_DWORD_STAT(STAT_ActiveEnemies);
// When an enemy dies
DEC_DWORD_STAT(STAT_ActiveEnemies);

1.4 Memory Stats Macros

Macro Purpose
DECLARE_MEMORY_STAT(StatName, StatId, GroupName) Declare a memory stat
TRACK_OBJECT_STAT(ObjectClass, StatId) Track UObject memory usage

2. Unreal Insights

The official high-performance profiling tool introduced in UE5, replacing the older Session Frontend / Profiler.

Launching

  • Editor menu: Tools → Unreal Insights
  • Command line: UnrealInsights.exe
  • Launch argument when running the game: -trace=cpu,gpu,memory,loadtime

Core Modules

Module Function What to Look For
Timing Insights CPU/GPU timeline Frame time, function call durations, thread activity, TaskGraph
Memory Insights Memory allocation tracking Memory leaks, allocation hotspots, object lifetimes
Networking Insights Network analysis Packet sizes, RPC calls, replication bandwidth
Loading Insights Load time analysis Asset load durations, async loading bottlenecks

Trace Channels

You can specify which channels to capture at launch:

1
-trace=cpu,gpu,memory,loadtime,net,frametime
Channel Content
cpu CPU timing, thread scheduling
gpu GPU render timings, Draw Calls
memory Memory allocation/deallocation events
loadtime Asset load times
net Network replication data
frametime Frame time statistics

Common Analysis Scenarios

  1. Frame Time Analysis: Timing Insights → expand Game Thread → find the most expensive SCOPE_CYCLE_COUNTER markers
  2. GPU Bottleneck Identification: Timing Insights → GPU view → inspect per-pass timings
  3. Memory Leak Investigation: Memory Insights → compare memory snapshots at two different points in time
  4. Load Stutter: Loading Insights → sort by duration to find the slowest-loading assets

3. Stat Commands

Enter stat commands into the console (press ~) in the editor or at runtime to see live performance data.

Common Commands

Command Description
stat fps Display frame rate and frame time
stat unit Display per-thread timings (Game / Draw / RHI / GPU)
stat unitgraph Display thread timings as a graph
stat scenerendering Scene rendering stats (Draw Calls, triangle count, visible object count)
stat engine Core engine stats
stat streaming Asset streaming stats
stat memory Memory usage overview
stat particles Particle system stats
stat physics Physics simulation stats
stat net Network stats
stat dumphitches Log hitch information to the output log

Reading the Key Metrics

1
2
3
4
5
6
7
8
9
10
11
Example stat unit output:
Frame: 33.2 ms ← Total frame time (target is 16.67ms = 60fps)
Game: 12.1 ms ← Game Thread (logic / AI / animation)
Draw: 5.3 ms ← Render Thread (render command preparation)
RHI: 4.1 ms ← RHI Thread (GPU command submission)
GPU: 18.7 ms ← Actual GPU render time

Identifying the bottleneck:
- Game high → optimize logic code (reduce Ticks, optimize AI)
- Draw high → reduce visible objects, improve LOD, culling
- GPU high → optimize materials / lighting / post-processing

Custom Stat Groups

1
2
// Once a custom group is declared, view it in the console with: stat MyGroup
DECLARE_STATS_GROUP(TEXT("My Game Stats"), STATGROUP_MyGame, STATCAT_Advanced);

Advanced Stat Commands

Command Description
stat startfile Start recording stats to a file
stat stopfile Stop recording; produces a .ue4stats file that can be opened in Unreal Insights
stat dumphitches Enable hitch logging; prints the call stack to the log when a threshold is exceeded
t.MaxFPS 60 Cap the maximum frame rate
r.VSync 0 Disable V-Sync for raw performance testing

4. GPU Analysis Tools

4.1 ProfileGPU (Console Command)

Entering ProfileGPU outputs a breakdown of GPU passes for the current frame in the Output Log:

1
2
3
4
5
6
Scene (18.2ms)
├── Base Pass (6.1ms)
├── Shadow Pass (3.2ms)
├── Lighting (4.8ms)
├── Translucency (2.1ms)
└── Post Process (2.0ms)

4.2 GPU Visualizer (vis Command)

Command Description
vis Open the GPU Visualizer window
ProfileGPU Single-frame GPU profile
r.ScreenPercentage 50 Lower render resolution to test GPU load

4.3 RenderDoc / PIX

  • RenderDoc: A general-purpose GPU frame analyzer with UE5 support. Lets you inspect pixel history and shader resources per Draw Call.
  • PIX (Windows): Microsoft’s GPU profiling tool, particularly well-suited for Xbox / DirectX projects.
  • Xcode GPU Profiler (macOS/iOS): GPU profiling for the Metal platform.

Note: This functionality (originally a UE4 feature) has been integrated into the GPU view inside UE5’s Unreal Insights.


5. Memory Analysis

Tool / Command Description
obj list List all UObjects and their memory usage
obj list class=StaticMesh List objects of a specific class
memreport -full Generate a full memory report
stat memory Live memory statistics
mimalloc UE5’s optional modern memory allocator (configured in .ini)

6. Blueprint Profiling

  • Blueprint Profiler: Editor menu Tools → Blueprint Profiler
  • Inspect execution time per Blueprint node to identify slow ones
  • Optimization tip: migrate hot-path Blueprint logic to C++

7. Debugging Tips

7.1 UE_LOG Timestamp Timing

A quick way to measure how long a code segment takes — just print timestamps to the log:

1
2
3
4
UE_LOG(LogTemp, Warning, TEXT("disconnect(): waiting for receiveThread %s"), *FDateTime::Now().ToString());
QUICK_SCOPE_CYCLE_COUNTER(STAT_WaitReceive);
WaitForSingleObject(receiveThread, INFINITE);
UE_LOG(LogTemp, Warning, TEXT("disconnect(): receiveThread finished %s"), *FDateTime::Now().ToString());

Example output:

1
2
Warning  LogTemp  disconnect(): waiting for receiveThread 2025.12.04-16.48.31
Warning LogTemp disconnect(): receiveThread finished 2025.12.04-16.48.46

Subtract the two timestamps to get the actual elapsed time for that code segment (roughly 15 seconds in this example).

7.2 Disabling Local Compiler Optimizations

During debugging, compiler optimizations can interfere with breakpoints and variable inspection. You can temporarily disable them for a specific block of code:

1
2
3
4
#pragma optimize("", off)
// Place the function or code you want to debug here
// Optimizations are off — variables won't be eliminated, breakpoints will hit normally
#pragma optimize("", on)
  • "" means use the current project’s optimization option set
  • off/on temporarily disables and re-enables optimization
  • Only affects code from the off pragma onward (until the matching on); typically wraps a single function implementation

7.3 Automated Performance Testing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FMyPerfTest,
"Performance.MyGame.StressTest",
EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter
)

bool FMyPerfTest::RunTest(const FString& Parameters)
{
AddCommand(new FWaitLatentCommand(5.0f)); // Wait for warm-up
AddCommand(new FMeasureTimeLatentCommand(
TEXT("StressTest_100Enemies"),
[this]() { Spawn100Enemies(); },
0.5f // Expected time threshold
));
return true;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1. Identify the Bottleneck
stat fps + stat unit → determine whether it's a CPU or GPU bottleneck

2. CPU Bottleneck
├─ stat unitgraph → identify which thread is slow
├─ Unreal Insights Timing → pinpoint the specific function
├─ Check Tick, Blueprints, GC, physics
└─ Use SCOPE_CYCLE_COUNTER for finer-grained measurement

3. GPU Bottleneck
├─ ProfileGPU → inspect pass distribution
├─ stat scenerendering → Draw Calls / triangle count
├─ GPU Visualizer → visual analysis
└─ RenderDoc → deep dive into individual Draw Calls

4. Memory Issues
├─ stat memory → overview
├─ memreport -full → detailed report
└─ Unreal Insights Memory Insights → memory leak tracking

5. Quick Debugging
├─ UE_LOG + timestamps → quickly pinpoint slow code
└─ #pragma optimize("", off) → disable optimizations for easier breakpoint debugging

References

https://zhuanlan.zhihu.com/p/273608458