Branchless Programming
Every if-statement forces the CPU to guess which way execution will go before it knows for sure, and branchless code rewrites the logic with arithmetic and bit tricks so there's no guess to get wrong.
Prerequisites: Branch Prediction
Modern CPUs execute many instructions at once in a pipeline, which only works if the CPU knows which instructions come next. An if statement is a fork in the road — the CPU can't wait to compute the condition before starting the next batch of work, so it guesses, via branch prediction, and starts executing down the predicted path speculatively. Guess right, and the guess cost nothing. Guess wrong, and the CPU has to throw away everything it started on the wrong path and restart — a branch misprediction, typically costing 15-20 cycles, small on its own but brutal when it happens millions of times a second on data the predictor can't learn a pattern from.
Branchless programming rewrites conditional logic to avoid the if entirely, replacing it with arithmetic, bitwise operations, or CPU instructions that compute both outcomes and select between them without ever forking control flow. There's nothing to mispredict because there's no prediction being made — every code path always executes, and the "choice" is made by arithmetic instead of a jump.
Branchless code is not about being cleverer than an if, it's about removing the need for the CPU to guess. It only pays off when the branch is genuinely unpredictable — a coin-flip condition on essentially random data — because computing both outcomes always costs something, and a well-predicted branch (like a loop counter) already costs almost nothing.
Branch versus branchless
Take a common step in filtering trades: clamp a signed quantity to be non-negative.
// branchy
int clamped = (qty < 0) ? 0 : qty;
// branchless (no comparison-driven jump)
int mask = qty >> 31; // all 1s if negative, all 0s if non-negative
int clamped = qty & ~mask; // zeroes qty out exactly when it was negative
The ternary version compiles, on some architectures, to an actual conditional jump — a genuine branch for the CPU to predict. The bit-trick version has no branch at all: it always executes the same three instructions (shift, invert, and), and the arithmetic itself produces the right answer regardless of the sign, so there's never anything to mispredict.
Worked example
Filtering 10 million random signed integers with the branchy qty < 0 ? 0 : qty version: the branch predictor, faced with genuinely random signs, gets it wrong close to 50% of the time. Benchmarked, this takes roughly 28 milliseconds. The branchless bit-mask version, with identical output, takes roughly 9 milliseconds — about 3x faster, purely from eliminating mispredictions on data with no learnable pattern.
Now run the same branchy code on data that's mostly positive (say 95% positive quantities, typical for a filtered order flow) — the predictor learns "usually not negative" and gets it right most of the time. There, the branchy version runs almost as fast as the branchless one, because there was little misprediction cost left to remove.
What this means in practice
Branchless tricks are worth reaching for in the innermost loops of latency-critical code — a matching engine's hot path, a risk check run on every tick — and specifically where the condition depends on data the predictor can't learn a pattern from. Applying them everywhere is a mistake: on predictable branches (loop bounds, rare error checks), branchless rewrites add complexity and sometimes more instructions for no measurable gain.
Branchless code is harder to read and easier to get subtly wrong than the equivalent if, since correctness now depends on bit-level arithmetic instead of an obvious comparison. Reach for it only after profiling has actually identified a specific branch as a hot, mispredicting bottleneck — never as a reflexive style choice, and never without a benchmark confirming it helped.
Related concepts
Practice in interviews
Further reading
- Warren, Hacker's Delight (ch. 2, 'Basic Methods')