Map

Conditional move (cmov / csel)

Wiki conceptperformancemicroarchitecturecompilers โ†ณ show in map Markdown
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.