branch-prediction-and-speculation · git:20260905.fd53e99 · 2026-09-05 · sha256 f01cc380134dd446

branch-prediction-and-speculation git:20260905.fd53e99A

Immutable. This exact content is served forever at /api/v1/blob/f01cc380134dd446.

---
name: branch-prediction-and-speculation
description: 'Use when explaining branch predictors, mispredict penalties, speculative execution, Spectre or Meltdown mitigations, or branchless code. Not for pipeline stage theory: use cpu-pipelines-and-hazards.'
---

# Branch prediction and speculation

## Contract

| Field | Bound contract |
|---|---|
| Trigger | Branchy hot code underperforms, a `likely` or branchless refactor needs judgment, a kernel mitigation such as retpoline or KPTI needs explaining, or code branches on secret data. |
| Authority | Read-only. The skill runs `perf stat` on a user-named binary, reads sysfs, and answers in chat. Nothing on disk changes, so there is nothing to roll back. No remote mutation. |
| Side effect | Chat output only. `perf stat` writes counters to stdout. |
| Done | The answer names the branch that mispredicts or the speculation path that leaks, gives a measured `branch-misses` count where a binary exists, and states the fix with the condition under which it helps. |

## Inputs

- Code or hot loop (required): the source or disassembly around the branch in question.
- Binary and workload (optional): needed for a measured verdict. Without them the answer is a hypothesis.
- Threat model (optional): whether the question is performance only or also side-channel safety.

## Procedure

1. Explain the mechanism in one pass. The front end predicts a direction for each conditional branch, executes the predicted path, and on resolve either commits or squashes the wrong-path work and refetches from the correct address. The squash cost grows with the distance from fetch to resolve, so a deeper pipeline pays more per mispredict. Backward branches are usually loop closers and predict taken; a data-dependent forward branch with no pattern is the hard case. Done when: the user can say why a given branch is predictable or not.
2. Measure before changing code. Done when: a `branch-misses` count and its ratio to `branches` for the real workload is recorded, or no binary exists and the answer is marked as unmeasured.

```bash
perf stat -e branches,branch-misses ./app
```

Read the ratio against the workload, not against a fixed number: a tight loop over sorted data should show a ratio near zero, while a parser over random input can sit far higher and still be at its floor. Only a ratio that drops after a change proves the change.

3. Pick the remedy for a mispredicting branch. Done when: one remedy is chosen and the condition under which it wins is stated.
   - Sort or partition the data so the branch becomes a run of one direction.
   - Replace the branch with a select. `int m = a < b ? a : b;` may lower to `cmov`. The select executes both operands every time, so it wins only when the branch mispredicts often; on a predictable branch it loses.
   - Split hot and cold paths so the rare path leaves the hot cache line.
   - Peel the loop exit or handle the tail without a branch when the exit mispredicts.
4. Apply compiler hints last. `__builtin_expect(!!(x), 1)` and `__builtin_expect(!!(x), 0)` steer code layout, not the dynamic predictor. On a current out-of-order core the predictor already learns most static patterns, so the hint helps layout of the cold path and little else. Measure after adding one. Done when: the hint is kept only with a measured win.
5. Cover the security side when the branch touches secrets. Speculation past a bounds check can load secret-dependent memory and leave its address in cache state (Spectre variant 1). Meltdown let a user load read a kernel mapping before the fault retired; KPTI separates the page tables. Done when: the applicable mitigation layer is named.
   - Kernel: read the state from `/sys/devices/system/cpu/vulnerabilities/` (one file per issue, such as `spectre_v1`, `spectre_v2`, `meltdown`, `l1tf`). Retpoline, IBRS, IBPB, and STIBP appear in the `spectre_v2` text. A host booted with `mitigations=off` reports `Vulnerable` here.
   - Compiler: Clang's `-mspeculative-load-hardening` masks pointers on the speculative path.
   - Code: constant-time algorithms with no secret-dependent branch or index. This is the only layer that protects a secret from a same-process timing channel.

For the pipeline model behind the penalty, use `cpu-pipelines-and-hazards`. For cache timing channels, use `cpu-cache-opt`. For the kernel mitigation set (KPTI, CET), use `kernel-security`.

## Failure and recovery

| Failure class | Behavior |
|---|---|
| No binary or workload | Deliver the mechanism and the candidate fixes as hypotheses. Mark the answer unmeasured. |
| `perf stat` denied | Report the `perf_event_paranoid` value the tool prints and the capability it needs. Do not change the sysctl. |
| Hint shows no gain | The predictor already handled the branch. Remove the hint and profile for the real bottleneck. |
| Branchless version slower | The branch was predictable and the select now executes both operands. Revert and benchmark on the target CPU. |
| Mitigation regresses throughput | Name the mitigation and its cost. Isolating the secret-handling code is the alternative; do not recommend disabling mitigations. |

## Output

A chat answer that names the mechanism, the measured `branch-misses` figure when a binary exists, one chosen remedy with its winning condition, and the mitigation layer that applies when secrets are involved.