Course contentsShow
← Unreal Engine Game Development
Lesson 770 of 88011. Performance Profiling and OptimizationFree lesson

Analyzing CPU Timing in Insights

You will learn to interpret timing tracks to identify specific game thread stalls, task graph bottlenecks, and expensive function calls.

In this lesson, you will learn how to parse Unreal Insights timing tracks to isolate frame-breaking stalls, identify Task Graph contention, and drill down into the specific function calls consuming your frame budget.

Reading the CPU Timing Track

When you open a trace in Insights, the CPU Timing tab is your primary dashboard. Each thread is displayed as a horizontal lane containing blocks representing the duration of execution for specific scopes—typically marked by TRACE_CPUPROFILER_EVENT_SCOPE macros in C++.

The length of these blocks represents wall-clock time. If you notice a frame where the GameThread has a wider-than-normal gap or a singular, massive block, you have found a stall.

Identifying Bottlenecks

  1. Game Thread Stalls: Look for deep, vertical stacks of blocks. A thin, long block usually indicates a function waiting for a lock or a slow synchronous operation (like loading an asset or a heavy physics synchronization). If the block is wide but short, it represents a high-frequency function that is being called too many times per frame.
  2. Task Graph Contention: Often, the Game Thread stall is actually waiting for a background thread to finish. When you see a large gap on the Game Thread, switch to the TaskGraph worker threads below it. If you see many small tasks being kicked off but none completing quickly, you are likely suffering from "task overhead"—creating too many micro-tasks that force the CPU to spend more time managing threads than doing actual work.
  3. Function Drill-Down: Once you isolate a spike, click the block. The Timeline View will highlight the parent-child relationships. Use the Timing Insights panel to sort by Inclusive Time to find the root cause, or Exclusive Time to see which function is doing the heavy lifting itself rather than delegating to children.
// Example: Adding a custom scope to identify a suspected bottleneck
void AMyActor::HeavyComputation() {
    TRACE_CPUPROFILER_EVENT_SCOPE(AMyActor_HeavyComputation);
    // ... complex logic here
}

By wrapping your suspect code in a TRACE_CPUPROFILER_EVENT_SCOPE, you gain an immediate visual representation in Insights, allowing you to correlate code changes directly with spikes in the timing track.

Key Takeaway: Use the Timing track to distinguish between high-frequency "death by a thousand cuts" function calls and singular, frame-spiking stalls, then verify the cause using the Timing Insights table view.