Optimization Best Practices

How professional teams approach performance: set budgets, measure on real hardware, find whether you're CPU- or GPU-bound, fix the biggest problem first, and keep it from coming back. This page covers the process and the engine-level settings. For script-level tips, see Optimizing Your Code.

A household budget. You don't save money by randomly skipping coffee. You write down how much you can spend (the frame budget), track where the money actually goes (profiling), cut the biggest expense first, and check your statement every month so it doesn't creep back up (regular profiling).

1. Decide your targets early

Performance is much cheaper to plan for than to fix at the end. Before production, write down:

TargetExample
Frame rate per platformPC 60 FPS (16.6 ms), Switch-class 30 FPS (33.3 ms), mobile 30 or 60 FPS
Minimum-spec device"A 4-year-old mid-range Android phone"
Memory budgetStay well below what the weakest device allows, leaving room for the OS
Load time"Any level loads in under 10 seconds"
Rough per-system split (example)Of 16.6 ms: gameplay scripts 3 ms, physics 2 ms, animation 2 ms, rendering 6 ms, headroom 3 ms

Aim below the limit. A game that takes exactly 16.6 ms per frame drops frames whenever anything extra happens. Leave headroom for spikes like explosions and loading.

A frame within budget (60 FPS)Scripts3 msPhysics2 msAnim2 msRendering6 msHeadroom3.6 msThe same frame during a big explosionScripts3 msPhysics2 msAnim2 msRendering6 msExplosion2.8 ms16.6 ms= 60 FPSstill fits,thanks to headroom0 ms4 ms8 ms12 ms16 ms
An example 60 FPS budget. Spending all 16.6 ms in a normal frame leaves nothing for spikes; keeping headroom means an explosion (highlighted) doesn't drop a frame.

2. Profile on the target hardware

ToolUse it to find
Profiler (Window > Analysis)Where CPU time goes each frame; GC allocations; rendering, physics, audio, memory overview
Profile Analyzer (package)Compare many frames, or before/after a change, statistically
Memory Profiler (package)What's using memory: textures, meshes, leaked objects
Frame DebuggerEvery draw call and why objects didn't batch
Platform toolsXcode Instruments (iOS), Android GPU Inspector, RenderDoc, PIX: deep GPU analysis

3. Find out what's limiting you

Fixing the wrong side wastes time. Figure out what you're bound by first:

Bound bySignsTypical fixes
CPU: scriptsProfiler shows your Update methods, BehaviourUpdate or coroutines taking most of the frameCache, do less per frame, pooling, Jobs + Burst
CPU: renderingLots of time preparing draw calls on the main/render thread; high Batches/SetPass countsBatching, instancing, fewer materials, LODs, culling
CPU: physicsPhysics.Simulate / FixedUpdate is largeSimpler colliders, layer matrix, larger fixed timestep
GPUThe CPU spends time waiting for the GPU (markers such as Gfx.WaitForPresentOnGfxThread); lowering resolution raises FPSLower resolution/render scale, cheaper shaders and post-processing, less overdraw and fewer shadows
Garbage collectorRegular spikes labeled GC.CollectRemove per-frame allocations; enable incremental GC

4. Measure, change one thing, measure again

  1. Record a baseline in a repeatable situation (same scene, same camera path).
  2. Change one thing.
  3. Record again and compare (the Profile Analyzer is ideal).
  4. Keep it only if it helped. Write down what you changed and the result.

Add your own markers so your systems show up clearly in the Profiler:

using Unity.Profiling;
using UnityEngine;

public class EnemyAI : MonoBehaviour
{
    private static readonly ProfilerMarker ThinkMarker = new ProfilerMarker("EnemyAI.Think");

    void Update()
    {
        using (ThinkMarker.Auto())   // everything in this block is timed under "EnemyAI.Think"
        {
            Think();
        }
    }

    void Think() { /* ... */ }
}

5. Architecture choices that scale

6. Assets and memory

AssetGood practice
TexturesUse platform compression (e.g. ASTC on mobile). Set Max Size to what's actually visible. Mipmaps on for 3D, off for UI. Atlases for UI and 2D sprites.
MeshesTurn off Read/Write Enabled unless scripts read the mesh (saves a CPU-side copy). Use LODs. Compress meshes if precision allows.
AudioMusic: Streaming. Short, frequent sounds: Decompress On Load. Everything else: Compressed In Memory. Mono for 3D sounds.
AnimationCompress clips; set Animators' Culling Mode so off-screen characters don't update fully.
LoadingUse Addressables to load and unload content on demand. Avoid the Resources folder for large content: everything in it is packed into the build and indexed at startup.

7. Engine and build settings

8. Keep it fast

Checklist

  1. Targets and budgets written down per platform.
  2. Profiled a development build on the minimum-spec device, including a long session.
  3. Know whether you're CPU-bound (which part?) or GPU-bound.
  4. No per-frame GC allocations in gameplay.
  5. Frequently spawned objects are pooled.
  6. Draw calls checked in the Frame Debugger; materials shared; instancing where it helps.
  7. Texture, audio and mesh import settings reviewed.
  8. Release build: IL2CPP, no dev logs, correct target frame rate.

See also