Skip to content

feat(deepseek-v4): cudnn_flashmla sparse attention backend (FlashMLA forward, cuDNN backward) - #4078

Merged
samsja merged 23 commits into
mainfrom
feat/ds-v4-cudnn-flashmla
Oct 14, 2026
Merged

samsja merged 23 commits into
mainfrom
feat/ds-v4-cudnn-flashmla

Conversation

@garrett361

@garrett361 garrett361 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Adds a cudnn_flashmla option to model.dsa_backend for DeepSeek V4 that runs the same FlashMLA sparse prefill forward vLLM uses for DeepSeek V4 prefill, so the trainer and inference compute attention with the same kernel. The backward is the cuDNN frontend's DSA kernel; it runs on SM90, SM100 and SM103, and tilelang stays the default.

  • For DeepSeek V4, whose slot width changes with sequence length and packing, cudnn_dsa_patch stops cuDNN
    recompiling its backward for each new width. GLM-5's backward uses one fixed width and is unaffected.
  • On B300 (SM103), both DSv4 test files pass and the kernels match a float32 reference; no end-to-end run there yet.
  • On H200 at the B300 changes (6a1cae560), both DSv4 test files pass again, and a 3-step end-to-end run (cp 8,
    ep 8) compiles 3 cuDNN kernels per rank in step 1 and matches the earlier losses, step time and peak memory.
  • Fixes GLM-5's SM100 cudnn_flashmla backward, which omitted cuDNN's required workspace and always raised.
  • tilelang stays the shared dsa_backend default and configs/advanced/deepseek-v4-flash/sft.toml opts in to
    cudnn_flashmla, following perf(dsa): cuDNN + FlashMLA kernels for GLM-5 sparse attention (model.dsa_backend) #3829, which did the same for the GLM-5.3 configs. FlashMLA only ships an x86_64 wheel
    in an optional extra, so a cudnn_flashmla default would break aarch64 and installs without the extra. We can flip
    the default instead if reviewers prefer.

DeepSeek-V4-Flash first 6 layers, real weights, cp 8, ep 8, one 8xH200 node. Sparse attention GPU time per step, rank 0 profiler trace at 8k tokens/GPU:

kernels tilelang cudnn_flashmla speedup
forward, incl. recompute (48 calls) 56.3 ms 26.5 ms 2.12x
backward (24 calls) 71.3 ms 36.7 ms 1.94x
all sparse attention 136.0 ms 76.7 ms 1.77x

Sparse attention is under 5% of the step here, so the end-to-end gain is smaller. Median steady-state step time:

tokens/GPU tilelang cudnn_flashmla speedup max loss diff vs tilelang max loss diff, two tilelang runs
8k 2.86 s 2.80 s 1.02x 1.0e-3 1.7e-3
16k 4.59 s 4.36 s 1.05x 7.3e-4 1.4e-3

Peak memory is equal, and each rank compiles 3 cuDNN kernels, all in step 1. Baseline main; results and config at 524ec668e.

🤖 Generated with Claude Code

garrett361 and others added 21 commits October 13, 2026 20:29
…d table

Add a backend argument to prime_rl::dsv4_sparse_attn and its backward,
dispatched through FORWARD_BACKENDS / BACKWARD_BACKENDS, with tilelang as
the only entry. DeepseekV4Attention reads dsa_backend from the model
config and rejects backends DSv4 does not implement. Kernel tests are
parametrized over DSV4_BACKENDS. No behavior change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
FlashMLA's sparse prefill runs the forward and the cuDNN frontend's SM90
DSA backward runs the backward, behind model.dsa_backend = "cudnn_flashmla".
Slot widths round up to FlashMLA's 128-slot tile. The public LSE stays
TileLang's (base 2, sink included), converted from FlashMLA's natural-log,
sink-excluded one. The backward receives the sink-inclusive natural-log
LSE with no attn_sink, so the sink gradient stays in torch, and its delta
comes from TileLang's fused preprocess kernel. The backend runs on SM90
only.

The cuDNN backward still compiles one main kernel per slot width.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… width

The cuDNN frontend keys its SM90 DSA backward main kernel on the slot width,
so each new HCA width compiled a new kernel (about 2.5 s) mid-training. The
kernel only uses max_topk to clamp topk_length, so `cudnn_dsa_compile`
builds it with max_topk at a clamp derived from the config (the next power
of two at or above the widest slot axis, 16384 for DeepSeek-V4-Flash) and
keys it on that clamp, in an in-memory cache. Calls without topk_length
(GLM) keep the frontend's behavior. Each compile of the preprocess, main
and postprocess kernels is logged at INFO with its duration.

The widest slot axis reaches the ops as `max_slots`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
compile_counts.py counts the cuDNN and TileLang kernels the sparse
attention ops compile in one process over corpus items, wide slot widths,
or two clamps. launch.sh runs the end-to-end arms serially on one held
node from frozen worktrees with fresh caches, and compare.py reports speed,
parity, and per-rank, per-step compile counts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ward op

The forward op already asserts it; the backward op is public too and would
otherwise fail with a TypeError deep in the slot-width rounding.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d logs/latest

Ranks sharing a Triton cache on NFS raced on its files and crashed the first
smoke run, and torchrun logs live under logs/latest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ults

Kernel-level compile counts, the six-arm end-to-end comparison, and rank 0
kernel breakdowns from job 3935 on 2026-10-13.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
They stay reachable at ef2ab83.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ontext A/Bs

Adds 128k- and 1M-token configs (16k and 128k tokens per GPU at cp 8) and
CONFIG / RUN_TAG knobs in launch.sh.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Job 4008 on 2026-10-13: 16k tokens per GPU, and the 1M-sequence attempt that
runs out of memory in the shared FP8 indexer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ckend

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Admit compute capabilities (10, 0) and (10, 3) for cudnn_flashmla. On
Blackwell the backward calls the cuDNN SM100 DSA kernel with the same
sink-inclusive natural-log LSE as SM90, a -inf attn_sink and a caller
workspace, and keeps dsink in torch.

cudnn_dsa_compile gains an SM100 path that builds the generic_m64 main
kernel with max_topk at the power-of-two clamp of max_slots and keys it
on the clamp, so one compile serves every slot width. It applies only to
calls with topk_length, refuses widths that are not a multiple of 64,
and leaves the tuned H128 two-CTA kernels and GLM's calls on stock
behavior. The SM90 path is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GLM-5's cudnn_flashmla backward on SM100/SM103 called
flash_attn_bwd_sm100 without a workspace, which the frontend requires,
so every Blackwell backward raised ValueError. Allocate it from
flash_attn_bwd_sm100_workspace_size.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d B300 handoff

The harness, H200 results and B300 handoff stay reachable at a4e159cee.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…opk clamp

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… prevents

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…as patches

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
samsja
samsja previously approved these changes Oct 14, 2026
@samsja
samsja marked this pull request as ready for review October 14, 2026 01:20
@samsja
samsja marked this pull request as draft October 14, 2026 01:20
…hmla backend

The shared dsa_backend default stays tilelang, as for GLM-5.3, whose
example configs opt in the same way.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@garrett361
garrett361 marked this pull request as ready for review October 14, 2026 16:25
@samsja
samsja merged commit f9f4bf0 into main Oct 14, 2026
23 of 25 checks passed
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.

2 participants