Overview

11 Branching, performance, and extendibility

Branches are shown as a central trade-off in game code: they can make logic harder to understand while also slowing down hot paths. Even simple constructs like if, switch, and the conditional operator add cognitive overhead because readers must inspect both outcomes, and on modern CPUs they can trigger prediction and pipeline penalties. The chapter emphasizes that predictable branches are less costly than random ones, but the safest performance strategy is still to reduce branching wherever possible rather than assume the compiler or hardware will make it free.

A major technique for doing that is to reorganize data so the runtime can process homogeneous groups without repeatedly checking conditions. Examples include splitting alive and dead enemies into separate arrays, using unused pool-index arrays instead of scanning every pooled object for availability, and separating UI into distinct scripts for different menus or screens. This improves both speed and maintainability: loops become simpler, debugging becomes more direct, and changes to one feature are less likely to affect unrelated code. The chapter also argues for keeping ownership of object lifetimes in your own code, avoiding repeated null checks on Unity objects, and using zero-tolerance error handling in development so problems are caught early instead of hidden by noisy runtime behavior.

The chapter also warns about hidden branches, especially those introduced by C# default parameter values, which can make behavior less obvious and create future maintenance hazards. A method may appear to have a simple call signature while secretly containing a branch that changes how it behaves. Overall, the message is that high-performance Unity code should favor explicit, branch-light design: validate data at the boundaries, keep runtime systems predictable, separate data by behavior, avoid fire-and-forget lifetimes that produce null-object bugs, and make code structure reflect actual game responsibilities rather than convenience alone.

When we test a predictable branch result against a random one, the random branch result is 1.5x to 2.12x slower.
A task to add a new paralysis feature to our enemies.
From these results, we can see that adding a null check on a Unity object can have a significant effect on our performance.
In our AssetManager script, we assign prefabs and menus in the Unity Editor during tooltime. These assets will not change at runtime, so we can skip checking whether they are null.
If we own the lifetime of a Unity object, we can safely skip checking it for null.
We can skip null checks even with dynamic object allocations if we know when they are instantiated and when we are going to deallocate them.
Our Top-Bar, which is found in nearly every menu, with a score on the left, a title in the middle, and a settings button on the right.
Three menus, each with a slightly different top bar. The settings menu has a title and a different button. The in-game menu has the current score, the title, and an options button. The game over menu has only the options button.

Summary

  • Branches increase both runtime cost and code complexity, even when they seem simple.
  • Branch misprediction can cause significant slowdowns due to pipeline flushes and wasted CPU work.
  • Even predictable branches can still be slower than branchless alternatives, especially in tight loops.
  • Unpredictable branch patterns (e.g., random conditions) hurt performance most.
  • Move conditional logic outside of core functions to keep them simple and predictable.
  • Avoid excessive data checks by owning the data's lifetime.
  • Unity’s null checks also account for the native object’s lifetime, adding overhead when repeated in hot runtime loops.
  • We can avoid checking GameObjects for null multiple times by checking them once during loading.
  • We can adopt a zero-tolerance approach to errors to catch them during development.
  • If we own the lifetime of data and GameObjects, we can avoid null checks.
  • Avoid coroutines and other asynchronous calls that can unexpectedly try to access null data.
  • We can reduce branching by separating data. For example, separating alive and dead enemies so we don’t have to check every enemy’s status.
  • We can reduce code complexity and improve extensibility by separating common UI elements.
  • Hidden branches increase the risk of unexpected behavior and hard-to-find bugs.

FAQ

What kinds of branches can affect both performance and code complexity in Unity games?Branches such as if, switch, and the conditional operator ?: can increase code complexity and slow down hot runtime code by adding extra instructions and making CPU prediction harder.
Why do branches make code harder to read and debug?Each branch adds a condition that must be understood, along with both possible outcomes. This means you often have to inspect more code paths and step through more lines during debugging to see what actually executes.
How does branch prediction help CPUs, and what happens when it fails?CPUs predict which branch path will be taken so execution can keep moving. If the prediction is wrong, the CPU discards the work done on the wrong path, flushes the pipeline, and resumes on the correct path, which causes a performance penalty.
Why can a simple branch slow code down even if it seems easy to predict?Even predictable branches add comparison and jump instructions, and they make the code less straightforward for the CPU to optimize. In practice, this can still create a noticeable slowdown compared with code that has no branch at all.
How can data-oriented design reduce branch checks?By separating data based on branch outcomes, such as keeping alive and dead enemies in separate arrays, you can loop only over the items you need and avoid checking each one individually every frame.
Why should branches be moved out of hot functions like moveEnemy()?Putting the branch outside keeps the function’s behavior simple and predictable. Then moveEnemy() always moves an enemy, instead of sometimes doing nothing, which improves readability and reduces runtime branching.
Why are null checks on Unity objects more expensive than normal C# null checks?Unity overloads equality for UnityEngine.Object, so checking == null also verifies whether the underlying native object still exists. That extra work makes repeated null checks in hot loops more costly.
What are the best ways to avoid repeated null checks in Unity gameplay code?You can check once when data is loaded, own the lifetime of the object so you know when it is valid, enforce zero tolerance for errors during development, and avoid fire-and-forget behaviors like unmanaged coroutines or static subscriptions that outlive their targets.
How can separating objects or UI elements reduce branching and improve extendibility?Instead of one shared script with many branches, create separate objects or scripts for different use cases. This makes each piece easier to debug, easier to change, and less likely to break other features when you add new behavior.
Why are default parameter values considered hidden branches?Default parameter values can hide behavior from the call site, so readers may not realize a method contains a branch or an optional condition. This can make code harder to understand and can lead to accidental behavior changes later if the default is modified.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • High Performance Unity Game Development ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • High Performance Unity Game Development ebook for free