(§3.4 draws on Wilson Fung’s Ph.D. dissertation.) The baseline SIMT stack is correct for nearly all GPU code — and the research community has spent a decade attacking its four weak spots:
- Lower
— serialized paths idle lanes;SIMD efficiency SIMD efficiencyFraction of SIMD lanes doing useful work; divergence's main casualty.Glossary → - Needless serialization — interleaving divergent paths is functionally fine (threads only communicate explicitly), the stack just forbids it;
- Inadequate MIMD abstraction — implicit reconvergence sync collides with locks (SIMT deadlock) and with exceptions/ interrupts;
- Area — 32×64 bits per warp (or 6×64 optimized) scaling with every in-flight warp, mostly unused when divergence is rare.
The proposals fall into four families —
3.4.1 Warp Compaction
The observation: a core runs hundreds to thousands of threads from the same kernel, so when one warp diverges at a data-dependent branch, its neighbors probably diverge at the same branch — each branch target is executed by plenty of threads, just scattered across static warps (the hardware’s arbitrary, program-invisible grouping at launch). Why not regroup?
| A·W0 | A·W1 | B·W0 | C·W0 | B·W1 | C·W1 | D·W0 | D·W1 | |
|---|---|---|---|---|---|---|---|---|
| t0 | ||||||||
| t1 | ||||||||
| t2 | ||||||||
| t3 | ||||||||
| t4 | ||||||||
| t5 | ||||||||
| t6 | ||||||||
| t7 |
time →
W0 executes block A at full width.
| A·W0 | A·W1 | B·DW0 | C·DW1 | D·W0 | D·W1 | |
|---|---|---|---|---|---|---|
| t0 | ||||||
| t1 | ||||||
| t2 | ||||||
| t3 | ||||||
| t4 | ||||||
| t5 | ||||||
| t6 | ||||||
| t7 |
time →
W0 executes A (nothing to compact while control flow is coherent).
A significant slice of MIMD’s benefit on SIMD hardware — with strings attached. DWF needs warps to reach the branch within a short window (scheduler-sensitive); a greedy scheduler can starve threads; regrouping threads scrambles memory coalescing and shared-memory bank behavior; and code relying on implicit intra-warp synchronization simply breaks. The follow-up insight: split the kernel into divergent and coherent regions, compacting in the former and restoring static warps in the latter — which matured into:
(Fung & Aamodt): continuous regrouping is pointless — compact only at divergent branches and reconvergence points, tracked by a block-wide SIMT stack. Synchronizing a whole thread block at each divergent branch costs TLP, so TBC’s compromise is scope (one block, while other resident blocks keep the core busy). Robust where DWF was fragile.Thread Block Compaction thread block compaction (TBC)Block-wide SIMT stack: compaction only at divergence/reconvergence, warps of other blocks keep the core busy.Glossary →- Large Warp Microarchitecture (Narasiman et al.): gang warps into large lockstep groups and compact every instruction (even predicated ones) — more compaction, less TLP, plus a thread-granularity scoreboard letting some warps run slightly ahead.
- CAPRI (Rhu & Erez): TBC + a compaction-adequacy predictor — a simple history-based predictor (think single-level branch predictor) that skips the stall-and-compact dance at branches where compaction wouldn’t pay.
- Cycle compression (Vaidya et al.): when the SIMD datapath is narrower than the warp (e.g. 16-wide hardware, 32-wide warps → 2 cycles/warp), skip fully-masked sub-groups; the swizzled variant re-arranges lanes to manufacture skippable sub-groups.
- SBI/SWI (Brunie et al.): widen the warp, issue two different instructions per cycle, and fill divergence gaps with the sibling path (SBI) or another diverged warp (SWI).
The lane constraint. Compaction has a physical limit: a thread’s registers live in its lane’s register-file slice, so a dynamic warp can only merge threads whose lanes don’t collide (our animation chose masks that interleave perfectly — reality is less kind). Program structure biases paths to particular lanes; Rhu & Erez’s SIMD lane permutation picks thread→lane mappings that break the bias and substantially improve compaction rates. Alternatively, a more flexible register file (more, narrower banks — Fung et al.) lets compaction ignore lanes; and dynamic micro-kernels (Steffen & Zambreno) migrate thread state outright through the scratchpad for ray tracing loops.
Software compaction gets the same effect without new hardware, paying in memory traffic: conditional streams for stream processors (Kapasi et al.), prefix-sum stream compaction on the scratchpad (Billeter et al.), deferred shading grouping rays by material (Hoberock et al.), CPU-side runtime remapping (Zhang et al.), and CCC (Khorasani et al.) — a compiler transform where each warp parks divergent tasks on a shared-memory stack and refills idle threads from it.
| DWF | Regroup same-PC threads into dynamic warps at divergence | Timing/scheduler-sensitive; hurts coalescing; breaks implicit warp sync |
| TBC | Block-wide SIMT stack; compact only at divergence/reconvergence | Block-level sync eats TLP (other blocks compensate) |
| Large Warp μarch | Lockstep warp-gangs; compact every instruction | Least TLP; needs per-thread scoreboard |
| CAPRI | Predict when compaction pays; skip the rest | Predictor state (small) |
| Cycle compression | Skip fully-masked sub-groups on narrow datapaths | Needs contiguity (or swizzle) to find skippable groups |
| SBI / SWI | Co-issue two instructions to fill divergence gaps | Doubled front-end complexity (offset by wider warps) |
| Software (streams, CCC…) | Regroup threads/tasks in software | Extra memory traffic to move thread data |
Check yourself
1. The baseline SIMT stack has four deficiencies motivating §3.4's research. Which list is right?
2. Predict the issue count (use the two animations): two 4-thread warps each split 2/2 at a branch. Baseline vs. ideal DWF for the divergent region?
3. Why can't DWF freely place a thread in any lane of a dynamic warp?
4. Thread Block Compaction's core insight over DWF?
5. What does CAPRI add to TBC?