Skip to content

Docker Build for DeepSeek-v4 (Don't Merge)#862

Open
wenxie-amd wants to merge 52 commits into
dev/wenx/deepseek-v4from
dev/tas/deepseek-v4
Open

Docker Build for DeepSeek-v4 (Don't Merge)#862
wenxie-amd wants to merge 52 commits into
dev/wenx/deepseek-v4from
dev/tas/deepseek-v4

Conversation

@wenxie-amd

Copy link
Copy Markdown
Collaborator

Don't merge!

lhzhang333 and others added 30 commits June 22, 2026 18:30
…brate

Capture script: disable check_for_nan_in_loss_and_grad. Its per-microbatch torch.isnan/isinf check in loss_func forces a device->host sync that the profiler bills as a multi-ms stall kernel under the loss/rerun frames; in a single-layer capture this per-step cost was wrongly attributed per layer and multiplied by the layer count.

Capture script: move the profiler window into the steady state. The first ~9 iters are warm-up (Triton/hipBLASLt autotune + compile) with noisy, inflated per-iter times that also desync and inflate the EP comm kernels; from ~iter 10 the iteration time is stable. PROFILE_STEP_START/END and TRAIN_ITERS are now env-overridable (defaults 16/19/22) so the captured ProfilerSteps are uniform (~185ms).

Regenerate site/data/flash.json from freshly captured cr0/cr4/cr128 traces (clean window, no per-step loss-validation stall). MoE cross-cr spread 0.4%; the remaining cr4 attention 'other' is the real per-layer Indexer CatArrayBatchedCopy, which the measured flash-16L anchor confirms is genuine (removing it would push calibFactor to ~1.17).

app.js: calibFactor 0.93 -> 0.91, re-derived against the measured anchor (6665 ms, 636 TFLOP/s/GPU).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Ports the perf-projection benchmark V4 support from dev/john/dsv4
(64407fe).

The in-repo benchmark built a stock GPT model (SelfAttention) instead of
the V4 model, so use_v4_triton_attention / compress_ratios were ignored
and attention timing was unrepresentative. Routes non-gpt model_type to
its builder and makes the harness V4-aware:
- V4 model routing in trainer.py (!= "gpt" instead of == "mamba").
- v4_module_inputs helper for V4 forward signatures (K-stream mHC
[B,S,K,D], keyword-only position_ids/token_ids).
- per-cr representative layer selection (PRIMUS_PROJ_MAX_LAYERS /
PRIMUS_PROJ_COMPRESS_RATIOS) -- one HCA (cr=128) + one CSA (cr=4).
- run_dsv4_projection_1gpu.sh.

This is the in-repo module-benchmark projection
(primus/core/projection/), a separate subsystem from the trace-based
projection site (deepseek-v4/projection/); they share no code and
coexist.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ers (#777)

Ports the DeepSeek-V4 low-precision training work from dev/john/dsv4
onto dev/tas/deepseek-v4. All precision paths default-OFF.

Quantization recipe (paper-aligned, flag-gated):
- FP8 tensorwise training for V4 Pro/Flash + single-GPU launchers.
- FP8 (mxfp8) attention/dense projections via Primus-Turbo
(USE_TURBO_PARALLEL_LINEAR).
- MXFP4 grouped experts via per-group native FP4 (hipBLASLt).
- Per-module recipe: experts MXFP4 / rest FP8 (moe_experts_fp4).
- FP4 CSA-indexer QK + paper-recipe FP8/FP4 attention, real-FP4 indexer
GEMM, MXFP8 expert-weight cache.
- Fused grouped-O into one grouped_gemm_fp8.

Indexer reconciliation (keep-both): preserves FP8-E4M3 indexer QK and
adds FP4 QK as a selectable mode; mutually exclusive (both default-OFF
-> BF16 QK), FP4 takes precedence as a dedicated real-GEMM branch.

Training + launchers:
- Muon mxfp8 fix: auto-enable LR warmup,  plumb NS knobs.
- Default seq_length 4096.
- gfx1250 launcher: opt-in tuned hipBLASLt (stock default), 3-layer
default to fit the 432GB card. gfx1250-only.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DeepSeek-V4 attention kernel work (flydsl for gfx950, and triton
tunning), flydsl default-OFF;.

- FlyDSL attention backend: opt-in alternate kernel suite
(gfx950/MI355X) structured like the sibling _tilelang package. Ships the
13-module import closure (fwd SWA/HCA/CSA + bwd dKV/dQ).
Soft-dependency: imports zero FlyDSL on the default-off path, falls back
to Triton when the runtime is absent. Config flags
use_v4_flydsl_attention / use_v4_flydsl_csa_attention plumbed through
DeepseekV4Attention as use_flydsl=.
- MI355X Triton tuning: three shape-gated, env-overridable launch-param
defaults (bwd dKV NUM_HEAD_GROUPS 1->2; fwd HCA BLOCK_M 64->128; bwd HCA
pool BLOCK_M 32->16). No kernel-logic change; cos-gated.
- Test: set the FlyDSL flags in the _make_bare_attn dispatch-precedence
helper (it builds the attn via __new__, bypassing __init__).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch V4-Pro Muon launcher to V4 Triton attention backend as default
 Fix gfx1250 WMMA tile collapse in V4 CSA fwd + indexer score kernels
 Add fused Triton HyperConnection expand kernel
Enable PRIMUS_SINKHORN_TRITON, PRIMUS_HC_TRITON, PRIMUS_ROPE_TRITON and
PRIMUS_V4_ROUTER_TRITON by default (:-1). These collapse the eager
small-kernel floods (Sinkhorn-Knopp loop, mHC HyperMixer glue, RoPE, MoE
router) into fused Triton kernels.
Co-authored-by: Cursor <cursoragent@cursor.com>
On gfx1250 the mxfp8 Triton quant mishandles all-zero blocks: PrimusGroupedMLP's
quantization_padding inserts zero-padded rows whose all-zero quant blocks
(amax==0 -> ZERO_AMAX_TO_ONE) yield garbage fp8 and inf in the grouped-expert
GEMM, diverging to NaN at iteration 1. grouped_gemm_fp8 handles unpadded tokens,
so gate the padding off on gfx1250 via a cached _gfx1250_skip_expert_pad();
other archs keep the padding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ization

The submodule was pinned to a v0.2.0-era commit while moun.py targets the
>=0.4.0a0 API (tp_mode, keyword-only nesterov, deepseekv4 coefficients); re-pin
to 0.4.0a0 so those resolve natively. Also orthogonalize the consolidated 3D
grouped-expert weight [E,N,K] per expert slice in
TensorParallelMuon.orthogonalize, since the upstream default handles only 2D.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e knob

- install the real primus_turbo wheel + put flydsl on PYTHONPATH/volume so the
  turbo fp8 path runs in the gfx1250 container
- default torch_profiler_use_gzip on and pass it explicitly (perfetto/chrome
  read .gz directly)
- forward TORCH_COMPILE_DISABLE / TORCHINDUCTOR_COMPILE_THREADS to skip the cold
  MoE-permute torch.compile

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#805)

At TP=1 the consolidated [E, N, K] grouped-expert momentum can be
orthogonalized in one batched Newton-Schulz call instead of an E-long
per-expert Python loop: emerging_optimizers>=0.4.0a0 newton_schulz
accepts 3-D input natively (batched_newton_schulz_step via
torch.baddbmm), so one call replaces E per-expert calls -- folding E
rounds of Python op-dispatch and the per-expert GEMM launches into a
single batched launch.

The per-window scale_factor keys on (N, K), which is shared across
experts, so the single scalar applies to the whole batch; the result is
numerically equivalent to the per-slice loop. Added a parity unit test
asserting this.

Gated to the non-partitioned (TP=1) path -- partitioned experts (TP>1)
keep the per-expert loop, since the batched + partition_dim path is not
exercised here. PRIMUS_MUON_BATCHED_NS=0 forces the loop as an escape
hatch.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The compressed-attention branch's _build_compressed_pool ran a burst of many
tiny eager kernels every forward, per compressed layer: it rebuilt the
compress-base RoPE cos/sin table (arange -> outer-product -> cos/sin) and ran
the per-window-softmax pool (add + cast + softmax + cast + mul + reduce) as
separate launches.

Two fusions, both gated default-on with eager fallback and numerically
equivalent to the prior path:

* RoPECache.forward_arange: memoise the cos/sin table for the deterministic
  arange(P) positions (P fixed per run), keyed by (n, device). Bit-identical
  to forward(arange(n)). PRIMUS_COMPRESS_ROPE_CACHE=0 disables.

* compressor_pool.py: a Triton kernel fusing score+ape + softmax(dim=window) +
  weighted-sum into one launch (custom autograd Function: fused fwd, analytic
  eager bwd). PRIMUS_COMPRESS_POOL_TRITON=0 (or non-CUDA / unsupported dtype)
  falls back to eager. Handles HCA (no overlap) and CSA (overlap) windows.

Tests: test_rope_arange_cache.py (cache parity + memoise + bypass, CPU),
test_compressor_pool.py (fused-vs-eager fwd + dkv/dscore/dape parity, GPU-gated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…E), opt-in

PrimusTurboGroupedLinear's fp8 path routes the expert weight gradient through
_WeightGradBridge, which adds it into the consolidated [E,N,K] main_grad with a
separate aten::add_ per micro-batch. With PRIMUS_TURBO_FUSE_GROUPED_WGRAD=1, wrap
the grouped GEMM in primus_turbo's fused_grouped_wgrad so the backward accumulates
each expert's wgrad straight into main_grad (beta=1) and returns grad_b=None; the
bridge backward then just flags grad_added_to_main_grad (skips the add_).

Per-expert _MainGradShim handles point at the contiguous 2-D slices main_grad[i]
of the consolidated block -- what fused_grouped_wgrad / _expert_main_grad_view
expect. Gated default-off: grad_b=None is only safe with no gradient all-reduce /
reduce-scatter (single-GPU TP=1/DP=1/EP=1). Requires a turbo wheel carrying
fused_grouped_wgrad (falls back to the eager bridge add_ otherwise).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ntainer

PrimusTurboGroupedLinear.forward_internal gates the grouped-wgrad fusion on
PRIMUS_TURBO_FUSE_GROUPED_WGRAD, which defaults off. The 1gpu launcher omitted
it from the docker --env forwarding set, so a host-side
PRIMUS_TURBO_FUSE_GROUPED_WGRAD=1 never reached the container: inside, the gate
read the default "0" and the fused path silently fell back to the per-expert
main_grad.add_ bridge (the opt was never actually exercised end to end). Add it
to ENV_ARGS, together with PRIMUS_TURBO_FUSE_WGRAD_DEBUG and the default-on
toggles PRIMUS_MUON_BATCHED_NS / PRIMUS_COMPRESS_ROPE_CACHE /
PRIMUS_COMPRESS_POOL_TRITON, so every opt is controllable from the launch env.

Also add a gated diagnostic (PRIMUS_TURBO_FUSE_WGRAD_DEBUG=1) in forward_internal
that logs the gate decision and whether _expert_main_grad_view accepts the
consolidated [E,N,K] main_grad. Off by default; the functional path is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tention

The V4 attention fwd/bwd Triton kernels' hard-coded BLOCK_M/BLOCK_N/num_warps/
num_stages and dKV head-split defaults are the gfx950 / MI355X R1/R2 sweep winners.
On gfx1250 (MI450, CDNA-next) the optimum differs systematically -- the fwd wants a
larger BLOCK_M with fewer warps/stages, and the MQA/HQ>=64 bwd wants far more dKV
head-split -- so a gfx950-tuned default is well off on gfx1250.

Add `_v4_attn_tuning.py`, which detects the GPU arch (via gcnArchName) and returns
per-arch defaults, and wire it into the fwd launcher (BLOCK_M/BLOCK_N/WARPS/STAGES)
and the bwd launcher (DKV_HEAD_GROUPS). The PRIMUS_V4_ATTN_* env knobs still override
everything, and gfx950 + not-yet-swept shapes keep their historical defaults so
nothing regresses. gfx1250 values come from node-safe microbench sweeps
(ab_sweep/opt7b_hca_bwd_sweep.py, ab_sweep/opt7c_fwd_sweep.py).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A gfx1250 sweep of the HCA (cr=128) fwd shape (ab_sweep/opt7d_hca_fwd_sweep.py, exact
_hca_extra_kv shapes: HK=HQ, k/v=cat([local S, pool P]), pool causal mask) found the same
BLOCK_M=128/BLOCK_N=32/num_warps=4/num_stages=1 config wins there too, vs the gfx950 HCA
winner BM=128/BN=16/W=8/S=1. Drop the SWA-only guard in fwd_attn_defaults so the gfx1250
tuned config applies to both the SWA and HCA fwd shapes. gfx950 defaults + env overrides
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…for V4-Pro MHA)

V4-Pro expands the single-latent KV to all H heads (deepseek_v4_attention.py), so the
attention runs MHA (HK==HQ) at the kernel level: the dKV head-split default is gated on
HQ>HK in the launcher and is therefore never reached, so the gfx1250 HG=32 value does not
apply to this model. A re-sweep at the real MHA shape (ab_sweep/opt7e_mha_bwd_sweep.py)
confirms the gfx950 bwd defaults are already optimal on gfx1250 (every alternative config
regresses) -- there is no bwd retune for V4-Pro. The real gfx1250 attention win is the FWD
(fwd_attn_defaults). Document this in bwd_dkv_head_groups_default; the default is kept as it
is still correct for a genuine MQA config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Launch-count reductions across the DeepSeek-V4 compressed-attention
paths (HCA, CSA, and the CSA indexer), plus launcher infra. All model
changes are **gated default-on with eager fallbacks** and **numerically
equivalent** (end-to-end loss `12.01 -> 8.78` matches baseline to bf16
noise; fp32-exact / bit-identical microbenches).

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Delete run_deepseek_v4_flash_proxy_1gpu.sh and run_deepseek_v4_pro_fp8_paper.sh.

Co-authored-by: Cursor <cursoragent@cursor.com>
lhzhang333 and others added 13 commits June 30, 2026 20:42
Add a Layer-timing mode switch (trace-derived vs manual) to the projection
UI. In manual mode the user types per-cr fwd/bwd times (stored per GPU,
prefilled from the trace baseline) which override the breakdown-derived
per-layer times; all downstream PP/bubble/GA/optimizer/throughput math is
reused unchanged. FLOPs stay analytic so TFLOP/s/GPU remains meaningful.
## Summary

Adds fused single-latent sparse-MLA attention backends for DeepSeek-V4,
reorganizes the attention-kernel package into versioned folders, unifies
backend selection behind two string selectors, and makes **`gluon`** the
default.

- **New backends**
- `gluon` — hand-tuned gfx950 (CDNA4) fused single-latent (K=V)
sparse-MLA.
- `triton_v2` — fused single-latent sparse-MLA in plain Triton (`tl.dot`
/ MFMA).
- Both cover all three V4 layer kinds (dense/SWA `cr=0`, CSA `cr=4`, HCA
`cr=128`) via a kernel-agnostic adapter.
- **Package reorg** — `v4_attention_kernels/` split into versioned
packages (`_eager`, `_triton_common`, `_triton_v0_deprecated`,
`_triton_v1`, `_triton_v2`, `_flydsl_v0_deprecated`, `_flydsl_v1`,
`_gluon_dsa`); every backend entry re-exported from the package
`__init__`.
- **Unified selectors** — replace the per-backend boolean flags with two
strings: `use_v4_attention_backend` (dense/HCA) and
`use_v4_csa_attention_backend` (CSA). **Default = `gluon`** (`triton_v1`
remains a supported opt-in).
- **Docs / scripts / tests** — new kernels `README.md`, run scripts +
example YAMLs migrated to the string selectors, unified single-table
benchmark (`bench_v4_attention.py`), and rewritten dispatch tests.

## Performance

### Attention kernel microbench
MI355X (gfx950), seq=4096, mbs=1, bf16, swa=128, sink on. `fwd ms | bwd
ms` (lower better); TFLOP/s over useful work (head_dim=512).

**V4-Flash (H=64)**

| cr | triton_v1 | gluon | triton_v2 |
|----|-----------|-------|-----------|
| 0 (SWA) | 0.44 / 1.98 | **0.33 / 1.20** | 0.33 / 2.77 |
| 4 (CSA) | 1.35 / 5.15 | **0.96** / 5.27 | 1.13 / 12.35 |
| 128 (HCA) | 0.65 / 2.98 | **0.40 / 1.76** | 0.46 / 4.03 |

**V4-Pro (H=128)**

| cr | triton_v1 | gluon | triton_v2 |
|----|-----------|-------|-----------|
| 0 (SWA) | 0.83 / 3.85 | **0.61 / 1.89** | 0.63 / 7.97 |
| 4 (CSA) | 3.98 / 15.05 | **3.04 / 14.51** | 3.74 / 60.42 |
| 128 (HCA) | 1.26 / 5.77 | **0.77 / 2.69** | 0.88 / 10.74 |

`gluon` is fastest fwd **and** bwd everywhere (~1.3–1.6× fwd, up to ~2×
bwd vs `triton_v1`). `triton_v2` matches gluon on forward but its
backward is untuned. Legacy `flydsl_v0` is 15–260× slower; `flydsl_v1`
forward is still WIP.

### End-to-end proxy (triton_v1 vs gluon)
8-layer V4-Flash proxy, FP8, EP=8 on 8×MI355X, GBS=8, seq=4096,
steady-state:

| backend | ms/iter | TFLOP/s/GPU | tokens/s/GPU | peak HBM/rank |
|---------|--------:|------------:|-------------:|--------------:|
| triton_v1 | 395.3 | 677.9 | 10361 | 229.2 GB |
| **gluon** | **392.7** | **682.4** | 10431 | 230.7 GB |

Both exit cleanly with matching loss trajectory. `gluon` is marginally
faster end-to-end (~0.66%); attention is a small slice of the step here
(dominated by MoE / DeepEP / Sinkhorn).

## Test plan
- [x] Dispatch unit tests `test_v4_p25_dispatch` /
`test_v4_p26_dispatch` pass (8/8).
- [x] EP=8 proxy smoke green for both `triton_v1` and `gluon` (exit 0,
loss parity).
- [x] Unified attention benchmark runs across all backends ×
`cr={0,4,128}` × {flash, pro}.

---

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…wd split (#834)

## Summary

Two commits on the DeepSeek-V4 performance-projection tool
(`deepseek-v4/projection`):

1. **feat: iteration-timeline view** (`3502b68b`) — a new 3-level view
that
   visually decomposes one training iteration's time, bottom-up:
- **Level 1 · layer** — one representative layer per compression ratio
     (`cr=0/4/128`), forward and backward split into `attn / mlp / a2a`.
- **Level 2 · pipeline ranks** — each PP rank's chunks at layer
granularity
(coloured by `cr`, recomputed layers hatched); identical ranks are
deduped
     and the critical stage (max fwd/bwd) is highlighted.
- **Level 3 · schedule** — a 1F1B pipeline-schedule Gantt (interleaved
when
`VPP>1`), styled after the Megatron-2 paper (Fig. 4), with a zoom slider
     and SVG/PNG export.
- Tabbed / stacked layouts with cross-level drill-down linkage, a sticky
Projection-controls sidebar, and manual per-layer overrides extended to
     `embedding / output / loss / MTP`. Methodology in
     `design/07-iteration-timeline.md`.

2. **fix: correct fwd/bwd split in the trace parser** (`1a07b807`) — the
kernel→phase attribution defaulted unlinked kernels to *forward*,
leaking
backward compute (incl. the MoE dgrad/wgrad grouped GEMMs) into the
forward
total and skewing the per-module fwd/bwd ratio. The phase is now
determined
in priority order: (1) `_fwd_`/`_bwd_` tag in the kernel name; (2) for
linked
   kernels, whether the launching CPU op falls inside an autograd
`evaluate_function` interval; (3) for unlinked kernels, whether the GPU
timestamp lies in the reconstructed backward GPU-time window.
Implausible
one-off device stalls billed to compute kernels are dropped. The
flash/pro
breakdown JSON is regenerated with the fixed parser. (A13; see
`design/02`.)

## Changes

- `site/` — `index.html`, `assets/app.js`, `assets/style.css`: timeline
view,
  layouts, linkage, sidebar, manual non-layer overrides.
- `site/data/flash.json`, `site/data/pro.json`: regenerated breakdowns
with the
  corrected fwd/bwd split.
- `tools/parse_trace.py`: fixed-phase kernel→module attribution.
- `design/02-assumptions.md` (A13), `design/04-projection-math.md`,
  `design/07-iteration-timeline.md`: docs.

## Test plan

- [ ] `python3 -m http.server -d deepseek-v4/projection/site 8000`; open
      `?model=pro` and `?model=flash`.
- [ ] Level 1 shows sane `attn/mlp/a2a` fwd/bwd bars; ratios look
correct after
      the parser fix.
- [ ] Level 2 dedups identical PP ranks and highlights the critical
stage.
- [ ] Level 3: default fits with no scrollbar; zoom reveals microbatch
numbers;
      set `VPP>1` to see interleaving; Export SVG/PNG works.
- [ ] Tabbed↔Stacked, cross-level linkage, and manual
per-layer/non-layer
      overrides behave as expected.
- [ ] `parse_trace.py` re-run reproduces the committed JSON.
Run pre-commit over previously unlinted changes: isort/autoflake/black
formatting, trailing-whitespace and end-of-file fixes, and shellcheck
fixes (SC2155 in p19 scripts, SC2148 shebang, SC2086 quoting).

Co-authored-by: Cursor <cursoragent@cursor.com>
# perf(v4-attn): optimize `triton_v2` sparse-MLA forward + backward

Optimizes the plain-Triton fused single-latent sparse-MLA backend
(`triton_v2`)
so it beats the hand-tuned `gluon` backend on **every** V4 attention
shape, for
both forward and backward, while matching eager accuracy. No changes to
any other
backend (`gluon` / `triton` / `flydsl` untouched).

Target GPU: **MI355X (gfx950)**. Shapes: `cr={0,4,128}` × `{flash H=64,
pro H=128}`,
`S=4096`, `mbs=1`, bf16, sink on, `swa=128`.

## What changed


`primus/backends/megatron/core/transformer/v4_attention_kernels/_triton_v2/`
- `dsa_bwd_v4_triton.py`, new `dsa_bwd_kernels.py` (owned bwd compute
kernels)
- `dsa_fwd_v4_triton.py`

**Backward**
- Reuse `dS`/`P` from the dQ kernel instead of recomputing `S/P/dS` in
the dKV kernel.
- Load `q`/`do` untransposed via a strided `[D, BLOCK_H]` index → drop
the
  `.transpose(1,2).contiguous()` HBM copies.
- Skip the provably-zero rope compute (V4 bakes RoPE in-place; the
64-dim rope pad
  is zero and its grad is discarded by the adapter).
- Adaptive dKV tiling for `H<=64`; single-chunk `R_CHUNK` for `H>=128`.
- Fuse `Delta = rowsum(O*dO)` into the dQ kernel (drop the separate
preprocess kernel).

**Forward**
- Skip the provably-zero rope QK term.
- Widen then focus the autotune space (`TILE_K=16` + `num_stages=3` win
→ latency-bound).

## Backward performance trend (geomean bwd TFLOPS / speedup vs gluon)

Reference gluon bwd geomean: **160.7 TFLOPS**.

| Round | Status | Change | Bwd geomean TFLOPS | vs gluon | Shapes won |
|------:|--------|--------|-------------------:|---------:|:----------:|
| 1 | BASELINE | recompute-dKV + external transpose copies | 52.1 |
0.324 | 0/6 |
| 2 | ACCEPTED | store dS/P in dQ, reuse in dKV (no recompute) | 105.5 |
0.658 | 0/6 |
| 3 | ACCEPTED | drop q/do transpose copies (load untransposed) | 154.1
| 0.967 | 1/6 |
| 4 | ROLLBACK | config sweep (BLOCK_H/TILE_K/warps/wpe/stages) | 154.1
| 0.967 | 1/6 |
| 5 | ACCEPTED | skip provably-zero rope compute (V4 zero-pad) | 160.9 |
1.016 | 4/6 |
| 6 | ROLLBACK | num_stages=2 pipelining | 160.9 | 1.016 | 4/6 |
| 7 | ACCEPTED | adaptive dKV tiling (H<=64 & R_CHUNK%128==0) | 168.2 |
1.033 | 6/6 |
| 8 | ROLLBACK | atomic dKV (drop interm+gather+CSR) | 55.6 | 0.345 |
0/6 |
| 9 | ACCEPTED | adaptive R_CHUNK: single-chunk for H>=128 | 172.1 |
1.075 | 6/6 |
| 10 | ACCEPTED | fuse Delta=rowsum(O*dO) into dQ (drop preprocess) |
**175.6** | **1.099** | **6/6** |

Net: **0.32× → 1.10× gluon** (≈3.4× vs the original triton_v2 bwd); `pro
cr4` bwd
reaches 284 TFLOPS (1.34×).

## Forward performance trend (geomean fwd TFLOPS / speedup vs gluon)

Reference gluon fwd geomean: **257 TFLOPS**. (Goal was 1.2×; not reached
— see note.)

| Round | Status | Change | Fwd geomean TFLOPS | vs gluon | Shapes ≥1.2×
|

|------:|--------|--------|-------------------:|---------:|:------------:|
| 1 | BASELINE | plain flash kernel, narrow autotune, computes zero rope
| 230 | 0.896 | 0/6 |
| 2 | ACCEPTED | skip zero-rope QK term (V4 zero-pad) | 243 | 0.945 |
0/6 |
| 3 | ACCEPTED | expand autotune {BH:16/32/64, TK:16/32/64/128,
ns:1/2/3, wpe:0/1/2} | 277 | 1.088 | 0/6 |
| 4 | ACCEPTED | focus autotune {BH:32/64, TK:16/32, ns:2/3/4, wpe:0/1}
(24 cfg, 5× faster tune) | ~276 | ~1.085 | 0/6 |
| 5 | ROLLBACK | add num_warps=8 | ~273 | ~1.074 | 0/6 |

Net: **0.90× → ~1.08× gluon** (triton_v2 fwd went from slower-than-gluon
to beating
gluon on all 6 shapes). The 1.2× target is not achievable in plain
Triton: profiling
shows the fwd is occupancy/register-bound (~13.6% occupancy,
`MemUnitStalled≈0`,
`MfmaUtil≈20%`, `VALUBusy≈33%`) — the `acc[BLOCK_H,512]` fp32
accumulator caps
occupancy. This is the same wall gluon hits, which gluon overcomes only
via its
hand-scheduled async double-buffered LDS pipeline (not expressible in
plain Triton).

## Final benchmark — all backends

MI355X (gfx950), `S=4096`, `mbs=1`, bf16, sink on, `swa=128`, `--warmup
5 --iters 20`.
Cell = `latency ms | TFLOP/s` (useful-work FLOPs, same formula for every
backend).

**Forward**

| variant | cr (layer) | triton | flydsl | gluon | triton_v2 | flydsl_v1
|

|---------|-----------|--------|--------|-------|-----------|-----------|
| flash | 0 (SWA) | 0.47 \| 147.6 | — | 0.35 \| 197.4 | **0.31 \|
221.0** | FAIL |
| flash | 4 (CSA) | 1.40 \| 244.9 | 15.51 \| 22.2 | 1.01 \| 341.8 |
**0.93 \| 369.4** | FAIL |
| flash | 128 (HCA) | 0.68 \| 126.4 | — | 0.42 \| 205.9 | **0.39 \|
219.8** | FAIL |
| pro | 0 (SWA) | 0.84 \| 163.0 | — | 0.63 \| 219.5 | **0.58 \| 237.6**
| FAIL |
| pro | 4 (CSA) | 4.01 \| 308.2 | 54.20 \| 22.8 | 3.05 \| 405.1 | **2.98
\| 414.5** | FAIL |
| pro | 128 (HCA) | 1.28 \| 133.8 | — | 0.77 \| 221.8 | **0.73 \|
236.8** | FAIL |

**Backward**

| variant | cr (layer) | triton | flydsl | gluon | triton_v2 | flydsl_v1
|

|---------|-----------|--------|--------|-------|-----------|-----------|
| flash | 0 (SWA) | 2.04 \| 84.4 | — | 1.21 \| 142.1 | **1.16 \| 148.6**
| FAIL |
| flash | 4 (CSA) | 5.18 \| 165.8 | 2909.43 \| 0.3 | 5.28 \| 162.7 |
**5.13 \| 167.6** | FAIL |
| flash | 128 (HCA) | 3.04 \| 70.6 | — | 1.78 \| 120.9 | **1.72 \|
124.9** | FAIL |
| pro | 0 (SWA) | 3.94 \| 87.3 | — | 1.89 \| 182.1 | **1.79 \| 192.4** |
FAIL |
| pro | 4 (CSA) | 15.02 \| 205.8 | 9413.50 \| 0.3 | 14.55 \| 212.6 |
**10.87 \| 284.5** | FAIL |
| pro | 128 (HCA) | 5.92 \| 72.5 | — | 2.71 \| 158.4 | **2.48 \| 173.4**
| FAIL |

`triton_v2` is fastest on all 6 shapes for both fwd and bwd. `flydsl`
(legacy) is
cr=4-only and orders of magnitude slower. `flydsl_v1` forward is an
upstream WIP
(`NotImplementedError`).

## Correctness

`test_v4_gluon_dsa_attention.py -k triton_v2` → **9/9 passed**
(cr=0/4/128,
dense/HCA/CSA, fwd+bwd vs fp32 eager). All optimizations are
kernel-internal (no
`id(...)`-keyed caches, no data-distribution assumptions) and transfer
1:1 to real
training. The rope-skip relies on the V4 invariant that the rope pad is
structurally
zero (adapter discards its gradient).

## Notes
- FlyDSL benchmarking needs the source at `/workspace/FlyDSL-amd`
(`git clone --depth 1 --branch v0.2.2 https://github.com/ROCm/FlyDSL`) +
`pip install flydsl`.
- Full backend table also added at
`deepseek-v4/benchmark/bench_v4_attention_results.md`.

Co-authored-by: Cursor <cursoragent@cursor.com>
…v2 training LDS/ping-pong fix (#839)

## Summary

Two related pieces of work on the DeepSeek-V4 fused single-latent (K==V)
sparse-MLA attention, on top of `dev/tas/deepseek-v4`:

1. **New `flydsl_v1` backend** — a fully native FlyDSL MFMA
implementation of the sparse-MLA attention (native forward + native dQ
backward + shared Triton dKV), replacing the previous NotImplemented
forward and the `triton_v2`-aliased backward. Depends only on the
installed `flydsl` pip package (gfx950 / CDNA4) and is loaded
**lazily**, so no other backend or build pays for it.
2. **`triton_v2` made training-ready + tuned** — root-caused and fixed
the dKV shared-memory (LDS) overflow that crashed `triton_v2` in
training, disabled the AMD ping-pong / async-copy knobs (a measured
pessimization) for the whole `triton_v2` fwd+bwd, and wired all fused
backends into the training dispatch.

## Root cause — dKV LDS overflow in training but NOT the benchmark

Same kernel, same API, **same shape** (in-process probe confirmed: H=64,
TOPK=128, d_qk=576, R_CHUNK=256, BH=64/TK=128, num_warps=4). The
overflow is **not** a shape or `num_stages` effect — it is a global AMD
Triton knob. `primus_turbo`'s `set_triton_knobs_gfx950()` flips on
`TRITON_HIP_USE_BLOCK_PINGPONG` + `TRITON_HIP_USE_ASYNC_COPY`, which
double-buffer (ping-pong) the LDS operand tiles and ~double the kernel's
shared memory:

| num_stages | standalone (bench ctx) | training ctx |
|---|---|---|
| 2 | fits (≤163840 B) | **347904 B** |
| 3 | 327680 B | **521984 B** |

The standalone benchmark never imports `primus_turbo`, so the knobs stay
off → no overflow. **Closed-loop proof:** setting those two env vars in
the standalone sweep reproduces the training bytes exactly. These knobs
are read at compile time and are **not** part of Triton's compile cache
key (`HIPOptions.hash` omits them), so they can be toggled per-kernel.

## Ping-pong is a pessimization for these V4 kernels

Benchmarked with the knobs forced on vs off (isolated Triton caches).
Ping-pong is uniformly **slower**, never faster:

| op (flash / pro, cr=4) | ping-pong ON | ping-pong OFF |
|---|---|---|
| fwd flash | 1.17 ms | **0.91–0.95** (−19–29%) |
| fwd pro | 3.91 ms | **2.97** (−24%) |
| bwd flash | 5.57–5.59 | **5.40–5.41** |
| bwd pro | 11.55 | **10.88** (~6%) |

**Fix:** `_triton_v2/_amd_knobs.py::amd_pingpong_disabled` — a scoped
context manager that compiles the **entire** `triton_v2` fwd and bwd (dQ
/ dKV-intermediate / gather) with the knobs disabled and restores them
on exit, leaving every other kernel untouched. Because the bwd now
compiles ping-pong-off, the faster **wide 64/128** dKV-intermediate
tiling fits in training too, so it is the default again where it applies
(H≤64, 128-aligned chunk).

## Standalone kernel benchmark (MI355X, seq=4096, bf16, sink on — `ms |
TFLOP/s`)

**Forward**

| variant | cr | triton | gluon | triton_v2 | flydsl_v1 |
|---|---|---|---|---|---|
| flash | 0 | 0.49 \| 139 | 0.33 \| 209 | **0.31 \| 225** | 0.45 \| 154
|
| flash | 4 | 1.45 \| 238 | 0.94 \| 365 | **0.91 \| 377** | 1.35 \| 255
|
| flash | 128 | 0.68 \| 126 | 0.41 \| 210 | **0.38 \| 228** | 0.56 \|
154 |
| pro | 0 | 0.91 \| 152 | 0.62 \| 223 | **0.59 \| 235** | 0.97 \| 142 |
| pro | 4 | 4.02 \| 307 | 3.04 \| 408 | **2.96 \| 417** | 4.01 \| 309 |
| pro | 128 | 1.31 \| 131 | 0.78 \| 221 | **0.74 \| 232** | 1.12 \| 154
|

**Backward**

| variant | cr | triton | gluon | triton_v2 | flydsl_v1 |
|---|---|---|---|---|---|
| flash | 0 | 2.00 \| 86 | 1.20 \| 143 | **1.14 \| 150** | 2.12 \| 81 |
| flash | 4 | 5.15 \| 167 | 5.26 \| 163 | **5.04 \| 170** | 7.59 \| 113
|
| flash | 128 | 2.90 \| 74 | 1.76 \| 122 | **1.73 \| 124** | 2.75 \| 78
|
| pro | 0 | 3.88 \| 89 | 1.90 \| 180 | **1.81 \| 190** | 5.42 \| 63 |
| pro | 4 | 15.01 \| 206 | 15.01 \| 206 | **10.84 \| 285** | 29.48 \|
105 |
| pro | 128 | 5.65 \| 76 | 2.69 \| 159 | **2.50 \| 172** | 6.68 \| 64 |

`flydsl_v1` vs `triton_v2` geomean: **fwd 0.67×, bwd 0.48×** — both
kernels are register-bound; `flydsl_v1` is correct but not yet
competitive.

## Training validation — V4-Flash proxy, FP8, EP=8, seq=4096, GBS=8

Four-way proxy (steady state = mean iters 5–12). `triton_v2` ran with
the new defaults (wide dKV + fwd/bwd ping-pong off) — **0 LDS
overflow**.

| backend | iter (ms) | vs fastest | lm loss @12 | NaN |
|---|---|---|---|---|
| triton_v2 (wide dKV + pp-off) | 391.5 | 1.000× | 10.269 | 0 |
| gluon | 391.5 | 1.000× | 10.269 | 0 |
| triton_v1 (production) | 392.6 | +0.3% | 10.258 | 0 |
| flydsl_v1 | 400.8 | +2.4% | 10.270 | 0 |

`triton_v1` / `triton_v2` / `gluon` are indistinguishable at the
iteration level (attention is a small slice of the MoE-dominated iter);
`flydsl_v1` is ~2.4% slower. All converge (fused backends to identical
loss).

## Config knobs

| env | default | effect |
|---|---|---|
| `USE_V4_ATTENTION_BACKEND` / `USE_V4_CSA_ATTENTION_BACKEND` |
`triton_v1` | `eager` \| `triton_v1` \| `triton_v2` \| `gluon` \|
`flydsl_v1` |
| `PRIMUS_DSA_FWD_PINGPONG_OFF` | `1` | compile triton_v2 fwd with
ping-pong disabled |
| `PRIMUS_DSA_BWD_PINGPONG_OFF` | `1` | compile triton_v2 bwd with
ping-pong disabled |
| `PRIMUS_DSA_DKV_SAFE` | `0` | `1` forces the narrow 32/64 dKV (fits
regardless of the knobs) |

## Test plan

- [x] Standalone benchmark, all 6 shapes × {flash, pro}, all backends
(`bench_v4_attention.py`)
- [x] `flydsl_v1` correctness vs `triton_v2` (fwd SNR ~47.7 dB; bwd dq
~46.7 dB, dkv ~73 dB)
- [x] In-process root-cause proof of the LDS overflow + byte-exact
standalone reproduction
- [x] FP8 proxy training, 4-way (triton_v1 / triton_v2 / gluon /
flydsl_v1): 0 NaN, loss converges, 0 LDS overflow
- [x] pre-commit hooks (isort / autoflake / black / …) pass

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
……) (#844)

## Summary

Fuse the remaining eager elementwise / reduce **"small kernels" in the
DeepSeek-V4 model body** into Triton FWD/BWD kernels. Every fusion is
**default-on, gated by an env knob with an eager fallback**, and
verified for **loss/grad parity** end-to-end on the V4-Flash EP8 FP8
proxy. Optimizer / comm / core-GEMM / core-attention are out of scope.

+ default enable turbo deepep/syncfree/... in yaml 

## What changed

- **Fused RMSNorm** (`_triton_common/rmsnorm.py`) — one FWD + one BWD
kernel with optional weight (+ weight grad), `mid_cast` and `out_dtype`
flags. Wired into the four **non-TE eager RMS sites**: per-head RMS
(`attention._apply_q`), `LocalRMSNorm` (compressor `kv_norm`), and the
`HyperMixer` / `HyperHead` packed-logits RMS. Env:
`PRIMUS_RMSNORM_TRITON`.
- **Fused HyperConnection `collapse`** (`_triton_common/hc_collapse.py`)
— the weighted K-stream reduction `Σ_k pre·x`, symmetric to the existing
`expand` fusion. Env: `PRIMUS_HC_COLLAPSE_TRITON`.
- **V4 router post-logits fusion now supports arbitrary E/K** (masked
`next_pow2` blocks + padded indices), so it actually fires at the
production `moe_router_topk=6` (previously **silently disabled** by a
power-of-2 predicate). Removed `is_triton_kernel_supported`;
`v4_router_post_triton` now just asserts CUDA. `PRIMUS_V4_ROUTER_TRITON`
stays default-on.
- **Fused RoPE-from-positions** (`RoPEFromPositionsFn`) — computes
`cos`/`sin` in-register from `(position_ids, inv_freq)` inside the
rotation kernel, removing the separate `cos`/`sin` generation launches,
the `.to(x.dtype)` cast, and the `cos`/`sin` HBM tensors (recomputed
identically for Q and K before).
- **sparse-MLA adapter** — return `None` for the structurally-zero `dv`
gradient instead of allocating a full `[B,H,Skv,D]` zero tensor (V4
single-latent K=V; the combined `dkv` is routed entirely through `dk`).
Memory optimization; grad-norm identical.
- Removed a per-step `.item()` device→host sync in the hash router
(redundant token-id bounds check that stalled the CPU every step).

## Tests

New: `test_fused_rmsnorm_triton.py` (31),
`test_fused_hc_collapse_triton.py` (44),
`test_fused_rope_from_positions.py`. Updated
`test_p39_router_post_triton.py` with non-power-of-2 E/K coverage incl.
production `E=256, K=6`. Regression suites (compressor-pool / hc-glue /
sinkhorn / block-pp / mtp / gluon-DSA) stay green.

## Performance — proxy train (V4-Flash EP8, FP8/mxfp8, all fusions on)

Cumulative EP8 proxy A/B (steady iters, `PROFILE=off`): **~401.8 →
~368.8 ms/iter (−8.2%)**, loss parity within bf16 noise. Main
contributor is the fused RMSNorm (−28.7 ms); RoPE fusion −3.0 ms;
collapse −1.3 ms; router fusion / `dv=None` are
correctness/enablement/memory wins within noise.

### Backend comparison (proxy train, 12 iters; iters 1–2 are
warmup/JIT-compile)

**Iter time (ms)**

| iter | triton_v1 | triton_v2 | gluon |
|-----:|----------:|----------:|------:|
| 1 (warmup) | 17866.70 | 27027.40 | 25354.90 |
| 2 (warmup) | 9212.00 | 13784.90 | 12949.40 |
| 3 | 548.10 | 538.90 | 540.30 |
| 4 | 372.70 | 370.30 | 370.20 |
| 5 | 376.70 | 370.40 | 367.40 |
| 6 | 373.90 | 364.90 | 367.90 |
| 7 | 373.30 | 368.60 | 369.50 |
| 8 | 376.60 | 366.30 | 370.80 |
| 9 | 371.30 | 369.90 | 369.40 |
| 10 | 373.10 | 367.90 | 369.60 |
| 11 | 371.40 | 370.90 | 367.50 |
| 12 | 370.10 | 368.00 | 368.00 |
| **mean (8–12)** | **372.50** | **368.60** | **369.06** |

**Throughput (TFLOP/s/GPU)**

| iter | triton_v1 | triton_v2 | gluon |
|-----:|----------:|----------:|------:|
| 1 (warmup) | 15.00 | 9.90 | 10.60 |
| 2 (warmup) | 29.10 | 19.40 | 20.70 |
| 3 | 488.90 | 497.30 | 496.00 |
| 4 | 719.10 | 723.80 | 723.90 |
| 5 | 711.30 | 723.60 | 729.40 |
| 6 | 716.80 | 734.50 | 728.40 |
| 7 | 717.90 | 727.00 | 725.40 |
| 8 | 711.60 | 731.60 | 722.70 |
| 9 | 721.70 | 724.50 | 725.50 |
| 10 | 718.30 | 728.50 | 725.00 |
| 11 | 721.60 | 722.40 | 729.20 |
| 12 | 724.00 | 728.30 | 728.10 |
| **mean (8–12)** | **719.44** | **727.06** | **726.10** |

### Notes
- Steady-state results are within the ~1% proxy noise band: **triton_v2
fastest (368.6 ms / 727 TFLOP/s)**, **gluon nearly identical (369.1 ms /
726)**, **triton_v1 slightly slower (372.5 ms / 719)**.
- Warmup (iters 1–2) is dominated by first-time Triton/gluon JIT
compilation; v2/gluon compile more/heavier kernels on the first iter but
this has no effect on steady state.
- Config: proxy V4-Flash EP8, FP8 (mxfp8), dense/CSA/HCA all set to the
corresponding backend, all small-kernel fusions default-on.

## Test plan
- [x] `pytest` new + updated unit tests (rmsnorm 31, hc_collapse 44,
rope, router p39 incl. E=256/K=6) — green
- [x] Regression: compressor-pool / hc-glue / sinkhorn / block-pp / mtp
/ gluon-DSA — green
- [x] End-to-end proxy A/B: loss + grad-norm parity vs eager baseline
- [ ] Reviewer: confirm env knobs default-on posture is desired for
production

(Note: pre-commit `black` reformatted 5 of the files at commit time —
cosmetic only.)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
)

## Summary
Adds **`gluon_v2`**, a 2nd-generation Gluon (gfx950 / CDNA4) fused
single-latent (K==V)
sparse-MLA attention backend for DeepSeek-V4, with **both forward and
backward in Gluon**.
It is the fastest V4 attention backend on every shape (fwd + bwd) and is
wired into both the
training dispatch (`use_v4_attention_backend` /
`use_v4_csa_attention_backend = 'gluon_v2'`)
and the `bench_v4_attention.py` harness (`gluon_v2` backend).

## Optimizations
Forward (`_gluon_v2/dsa_fwd_v4_gluon.py`), ported/derived from the aiter
DSv4 gluon kernel:
- **rope-skip** — V4 bakes RoPE in-place over the 512 latent, so the
rope QK term is provably
  zero; skip the 64-wide rope MFMA + K_rope loads.
- **exp2 softmax** — fold `log2(e)` into the QK scale so the
online-softmax exp is a single
hardware `exp2` (big win at large top-k); LSE converted back to natural
log for the bwd.
- **MFMA K=32** — the QK score matmul reduces `D_V=512`, so
`instr_shape=[16,16,32]` halves
  the MFMA instruction count vs `[16,16,16]`.

Backward (`_gluon_v2/dsa_bwd_dq_gluon.py`,
`dsa_bwd_dkv_interm_gluon.py`, `dsa_bwd_v4_gluon.py`):
- **rope-skip** in both the dQ and dKV-interm kernels (frees the
`dQ_rope`/`dKV_rope` fp32
accumulator VGPR + kills per-tile K_rope loads — the backward is
VGPR-occupancy-bound, so
  this is the dominant lever).
- **MFMA K=32** for the dQ `S = Q@Kᵀ` and `dP = dO@Kᵀ` matmuls (both
reduce `D_V=512`).
- **single-chunk dQ RMW** (`R_CHUNK=min(topk,1536)` for `H>=128`) —
eliminates the redundant
  dQ read-modify-write passes that dominated the large-top-k backward.
- (exp2 was tried in the bwd and rolled back — the single-exp,
occupancy-bound bwd doesn't
  benefit like the online-softmax forward does.)

## Changes
- New backend package `primus/.../v4_attention_kernels/_gluon_v2/` +
adapter
`v4_csa_attention_gluon_v2.py` (`v4_attention_gluon_v2` /
`v4_csa_attention_gluon_v2`).
- `v4_attention_kernels/__init__.py`: lazy
`load_gluon_v2_attention_backends()`.
- `deepseek_v4_attention.py`: `gluon_v2` added to the dense/HCA + CSA
dispatch and the valid
  backend sets (gfx950-gated lazy load, same contract as `gluon`).
- `bench_v4_attention.py`: `gluon_v2` backend (+ `aiter_gluon` fwd-only
reference).
- New eager-alignment UT `tests/.../test_v4_gluon_v2_attention.py`
(dense/HCA/CSA, fwd+bwd) — 9/9.
- `run_deepseek_v4_flash_proxy.sh`: default `PRIMUS_LAUNCHER=direct`
(single-node in-container).

## Attention microbenchmark (MI355X, seq 4096, bf16, sink on; TFLOP/s,
higher = better)
Forward:
| shape | triton_v2 | gluon | **gluon_v2** | aiter_gluon (fwd-only) |
|---|--:|--:|--:|--:|
| flash cr0 | 217.6 | 208.7 | **232.7** | 137.2 |
| flash cr4 | 391.4 | 372.6 | **465.0** | 385.2 |
| flash cr128 | 219.0 | 210.6 | **248.8** | 161.1 |
| pro cr0 | 238.2 | 237.0 | **258.0** | 166.4 |
| pro cr4 | 444.7 | 423.6 | **523.3** | 523.5 |
| pro cr128 | 241.3 | 237.5 | **279.9** | 185.7 |
| **geomean** | 279.5 | 270.4 | **317.0** | 227.2 |

Backward:
| shape | triton_v2 | gluon | **gluon_v2** |
|---|--:|--:|--:|
| flash cr0 | 144.5 | 138.6 | **148.3** |
| flash cr4 | 141.8 | 162.2 | **175.6** |
| flash cr128 | 126.6 | 128.6 | **136.9** |
| pro cr0 | 184.4 | 184.5 | **197.2** |
| pro cr4 | 287.1 | 229.2 | **360.2** |
| pro cr128 | 174.4 | 175.2 | **189.0** |
| **geomean** | 169.7 | 166.7 | **190.5** |

**gluon_v2 vs triton_v2** (previous best): forward **~1.13× geomean**
(up to 1.19× @ flash cr4),
backward **~1.12× geomean** (up to 1.26× @ pro cr4). Fastest backend on
all 6 shapes, both
directions. Full 6-backend table:
`deepseek-v4/benchmark/bench_v4_attention_results.md`.

## Proxy training (8-layer V4-Flash proxy, EP=8 on 8×MI355X, seq 4096,
FP8, GBS=8)
Whole-step, steady state (iters 8–15):
| attn backend | ms/iter | TFLOP/s/GPU | tokens/s/GPU |
|---|--:|--:|--:|
| triton_v2 | 357.8 | 749.1 | 11449 |
| gluon | 360.7 | 742.9 | 11355 |
| **gluon_v2** | **356.5** | **751.7** | **11489** |

gluon_v2 is fastest end-to-end (−0.4% vs triton_v2, −1.2% vs gluon); the
delta is small because
attention is only a fraction of the full MoE+attention step.

## Test plan
- [x] `pytest tests/.../test_v4_gluon_v2_attention.py` — 9/9 (fwd+bwd vs
fp32 eager)
- [x] `bench_v4_attention.py --variant both --cr all` — gluon_v2 fastest
on all 6 shapes
- [x] Proxy training smoke (triton_v2 / gluon / gluon_v2), all exit 0

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
RuibinCheung and others added 2 commits July 8, 2026 04:37
- Bump PRIMUS_TURBO_COMMIT and PRIMUS_TURBO_AITER_COMMIT
- Comment out run-unittest-torch and run-unittest-jax jobs
- Fix pre-commit lint: black (primus_turbo.py) and shellcheck SC2027
  (run_deepseek_v4_pro_muon_1gpu.sh)

Co-authored-by: Cursor <cursoragent@cursor.com>
@wenxie-amd wenxie-amd changed the title Docker Build for DeepSeek-v4 Docker Build for DeepSeek-v4 (Don't Merge) Jul 8, 2026
wenxie-amd and others added 2 commits July 8, 2026 07:37
Compile Triton (pinned to 09500db9) after Primus-Turbo, installing
directly instead of building a wheel first.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Overwrite ci.yaml and both Dockerfiles with main's versions and import
  main's tools/ci/ helper scripts.
- Set PRIMUS_TURBO_COMMIT=231db39...; AITER already 0f3c58e6...
- Triton: build from source (09500db9) with a single direct install
  (no separate wheel build step).
- Comment out run-unittest-torch / run-unittest-jax and the dependent
  coverage-summary job.
- Drop the version/commit consistency lint step: this branch has no
  pyproject.toml and unpinned actions in other workflows, so it cannot pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
@wenxie-amd
wenxie-amd marked this pull request as draft July 8, 2026 14:08
wenxie-amd and others added 5 commits July 8, 2026 22:18
## Summary

- Add the `gluon_v3` DeepSeek-V4 sparse-MLA backend.
- Add CSA formula-pack + aiter Gluon LSE forward routes for H64/H128 CSA
shapes, with the accepted Gluon backward chunking.
- Register `gluon_v3` in the benchmark and add eager-alignment unit
tests.

## Benchmark

Environment: MI355X (`smci355-ccs-aus-n03-33`), `dev_primus_wenx`,
`seq=4096`, `mbs=1`, bf16, sink on, warmup 10, iters 30.

| shape | gluon_v3 fwd | turbo_flydsl fwd | gluon_v3 bwd | turbo_flydsl
bwd |
|---|---:|---:|---:|---:|
| flash cr=0 | 0.30 ms / 230.3 TF | 0.31 ms / 222.9 TF | 1.15 ms / 148.8
TF | 1.40 ms / 122.7 TF |
| flash cr=4 | 0.71 ms / 487.0 TF | 0.78 ms / 439.2 TF | 4.04 ms / 212.7
TF | 4.05 ms / 212.3 TF |
| flash cr=128 | 0.35 ms / 246.9 TF | 0.36 ms / 237.7 TF | 1.57 ms /
137.0 TF | 1.85 ms / 116.1 TF |
| pro cr=0 | 0.54 ms / 255.4 TF | 0.55 ms / 250.1 TF | 1.77 ms / 194.5
TF | 2.16 ms / 158.9 TF |
| pro cr=4 | 2.01 ms / 615.1 TF | 2.09 ms / 592.1 TF | 8.55 ms / 361.8
TF | 9.41 ms / 328.6 TF |
| pro cr=128 | 0.62 ms / 278.5 TF | 0.64 ms / 266.9 TF | 2.27 ms / 189.3
TF | 2.92 ms / 147.3 TF |

Full all-backend benchmark table is updated in:
`deepseek-v4/benchmark/bench_v4_attention_results.md`


`latency ms | TFLOP/s`; **bold** is fastest latency in the row.

| variant | cr | triton | gluon | triton_v2 | gluon_v2 | gluon_v3 |
flydsl_v1 | turbo_flydsl | aiter_gluon |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| flash | 0 | 0.47 \| 146.6 | 0.33 \| 206.9 | 0.33 \| 210.3 | **0.30 \|
230.0** | **0.30 \| 230.3** | 0.46 \| 150.5 | 0.31 \| 222.9 | 0.49 \|
139.2 |
| flash | 4 | 1.49 \| 231.0 | 0.94 \| 366.5 | 0.88 \| 390.7 | 0.75 \|
456.3 | **0.71 \| 487.0** | 1.37 \| 251.2 | 0.78 \| 439.2 | 0.88 \|
389.5 |
| flash | 128 | 0.77 \| 112.2 | 0.41 \| 209.5 | 0.41 \| 211.6 | **0.35
\| 248.0** | **0.35 \| 246.9** | 0.58 \| 147.2 | 0.36 \| 237.7 | 0.53 \|
161.6 |
| pro | 0 | 0.86 \| 160.5 | 0.58 \| 235.5 | 0.60 \| 228.9 | **0.54 \|
253.4** | **0.54 \| 255.4** | 1.06 \| 130.3 | 0.55 \| 250.1 | 0.84 \|
163.9 |
| pro | 4 | 4.48 \| 276.1 | 2.90 \| 425.8 | 2.79 \| 444.0 | 2.37 \|
522.7 | **2.01 \| 615.1** | 4.80 \| 257.9 | 2.09 \| 592.1 | 2.32 \|
532.3 |
| pro | 128 | 1.48 \| 116.4 | 0.74 \| 233.5 | 0.72 \| 239.5 | **0.62 \|
276.4** | **0.62 \| 278.5** | 1.20 \| 143.2 | 0.64 \| 266.9 | 0.91 \|
188.9 |

## Backward

`latency ms | TFLOP/s`; **bold** is fastest latency in the row.

| variant | cr | triton | gluon | triton_v2 | gluon_v2 | gluon_v3 |
flydsl_v1 | turbo_flydsl | aiter_gluon |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| flash | 0 | 2.14 \| 80.2 | 1.22 \| 140.5 | 1.18 \| 146.1 | 1.16 \|
148.2 | **1.15 \| 148.8** | 2.22 \| 77.3 | 1.40 \| 122.7 | — |
| flash | 4 | 5.30 \| 162.0 | 5.26 \| 163.2 | 6.01 \| 142.8 | 4.89 \|
175.8 | **4.04 \| 212.7** | 6.28 \| 136.9 | 4.05 \| 212.3 | — |
| flash | 128 | 2.92 \| 73.5 | 1.66 \| 129.6 | 1.67 \| 128.9 | **1.57 \|
137.1** | **1.57 \| 137.0** | 2.81 \| 76.5 | 1.85 \| 116.1 | — |
| pro | 0 | 4.10 \| 83.8 | 1.87 \| 183.3 | 1.86 \| 184.3 | **1.75 \|
196.8** | 1.77 \| 194.5 | 5.76 \| 59.7 | 2.16 \| 158.9 | — |
| pro | 4 | 15.17 \| 203.9 | 13.46 \| 229.8 | 10.75 \| 287.7 | **8.54 \|
362.3** | 8.55 \| 361.8 | 30.53 \| 101.3 | 9.41 \| 328.6 | — |
| pro | 128 | 5.61 \| 76.6 | 2.43 \| 177.1 | 2.45 \| 175.3 | **2.27 \|
189.3** | **2.27 \| 189.3** | 6.96 \| 61.7 | 2.92 \| 147.3 | — |

## Optimization Notes

### Forward

`gluon_v3` keeps the existing Gluon sparse-MLA path for dense/SWA and
HCA, but adds a specialized CSA forward route for the two production CSA
shapes:

- V4-Flash CSA: `H=64`, `TOPK=640`
- V4-Pro CSA: `H=128`, `TOPK=1152`

The CSA route uses the aiter Gluon MLA kernel with `return_lse=True`, so
it can still feed the existing backward path. The key change is how the
V4 dense top-k layout is converted into the ragged CSR format required
by aiter.

Instead of using Python/torch boolean indexing or caching a prebuilt
ragged index tensor, `gluon_v3` uses a GPU-side closed-form pack. This
is possible because the V4 CSA top-k layout is fixed:

```text
[SWA window 128 entries] + [pool top-k entries]
```

For each token, the number of valid local SWA entries and the compact
output offset can be computed directly from the token index. This
removes the dynamic count + prefix-sum + generic pack overhead and
avoids any benchmark-only tensor-id cache.

This makes the aiter Gluon forward path transfer-safe for real training:
the pack runs every call on the runtime `topk` tensor.

### Backward

Backward remains based on the Gluon sparse-MLA backward path rather than
switching to aiter. The main accepted optimization is the H=64 CSA
chunking policy.

Previously, `flash cr=4` has `TOPK=640` and used `R_CHUNK=256`, which
split backward into 3 chunks. Each chunk repeats dQ work,
dKV-intermediate computation, CSR construction, and gather/reduction.

`gluon_v3` changes the H=64 policy to:

```text
R_CHUNK = min(topk, 320)
```

This reduces `flash cr=4` backward from 3 chunks to 2 chunks while
preserving correctness. For H>=128, the existing whole-topk policy is
kept:

```text
R_CHUNK = min(topk, 1536)
```

### Real-Training Transfer

The accepted changes avoid benchmark-only shortcuts:

- No `id(tensor)` cache.
- No cached ragged CSR keyed by `topk_indices`.
- Dense-to-ragged packing executes on GPU every forward call.
- Backward chunking changes only kernel-side scheduling and repeated
work.

So the measured gains should transfer to real training workloads that
use the same V4 CSA dense top-k contract.

### Performance Impact

Final benchmark on MI355X, `seq=4096`, `mbs=1`, bf16, sink on:

| shape | gluon_v3 fwd | turbo_flydsl fwd | gluon_v3 bwd | turbo_flydsl
bwd |
|---|---:|---:|---:|---:|
| flash cr=0 | 0.30 ms | 0.31 ms | 1.15 ms | 1.40 ms |
| flash cr=4 | 0.71 ms | 0.78 ms | 4.04 ms | 4.05 ms |
| flash cr=128 | 0.35 ms | 0.36 ms | 1.57 ms | 1.85 ms |
| pro cr=0 | 0.54 ms | 0.55 ms | 1.77 ms | 2.16 ms |
| pro cr=4 | 2.01 ms | 2.09 ms | 8.55 ms | 9.41 ms |
| pro cr=128 | 0.62 ms | 0.64 ms | 2.27 ms | 2.92 ms |

`gluon_v3` is faster than `turbo_flydsl` on every measured forward and
backward cell in the benchmark.

## Test Plan

- `pytest -q
tests/unit_tests/megatron/transformer/deepseek_v4/test_v4_gluon_v3_attention.py
-s`
  - Result: `6 passed`
- `python deepseek-v4/benchmark/bench_v4_attention.py`
- Result: full sweep completed successfully; `gluon_v3` beats
`turbo_flydsl` on all measured fwd/bwd cells.
- Pre-commit hooks passed during commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
…n backend ("turbo") (#873)

## Summary

Adds a new DeepSeek-V4 attention backend, **`turbo`**, that calls
straight into the
installed **Primus-Turbo** native-FlyDSL sparse-MLA v2 kernels (the
"turbo API").
Primus owns only a thin V4 adapter binding; the kernels live in
`primus_turbo.flydsl.attention.kernels.sparse_mla_v2`. Selectable via:

```yaml
use_v4_attention_backend: turbo        # dense (cr=0) / HCA (cr=128)
use_v4_csa_attention_backend: turbo    # CSA (cr=4)
```

It is the same fused single-latent (K==V) sparse-MLA-with-sink math as
the in-tree
`gluon_v2` / `triton_v2` / `flydsl_v1` backends, and is the fastest of
them on
gfx950 / MI355X.

## What's added

**Backend**
(`primus/backends/megatron/core/transformer/v4_attention_kernels`)
- `_turbo_flydsl/__init__.py` — re-exports the turbo kernel-pair
(`sparse_mla_{fwd,bwd}_v4_flydsl`) as
`sparse_mla_{fwd,bwd}_v4_turbo_flydsl`.
- `v4_csa_attention_turbo_flydsl.py` — binds them via the
kernel-agnostic V4 adapters
  (`make_attention` / `make_csa_from_pool`) → `v4_attention_turbo` /
  `v4_csa_attention_turbo`.
- `v4_attention_kernels/__init__.py` — `load_turbo_attention_backends()`
lazy loader
(hard-depends on `primus_turbo`'s flydsl attention + the `flydsl` pip
package,
  gfx950 / CDNA4), mirroring the existing `gluon` / `flydsl_v1` loaders.

**Dispatch / config**
- `deepseek_v4_attention.py` — `"turbo"` added to the dense/HCA + CSA
backend
allow-lists, lazily loaded and arch-gated in `__init__`, and dispatched
in both the
  dense/HCA and CSA forward paths.
- `deepseek_v4_transformer_config.py` — documents `"turbo"` in the
backend selectors.

**Compat / infra**
- `extensions/primus_turbo.py` — makes the `is_gfx1250` import defensive
(`try/except ImportError → False`), so a `primus_turbo` that predates
that util
(e.g. the flydsl-attention branch) no longer raises `ImportError` on
non-gfx1250
archs (gfx942 / gfx950); it just takes the normal `main_grad.add_` path.
- `.github/workflows/ci.yaml` — bumps `PRIMUS_TURBO_COMMIT` to `350ec3f`
(`dev/kyle/flydsl_attn_deepseekv4`), which carries the sparse-MLA
attention and its
  cr=4 correctness fixes.

**Bench / test**
- `deepseek-v4/benchmark/bench_v4_attention.py` — registers the
integrated
  `_turbo_flydsl` backend (replacing throwaway agent-workspace entries).
-
`tests/unit_tests/megatron/transformer/deepseek_v4/test_v4_turbo_flydsl_attention.py`
  — fwd+bwd vs the fp32 eager reference for dense / HCA / CSA.

## Validation (MI355X / gfx950, torch 2.10, triton 3.7.0, flydsl 0.2.2)

- **Unit test:** `9 passed` — dense/HCA/CSA × 3 shapes vs fp32 eager
  (`median_rel ≈ 2e-3`, `cos_err ≈ 1e-6`), also cross-checked vs the
  `triton_v2` / `gluon_v2` bf16 anchors.
- **Microbenchmark** (`bench_v4_attention.py`, S=4096): `_turbo_flydsl`
is the fastest
backend on all 6 shapes — **~1.3–1.5× fwd** and **~1.4–2× bwd** vs the
best in-tree
`gluon_v3`. E.g. flash cr=4: `0.53 ms / 652 TF` fwd, `2.54 ms / 338 TF`
bwd
  (vs gluon_v3 `0.65 / 525`, `4.01 / 214`).
- **End-to-end** (`run_deepseek_v4_flash_proxy.sh`, FP8, 8-GPU EP=8,
`compress_ratios=[0,0,4,128,4,128,4,0]`,
`USE_V4_*_ATTENTION_BACKEND=turbo`):
**10/10 iterations, exit 0, 0 NaN iterations**, lm loss 10.83 → 10.48
(healthy),
  ~827 TFLOP/s/GPU, ~324 ms/iter.


All three proxy runs done (0 NaN, all exit 0). Here are both
deliverables.

## 1. All-backend microbenchmark (`bench_v4_attention_results.md`
updated)

MI355X, seq=4096, bf16, `ms | TFLOP/s` (**bold** = fastest).
`_turbo_flydsl` = the integrated turbo backend.

**Forward**
| variant·cr | triton_v2 | gluon_v2 | gluon_v3 | turbo_flydsl |
**_turbo_flydsl** |
|---|---|---|---|---|---|
| flash 0 | 0.30\|230 | 0.28\|248 | 0.28\|248 | 0.30\|229 |
**0.20\|336** |
| flash 4 | 0.87\|397 | 0.73\|469 | 0.66\|524 | 0.72\|477 |
**0.53\|652** |
| flash 128 | 0.38\|224 | 0.33\|264 | 0.33\|263 | 0.35\|246 |
**0.22\|384** |
| pro 0 | 0.58\|236 | 0.51\|269 | 0.51\|269 | 0.55\|252 | **0.38\|358**
|
| pro 4 | 2.78\|444 | 2.36\|525 | 1.92\|645 | 2.09\|591 | **1.41\|878**
|
| pro 128 | 0.72\|239 | 0.61\|281 | 0.61\|281 | 0.63\|271 |
**0.43\|396** |

**Backward**
| variant·cr | triton_v2 | gluon_v2 | gluon_v3 | turbo_flydsl |
**_turbo_flydsl** |
|---|---|---|---|---|---|
| flash 0 | 1.16\|148 | 1.13\|153 | 1.13\|152 | 1.38\|125 |
**0.67\|258** |
| flash 4 | 5.93\|145 | 4.81\|179 | 3.99\|215 | 3.94\|218 |
**2.55\|337** |
| flash 128 | 1.67\|129 | 1.54\|139 | 1.54\|139 | 1.84\|117 |
**0.78\|275** |
| pro 0 | 1.81\|190 | 1.71\|201 | 1.70\|202 | 2.09\|165 | **1.29\|267**
|
| pro 4 | 10.74\|288 | 8.52\|363 | 8.52\|363 | 9.41\|329 | **6.32\|489**
|
| pro 128 | 2.47\|174 | 2.27\|189 | 2.27\|189 | 2.91\|148 |
**1.49\|288** |

`_turbo_flydsl` is fastest on all 12 cells (~1.36× fwd, ~1.57× bwd vs
gluon_v3). Full 9-backend table + speedups are in
`bench_v4_attention_results.md`.

## 2. Proxy training per-iter comparison (FP8, 8-GPU EP=8,
`cr=[0,0,4,128,4,128,4,0]`)
`gluon_v3` is now wired as a training backend and the proxy ran clean
(exit 0, **0 NaN**). Here's the 3-way per-iter comparison you originally
asked for (triton_v2 / gluon_v3 / turbo), same proxy config (FP8, 8-GPU
EP=8, `cr=[0,0,4,128,4,128,4,0]`, 10 iters). Whole-model `elapsed ms |
TFLOP/s/GPU`:

| iter | triton_v2 | gluon_v3 | **turbo** |
|---:|---|---|---|
| 1 † | 67284.2 \| 4.0 | 51034.8 \| 5.3 | 25917.5 \| 10.3 |
| 2 † | 33898.1 \| 7.9 | 25768.4 \| 10.4 | 13208.7 \| 20.3 |
| 3 | 517.3 \| 518.1 | 509.0 \| 526.5 | 495.4 \| 540.9 |
| 4 | 342.1 \| 783.3 | 329.6 \| 813.0 | **324.5 \| 826.0** |
| 5 | 338.9 \| 790.7 | 332.9 \| 804.9 | **321.5 \| 833.4** |
| 6 | 339.8 \| 788.7 | 345.7 \| 775.3 | **327.2 \| 819.1** |
| 7 | 344.1 \| 778.8 | 329.4 \| 813.4 | **322.7 \| 830.5** |
| 8 | 338.9 \| 790.8 | 331.7 \| 808.0 | **324.1 \| 826.8** |
| 9 | 336.2 \| 797.0 | 328.9 \| 814.7 | **322.8 \| 830.3** |
| 10 | 338.7 \| 791.3 | 334.0 \| 802.4 | **324.8 \| 825.2** |
| **steady mean (4–10)** | **339.8 \| 788.7** | **333.2 \| 804.5** |
**323.9 \| 827.3** |

† iters 1–2 are first-call kernel compile (turbo compiles fastest:
~25.9s vs ~51s gluon_v3 / ~67s triton_v2).

**Takeaways**
- **turbo is fastest end-to-end**: steady **323.9 ms / 827 TFLOP/s/GPU**
— **~4.7% faster iter than triton_v2** (339.8 ms) and **~2.8% faster
than gluon_v3** (333.2 ms).
- **gluon_v3 ≈ gluon_v2** end-to-end (333.2 vs 333.9 ms): its microbench
edge is mostly the cr=4 *forward*, which is a small slice of the FP8
step (MoE dominates), so it barely moves the whole-iter number.
- All three: **0 NaN iterations**, healthy loss, exit 0.
- End-to-end deltas are modest because attention is a fraction of the
step; turbo is consistently ahead on every steady iter and biggest on
the isolated attention microbenchmark (~1.36× fwd / ~1.57× bwd vs
gluon_v3).


## Notes / limitations

- Requires an installed `primus_turbo` that carries
`primus_turbo.flydsl.attention`
(i.e. the `dev/kyle/flydsl_attn_deepseekv4` line) and the `flydsl` pip
package;
  otherwise the lazy loader raises a clear, actionable `ImportError`.
- The flydsl dense/HCA **banded** path is single-sequence, so
multi-batch
(`mbs > 1`) dense/HCA is not yet supported by this backend (cr=4 / CSA
handles
`mbs > 1` fine); the proxy and production run `mbs = 1`. The unit test
pins `B=1`
  and documents this.

## Test plan

- [x] `pytest tests/.../test_v4_turbo_flydsl_attention.py` (gfx950) —
9/9 pass.
- [x] `python deepseek-v4/benchmark/bench_v4_attention.py` —
`_turbo_flydsl`
      registers and is fastest across the sweep.
- [x] `USE_V4_ATTENTION_BACKEND=turbo USE_V4_CSA_ATTENTION_BACKEND=turbo
./run_deepseek_v4_flash_proxy.sh`
      — trains cleanly (0 NaN).

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@wenxie-amd
wenxie-amd marked this pull request as ready for review July 14, 2026 14:03
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.

4 participants