Abstract
Problem: When and how should game code be optimized, and why is it so often misunderstood by non-programmers?
Approach: Tim Cain draws on decades of experience shipping games (Fallout, Arcanum, South Park: The Stick of Truth) to explain optimization philosophy, techniques old and new, and the organizational challenges it creates.
Findings: Optimization must be deferred until code is stable and real bottlenecks are measurable. It involves trading CPU cycles against memory, has grown more compiler-driven over the decades, and remains inherently unpredictable to schedule — which pushes it into crunch. Designers and artists can help by understanding computational cost and self-limiting expensive requests.
Key insight: You cannot optimize early — and you cannot accurately estimate how long optimization will take, making it an unavoidable late-project wildcard that management cannot simply "manage away."
Faster vs. Tighter
Optimization means making code faster (fewer CPU cycles) and tighter (less memory). These are frequently a trade-off: unrolling loops makes code faster but larger; caching precomputed values saves cycles but costs memory. In rare, wonderful cases you find code that is both faster and smaller.
Don't Optimize Early
Tim's central rule: you cannot optimize early. You must wait until you can see where cycles are being spent and where memory is being used. Optimizing too soon means those optimizations go to waste when features change or code is rewritten.
The very first Fallout demo — two years before the game shipped — was intentionally unoptimized. That was correct. You don't want to polish code that will be torn apart.
The Arcanum Prototype Exception
Arcanum's prototype system is an interesting counterexample. By making every object reference a prototype for its default values, the team saved enormous amounts of memory at a small cycle cost (an extra lookup when an object didn't override a field). Because this was an architectural decision made at the foundation, it paid off for the entire project — but this is design-level optimization, not the late-stage tuning Tim is warning against.
Assembly, Compilers, and the Generational Shift
The Old Way: Hand-Written Assembly
Fifteen to twenty years ago (and earlier), programmers would identify hot loops via profilers and rewrite C code into hand-tuned assembly. Games were written in C (not C++) because C compiled faster and was easier to optimize. A skilled human could often beat the compiler.
The New Way: Compiler Flags and Pragmas
Modern compilers have largely closed that gap. Programmers now use optimization flags (-O2, -Os, etc.) and pragmas to control optimization per module or even per code section. You can optimize for speed, size, or turn optimization off entirely for problem areas.
Tim notes that less experienced programmers often don't know they can inspect the compiler's assembly output — and even those who do sometimes consider it a waste of time, preferring to toggle flags randomly until things work. This bothers him ("doing work without knowledge bothered me"), but he concedes the flag-toggling approach is often faster in practice. Both tools should be in your toolbox.
Platform-Specific Pain
Optimization isn't just about algorithms — it's about hardware. Tim recalls having to write separate assembly routines for every Super VGA card. Even modern consoles differ: the PS3's Cell architecture made South Park: The Stick of Truth particularly difficult. Breakpoints wouldn't stop where expected because multiple processors were running code in a distributed fashion.
"Optimization Isn't Important Anymore" — Tim Disagrees
Tim pushes back on the idea that modern hardware makes optimization unnecessary. He still sees wasteful patterns in production code:
Recalculating stable values. Programmers recalculate values every time they're needed even when the inputs haven't changed, rather than caching the result and marking it dirty when dependencies change.
Allocating arrays to find a single element. On one game, a programmer would request an array of all creatures in the area, sort it by distance, grab the nearest one, and throw the rest away — every time the nearest creature was needed. Tim replaced this with an event-driven system: whenever a creature moved, it checked if it was now the closest to the player. When something died, a single recalculation pass ran. The result was both faster and used less memory.
Sorting Algorithms: A Classic Optimization Lesson
There is no single best sorting algorithm. Some degrade on nearly-sorted data; some aren't worth their overhead for small collections. For small sets, use whatever is simple and memory-efficient. For large sets, study the trade-offs carefully. Radix sort is blazingly fast but memory-hungry and has data constraints.
Why Optimization Causes Crunch
Because optimization must be deferred until late in the project, and because it is nearly impossible to estimate accurately — Tim says he has never met a programmer who could reliably predict how long optimization would take — it inevitably bleeds into crunch. This isn't a management failure that can be "managed away." It is an inherent property of the work.
Designers and Artists Can Help
Optimization isn't purely a programming problem. Designers and artists who understand computational costs can self-limit their expensive requests:
- Lighting: Do you really need dynamic lights? How many can move at once? Static lighting is far cheaper, and the rendering approach can be adapted based on the answers.
- Drug system (Fallout): The original Fallout drug design had every drug working in a completely unique way — different effect types, durations, stacking rules. It would have taken months to code. Tim worked with the lead designer to create a generic drug system: a unified set of possible effects, variable durations, and phased stages (e.g., a drug makes you perceptive, then weakens you as it wears off, then returns to normal — or never returns). Once built, new drugs could be created with pure data, no new code needed.
Listen to Your Programmers
Tim's closing advice to managers and non-programmers: listen to your coders when they explain how things will be built and optimized. Many programmers are introverts, so try to listen extra hard. Tim credits this mutual effort — programmers speaking clearly, managers listening carefully — with saving many of his projects.
References
- Tim Cain. YouTube video. https://www.youtube.com/watch?v=QWAetn0Ch9I