Conditional move (cmov / csel)
- title
- Conditional move (cmov / csel)
- type
- concept
- summary
- A branchless instruction that picks one of two values from a flag; faster than a branch only when the branch is unpredictable
- tags
- performance, microarchitecture, compilers
- sources
- lucky-code
- created
- 2026-07-18
- updated
- 2026-07-22
A conditional move writes one of two source values into a destination depending
on a condition flag, without branching. x86 calls it cmov; AArch64 calls it
csel (conditional select). Instead of jumping over a store, the CPU computes
both candidate inputs, then the instruction commits the one the flag selects.
Control flow stays straight โ there is exactly one path through the loop.
The tradeoff
A conditional move is not free speed. It trades a possible pipeline flush for guaranteed work. A predicted-correct branch is nearly free: the CPU speculates past it and the speculation holds. A conditional move always does the same work regardless of the flag, so it never mispredicts, but it also never gets the "skip the other side" discount a well-predicted branch gives.
So the rule is about predictability:
- If the branch is easy to predict (nearly always taken, or nearly always not), a branch is faster โ the predictor is right almost every time and the branch costs almost nothing.
- If the branch is a coin flip, a conditional move is faster โ a real branch would mispredict roughly half the time, and each miss flushes the pipeline and re-fetches instructions.
Quicksort partitioning on random data is close to the worst case for a branch
predictor: about half the elements fall each side of the pivot with no pattern.
That is exactly where the branchless form pays off. See
compiler-codegen-luck for a case where the same partition loop ran 6x faster
once Clang emitted csel instead of a branch.
Who decides
The compiler picks between a branch and a conditional move at compile time,
before it knows the data distribution, so it is guessing. The choice is also
brittle: on LLVM it comes out of the SimplifyCFG pass matching a specific IR
shape, and small, semantically irrelevant differences in how the source is
written can flip the decision. GCC and Clang often disagree on the same code.
Where you know the branch is genuinely unpredictable, you can nudge the
compiler. Clang has __builtin_unpredictable() to mark a condition as a coin
flip. A ternary (p = cond ? a : b) frequently lowers to a conditional move but
is not guaranteed to. When it matters, the reliable check is to read the
generated assembly rather than trust the source shape. The deeper reason this is
hard: whether a conditional move helps depends on runtime data the source cannot
express, so no amount of source-level cleverness settles it on its own.
Conditional select is one entry in a much larger catalog of branchless integer tricks โ saturating arithmetic, sign extraction, min/max without a compare-jump, division by a constant. Warren's hackers-delight is where those live, and reading it is the fastest way to recognize the shapes a compiler is reaching for when it emits something unexpected.