Game Development · Guide

Profiling & Shipping

Finding where the frame time actually goes, keeping the asset pipeline honest, and everything between a working build and a released game.

— min read Game Development

The Last Twenty Percent

A game that runs at 60 fps on your machine and 22 fps on the target hardware is not nearly finished. Optimisation, platform work and certification routinely take as long as building the thing did.

Performance here is a budget, not a score. At 60 fps a frame is 16.6 ms, and every system spends part of it. Once the budget is written down, optimisation becomes a matter of finding who overspent instead of a general anxiety about speed.

Profiling

Measure before changing anything. Intuition about where time goes is wrong often enough that acting on it wastes days and sometimes makes the frame slower.

The first question is always CPU-bound or GPU-bound, because the answers have nothing in common. Drop the resolution: if the frame rate improves, you are GPU-bound and the work is in shaders, overdraw and fill rate. If nothing changes, you are CPU-bound and the work is in game logic, physics and draw call submission.

SymptomUsual cause
Low average frame rateToo much per-frame work — profile and budget it
Periodic hitchesGarbage collection, or loading on the main thread
Slow only on one platformMemory bandwidth or fill rate, not logic
Degrades as the level fillsAn O(n²) system — usually collision or AI queries
Average frame rate hides the problem. Players feel the worst frames, so track the 99th percentile — a steady 45 fps is more comfortable than 60 fps punctuated by 200 ms stalls.

Allocation is the usual source of hitches in a managed language. Allocating inside the frame loop hands work to the garbage collector, which eventually stops the world at the least convenient moment. Pool objects and reuse buffers instead of allocating per frame.

The Asset Pipeline

Assets outweigh code by orders of magnitude, and the pipeline that turns source art into shipped data decides both load times and download size — the two numbers that most affect whether anyone plays past the first minute.

ConcernPractice
TexturesGPU-compressed formats per platform, with mipmaps
AudioCompressed for music, uncompressed for short frequent effects
MeshesLevel-of-detail chains so distant objects cost less
Source artVersion-controlled with LFS, never the imported output
LoadingStreamed in the background, never on the main thread

Import settings are where builds quietly go wrong: an uncompressed 4K texture on a background prop, or a fully decompressed music track resident in memory, costs more than any amount of code optimisation will recover. Audit them, and keep the import step automated so nobody has to remember.

Texture memory is usually the platform limit that bites first, and it is not the file size — it is width × height × format on the GPU, mipmaps included.

Platforms & Release

Every console platform has a certification process: a list of technical requirements covering suspend and resume, controller disconnection, save data corruption, storage-full handling, age ratings and terminology. Failing it means resubmission and weeks of delay, so it is read at the start of a project rather than the end.

TargetWatch for
PCUnbounded hardware variance, drivers, alt-tab and windowing
ConsoleCertification, fixed memory budgets, mandated behaviours
MobileThermal throttling, battery, interruptions, tiny memory
WebDownload size, browser APIs, no threads for free

Mobile deserves special care because sustained performance is not peak performance. A device hits its thermal limit after a few minutes and downclocks — a game that benchmarks at 60 fps can settle at 35 fps once the phone is warm, and that is the number players experience.

Ship with crash reporting and telemetry from day one. Where players quit, which levels take four attempts, which GPU crashes — none of it can be recovered after release if it was not being recorded. And build the release candidate on CI, not on a developer's machine: a build nobody can reproduce is a build nobody can fix.

Interview Questions

How do you tell CPU-bound from GPU-bound?

Lower the resolution. If the frame rate improves the GPU was the limit; if it does not move, the CPU is, and the work is in logic, physics or draw call submission.

Why track the 99th percentile rather than the average?

Players feel the worst frames. A steady 45 fps is more comfortable than 60 fps with periodic 200 ms stalls, and an average hides exactly that.

What causes periodic hitches in a managed language?

Allocation inside the frame loop. The garbage collector eventually runs and pauses the game — pooling objects and reusing buffers removes the cause rather than the symptom.

Why do import settings matter so much?

They decide runtime memory and load times. An uncompressed 4K texture on a background prop costs more than most code optimisations will ever recover.

What does console certification cover?

Technical requirements — suspend and resume, controller disconnection, save data handling, storage-full cases, ratings and terminology. Failing means resubmission and weeks of delay.

Why is mobile performance different?

Thermal throttling. A device downclocks after a few minutes of load, so sustained frame rate is well below the benchmarked peak, and that is what players actually get.

Quick Quiz

1. Lowering resolution improves frame rate. You are…
2. The most useful frame-time metric is…
3. Periodic hitches in a managed runtime usually mean…
4. Texture memory on the GPU is determined by…
5. A mobile game benchmarks at 60 fps but settles at 35. Most likely…