An optimization can improve the editor’s frame time without improving the game on a player’s device. Before comparing numbers, decide which build and workload they describe.
For release decisions, use a build close to the one you ship, running on the target hardware. Use a diagnostic build to investigate expensive frames. Then return to the production-like build to check the fix. This article calls that production-like build, with a small amount of instrumentation, a measurement build. It is a project convention, not a build configuration shared by the three engines.
Choose a build for the question
Editor profiling is useful for finding expensive scripts, asset work, and allocations during development. A standalone build with diagnostics, such as a Unity or Unreal Engine Development build, removes the editor process from the measurement and supports detailed profiling on the target device. Neither result should be silently substituted for the performance of a shipping build.
Keep each result tied to its build configuration. A different configuration can change both the cost and the behavior being measured.
The editor shares resources with editor windows, asset processing, and other tools. Diagnostic builds can include extra code and profiling overhead. The differences depend on the engine, backend, platform, and enabled options; there is no fixed overhead to subtract. An expensive function found in a diagnostic build is still worth investigating. Confirm its effect in the build your players will run.
Make the runs comparable before changing code
A build setting alone cannot make a benchmark reproducible. Start with one scenario, such as loading a saved scene and following a fixed camera route, and record these conditions with every result:
- Build ID and commit, engine version, scripting backend, and build configuration
- Device, GPU driver, resolution, quality settings, VSync, and frame-rate cap
- Scene, starting state, inputs or route, and measurement duration or frame count
- Warm-up policy and cache state, especially for shader compilation and first loads
- Enabled instrumentation, sampling interval, and any missing or dropped samples
Run the unchanged baseline more than once before testing the candidate. If those results vary as much as the expected improvement, investigate the test conditions first. A repeat helps reveal variability; it does not by itself establish statistical confidence. On mobile devices and laptops, keep power and thermal conditions comparable too.
Keep first-load tests separate from warmed-up gameplay. Excluding shader compilation may be useful for a steady-state test, but it would hide the problem in a test intended to measure first-launch stutter.
Configure a measurement build
Unreal Engine: Test when your engine build supports it
Epic describes Test as Shipping with some console commands, stats, and profiling tools enabled. It is a useful starting point when you need those diagnostics alongside release-oriented optimization. Development also enables most optimizations; it is not equivalent to an unoptimized debug build.
Check the engine installation before selecting Test. The default Installed Build configurations are Shipping, Development, and DebugGame. Test generally requires a source-built engine or a custom Installed Build that includes it. Selecting Test in a command does not add the missing binaries.
For an engine that supports it, a Windows packaging command can take this form; replace the project path and use the Automation Tool launcher for your installation:
RunUAT.bat BuildCookRun -project="C:/Path/MyGame.uproject" -platform=Win64 -clientconfig=Test -cook -stage -pak -package -build
Check which diagnostic commands and counters are actually available in that package. Do not make the measurement depend on UE_LOG being present: logging is compiled out by default in Test and Shipping, and changing that setting can require rebuilding the engine. Keep measurement code narrowly scoped to the project. If you use Development for diagnosis because Test is unavailable, verify the result again in Shipping with the instrumentation your project supports.
Unity: match the shipping backend and compiler settings
In Unity 6, create a dedicated Build Profile and turn Development Build off for the production-like comparison. Match the scripting backend and compiler configuration to the product: if you ship IL2CPP, measure IL2CPP; if you ship Mono, retain Mono. For IL2CPP, match Release or Master as appropriate. Switching backends while comparing a code change introduces another variable.
Keep a separate Development profile for the Unity Profiler. Profiler attachment and Deep Profiling are diagnostic choices, not settings to leave enabled unnoticed during a release comparison. Before Unity 6, a BuildPipeline script can preserve the same separation.
A profile-specific scripting define can limit diagnostic event markers to the measurement build:
using System.Diagnostics;
public static class Measure
{
[Conditional("MEASUREMENT")]
public static void Mark(string label)
{
UnityEngine.Debug.Log($"[measure] {label} @ {UnityEngine.Time.frameCount}");
}
}
Add MEASUREMENT to the profile’s scripting defines, then call Measure.Mark at scenario boundaries. This example is a log marker, not a frame-time collector. Avoid calling it every frame: formatting and writing logs add work. [Conditional] removes call sites when the symbol is absent; it does not remove the method body from the assembly.
Godot: release export with a measurement feature tag
Duplicate the export preset and turn Export With Debug off. Keep the renderer, platform, and export templates aligned with the release you intend to ship. You do not need to compile custom engine templates just to define a measurement preset.
Add a custom feature such as measurement in the export preset:
custom_features="measurement"
For a C# project, a marker at a scenario boundary can use that tag:
if (OS.HasFeature("measurement"))
{
GD.Print($"[measure] scenario-start frame={Engine.GetFramesDrawn()}");
}
Custom feature tags apply to exported projects, not ordinary runs from the editor. Verify the exported executable. As with the Unity example, the marker only identifies a point in the scenario; the metrics need their own collector. Keep its overhead and configuration consistent across runs.
Read metrics at the resolution you collected
For a 60 FPS target, the frame budget is about 16.67 ms; at 30 FPS it is about 33.33 ms. Inspect the distribution of frame times, not only the average FPS. P95 is the value at or below which 95% of the observations fall. It can still hide rarer, severe hitches, so inspect higher percentiles, counts above your frame budget, and a timeline when available.
The population matters. A P95 calculated from periodic telemetry samples is a percentile of those samples. It is not automatically the P95 of every rendered frame. Record the sampling method before comparing two reports.
Attach scene, position, and camera direction when the problem is spatial. A slow frame near a doorway with the camera facing a crowded room is a more useful reproduction clue than a session-wide average. It still does not identify which object or system caused the cost.
Diagnose the regression, then repeat the original test
Use the recorded conditions to reproduce the problem with Unity Profiler or Memory Profiler, Unreal Insights, or Godot’s profiler. For Godot C# code, use a suitable .NET profiler for script-level investigation; the built-in script profiler is for GDScript.
Change one suspected source of cost, then rerun the original scenario in the measurement build. Check whether the improvement persists across repeats and whether memory, loading, or another part of the frame got worse. A faster diagnostic trace is an intermediate result.
The same evidence can guide decisions about headroom. A scene below its frame budget may have room for more effects or objects, but a spare render-thread budget does not imply spare GPU time or memory. Measure the proposed addition under the same target-device conditions.
Use Framedash to keep the comparison inspectable
Framedash’s dashboard heatmaps help locate collected performance samples on a map and filter them by build, platform, and available attributes. SDK setup and supported collectors differ by engine: see Unity, UE5, and Godot C#. Verify the metrics your target build actually sends before relying on a chart.
For Unity, the performance-run comparison pilot gives a concrete starting point: capture a baseline, an unchanged repeat, and a candidate on your PC, then inspect their conditions and frame-interval summaries. It needs no map. These intervals are measured between SDK Update callbacks, not at GPU completion or display presentation. The pilot reports comparable or inconclusive evidence; it does not issue an automatic regression verdict.
Start with one repeatable scenario and one change you want to verify. Keep the baseline result and the test conditions alongside the candidate so the next comparison can answer the same question.