Skip to content

perf(cuda-graphs): capture-clean decoder + CUDA-graph AR forward (stacked on #3)#4

Open
trvachov wants to merge 1 commit into
trvachov/batched-ar-decode-testedfrom
trvachov/cuda-graphs-tested-stacked
Open

perf(cuda-graphs): capture-clean decoder + CUDA-graph AR forward (stacked on #3)#4
trvachov wants to merge 1 commit into
trvachov/batched-ar-decode-testedfrom
trvachov/cuda-graphs-tested-stacked

Conversation

@trvachov

Copy link
Copy Markdown

Summary

Adds CUDA-graph acceleration for the AR decoder forward (~2.25× on the forward), plus the capture-clean decoder change that makes it possible.

  • Capture-clean decoder (decoder.py): pass tgt_is_causal/memory_is_causal explicitly so nn.TransformerDecoder skips _detect_is_causal_mask — a per-forward host sync (bool((mask==...).all())) that both added latency and made the forward uncapturable by CUDA graphs. Numerically identical.
  • Per-shape CUDA-graph cache (sampler.py, opt-in REASYN_CUDAGRAPH): captures one graph per bucketed (batch, seq) shape and replays it; static token/gather buffers updated per call. 2.25× on the AR forward, output bit-identical to eager.

⚠️ Stacked on #3 — merge #3 first

This PR is based on trvachov/batched-ar-decode-tested (PR #3), not reasyn_v2, because CUDA graphs are built on the batched/bucketed AR forward. Its diff shows only the decoder + CUDA-graph changes. Merge #3 first, then this. (Independent of #2.)

Correctness / safety (from adversarial review + fixes)

  • Eager/graph parity: both paths use the same tensor layout, so REASYN_CUDAGRAPH=0 vs 1 is numerically equivalent (verified over many varied token/gather inputs under identical RNG).
  • Multi-GPU safe: all stream/warmup/capture/replay bound to self.device; workers call set_device(cuda:<gpu_id>).
  • Memory bounded: 3-entry LRU of independent graph pools with reservation release (incl. on exception); graph mode requires num_workers_per_gpu==1 + batched+bucket+bf16 + a CUDA model device.
  • Exception-safe GPU-lock release (context manager); explicit .to(float32, copy=True) output.

Tests

tests/test_cuda_graphs.py — env-off/env-on parity, identical-RNG sampling, device/guard/lock checks, bounded memory. 19 passed on A100 (this suite + the inherited batched-AR suite).

Toggle

REASYN_CUDAGRAPH (opt-in, default off; requires batching+bucketing+bf16 + CUDA).

Testing environment ⚠️

Results were produced on Python 3.12 / PyTorch 2.11 / CUDA 13.2 — not the repo-pinned Python 3.10 / PyTorch 2.7 / CUDA 11.8. CUDA-graph capture and SDPA kernel selection are especially version-sensitive; re-confirm on the pinned stack / CI before merge.

Independently reviewed, fixed, and re-verified (rebased onto the fixed batched-AR; both test suites green) before opening.

Add a capture-clean decoder with explicit is_causal and a per-shape CUDA-graph cache for batched AR forwards. Preserve eager/graph layout parity, bind capture and workers to the selected device, bound independent graph pools with an LRU and one-worker enforcement, make GPU lock release exception-safe, guard unsupported modes, and copy public logits to owned FP32 storage.

Add checkpoint-backed CUDA-graph parity, device, guard, lock, ownership, and memory regression tests.

Stacked on trvachov/batched-ar-decode-tested; this commit depends on that batched-AR change and must merge after it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant