Skip to content

[CuteDSL] FLCE: 2-SM online-softmax fast path (Blackwell), ~5x over Triton#1291

Open
justinhh4 wants to merge 1 commit into
linkedin:mainfrom
justinhh4:justinhh4/cutedsl-flce-2sm
Open

[CuteDSL] FLCE: 2-SM online-softmax fast path (Blackwell), ~5x over Triton#1291
justinhh4 wants to merge 1 commit into
linkedin:mainfrom
justinhh4:justinhh4/cutedsl-flce-2sm

Conversation

@justinhh4

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #1279 (the CuTe DSL cross-entropy PR). This adds the CuTe DSL fused-linear-cross-entropy (FLCE) — forward + backward, signature-compatible with the Triton LigerFusedLinearCrossEntropyFunction, selected via LIGER_KERNEL_IMPL=cutedsl.

Unlike a straightforward port, this kernel is built on quack's 2-SM (CtaGroup.TWO) tcgen05 GEMM for Blackwell and a store-free online-softmax forward: the (BT, V) logits are never materialized (not even per chunk). The forward computes only the per-row LSE + the target logit in a fused GEMM epilogue; the backward recomputes the masked softmax on the fly, tiled over the vocabulary so the grad_weight GEMM contracts over K = BT and writes each slice once. The result is both faster and leaner than the Triton FLCE on B200 — up to ~5× faster at LLM vocab sizes and ~2× less peak memory.

Scope: this kernel is specifically optimized for NVIDIA B-series (Blackwell, sm_100). fp32, non-Blackwell GPUs, softcap, and the argmax metrics (token-accuracy / predicted-tokens) transparently delegate to the upstream Triton FLCE (the 2-SM GEMM is 16-bit-input only), so those paths are byte-identical to — and exactly as fast/lean as — Triton. All benchmarks below are B200.

Feature parity with the Triton FLCE: ce_weight, bias, softcap, label_smoothing, z-loss (lse_square_scale), ignore_index, token-accuracy, predicted-tokens, use_token_scaling, accum_dtype, reductions mean/sum, plus forward-only reduction='none'. reduction='none' with grad is refused by design (the fused pass accumulates grad_weight over tokens and cannot honor a per-token upstream grad — matching the documented FLCE contract, and the Triton FLCE's own limitation).

Files

  • src/liger_kernel/ops/cutedsl/ops/fused_linear_cross_entropy.py — the FLCE Function + fwd/bwd (2-SM online-softmax fast path + Triton-delegated fallback).
  • src/liger_kernel/ops/cutedsl/ops/__init__.py — export the FLCE symbols so LIGER_KERNEL_IMPL=cutedsl rewires the public FLCE too.
  • src/liger_kernel/ops/cutedsl/ops/utils.pyinfer_device_arch (the Blackwell fast-path gate).
  • test/transformers/test_cutedsl_fused_linear_cross_entropy.py — parity suite (below).
  • setup.py — add quack-kernels~=0.5.0 to the cutedsl optional-deps group.

Correctness

test/transformers/test_cutedsl_fused_linear_cross_entropy.py277 passed (LIGER_KERNEL_IMPL=cutedsl pytest ...). Full parity vs the Triton FLCE (and vs torch where applicable) across dtype × reduction × every feature × grad, plus: AMP/autocast, not-last-layer scalar grad, non-contiguous inputs, the functional API + CrossEntropyOutput structured output, LIGER_KERNEL_IMPL=cutedsl dispatch wiring, production vocab (V=32000, the partial-tile tail-predication path), and the host-side validation / by-design-refusal contracts.

Stacked on #1283. The AMP + bias=False parity tests exercise a Triton FLCE addmm-under-autocast path fixed by #1283 (mat1 and mat2 must have the same dtype). This PR does not modify the Triton FLCE — that fix is #1283's. Merge #1283 first (or merge this after it); the full suite is green on a tree that includes #1283.

Benchmarks — CuTe DSL vs Triton FLCE (NVIDIA B200)

Interleaved A/B, median p50 of the full (fwd+bwd) mode, grad_output=1.0, H=4096, bf16. speedup = Triton ÷ CuteDSL (>1.00 → CuteDSL faster). Peak memory is max_memory_allocated for the same full pass. Reference config: BT=8192, V=128256.

bf16 — full (fwd+bwd), vocab sweep (BT=8192, H=4096)

vocab cutedsl (ms) triton (ms) speedup cutedsl (MB) triton (MB) mem ratio
32000 7.17 11.11 1.55× 1153 1907 1.65×
102400 20.38 55.88 2.74× 3159 5407 1.71×
128256 26.56 123.83 4.66× 3714 6638 1.79×
152064 30.08 147.16 4.89× 4350 7821 1.80×
201088 40.06 194.09 4.85× 5555 10260 1.85×
262144 52.45 485.09 9.25× 7047 13221 1.88×

bf16 — full (fwd+bwd), BT sweep (V=128256, H=4096)

BT cutedsl (ms) triton (ms) speedup cutedsl (MB) triton (MB) mem ratio
1024 4.50 117.49 26.11× 3282 6405 1.95×
2048 7.41 118.17 15.94× 3343 6438 1.93×
4096 13.75 119.97 8.72× 3467 6505 1.88×
8192 26.43 123.87 4.69× 3714 6638 1.79×
16384 50.41 138.22 2.74× 4210 6905 1.64×
32768 103.51 171.64 1.66× 4551 7440 1.63×
65536 194.87 240.84 1.24× 5884 8509 1.45×

Takeaways. CuteDSL FLCE is faster at every shape and ~1.5–1.9× leaner in peak memory (the store-free online-softmax forward never materializes chunk×V logits, unlike the Triton chunked path). The speedup is largest at the memory-bound regime (large V, small–moderate BT) where Triton's chunked cuBLAS path is dominated by the logit materialization + separate softmax passes; at very large BT both approach the GEMM roofline and the lead narrows to ~1.2×.

fp32 (delegates to Triton) — full, vocab sweep (BT=8192, H=4096)

fp32 has no Blackwell tensor-core GEMM path (the 2-SM tcgen05 GEMM is 16-bit-input only), so it delegates to the Triton FLCE and is tied on speed and memory by construction:

vocab cutedsl (ms) triton (ms) speedup cutedsl (MB) triton (MB)
32000 107.2 107.0 0.998× 2668 2665
128256 480.6 480.0 0.999× 8976 8974
262144 1124.5 1123.1 0.999× 17750 17750

Comparison with other memory-efficient linear-CE kernels (NVIDIA B200, bf16)

For context, here is the CuteDSL FLCE swept against the two well-known store-free / chunked peer libraries: Apple Cut-Cross-Entropy (cut-cross-entropy 25.9.3, default preset = gradient filtering + bf16 grad accum) and quack (Dao-AILab chunked_linear_cross_entropy 0.5.0, chunk_size=4096). (The Triton FLCE baseline is in the tables above.) All bf16, full (fwd+bwd) p50 latency (ms) + peak max_memory_allocated (MB). The (N×) next to each peer's ms and MB is its value ÷ cutedsl — for both, >1× means cutedsl is better (faster / leaner) and <1× means the peer is. By design class: cutedsl and CCE are store-free (no chunk×V logits); quack is chunked-store.

vocab sweep (BT=8192, H=4096)

V cutedsl ms cutedsl MB cce ms cce MB quack ms quack MB
32000 7.0 1153 31.4 (4.5×) 1055 (0.9×) 4.4 (0.6×) 1841 (1.6×)
102400 20.4 3159 98.5 (4.8×) 2786 (0.9×) 15.5 (0.8×) 5302 (1.7×)
128256 25.8 3714 123.5 (4.8×) 3422 (0.9×) 18.8 (0.7×) 6573 (1.8×)
152064 29.4 4350 145.2 (4.9×) 4007 (0.9×) 23.1 (0.8×) 7743 (1.8×)
201088 39.4 5556 172.9 (4.4×) 5216 (0.9×) 30.6 (0.8×) 10157 (1.8×)
262144 51.5 7047 173.0 (3.4×) 6714 (1.0×) 39.9 (0.8×) 13153 (1.9×)

BT sweep (V=128256, H=4096)

BT cutedsl ms cutedsl MB cce ms cce MB quack ms quack MB
1024 4.3 3282 16.8 (3.9×) 3246 (1.0×) 2.7 (0.6×) 4295 (1.3×)
4096 13.7 3467 62.5 (4.6×) 3321 (1.0×) 9.4 (0.7×) 4370 (1.3×)
8192 25.7 3714 123.5 (4.8×) 3422 (0.9×) 18.9 (0.7×) 6573 (1.8×)
16384 50.0 4210 245.4 (4.9×) 3623 (0.9×) 37.5 (0.7×) 6774 (1.6×)
32768 103.4 4551 491.1 (4.7×) 4026 (0.9×) 76.5 (0.7×) 7177 (1.6×)
65536 193.6 5884 982.8 (5.1×) 4832 (0.8×) 152.3 (0.8×) 7982 (1.4×)

Reading the frontier. cutedsl and CCE are both store-free and sit at ~half the peak memory of the chunked-store quack across the sweep. Within that lean class, cutedsl is dramatically faster than CCE — ~4.8× at V=128k, and the gap widens with BT (CCE's backward scales poorly: 25.7 vs 123 ms at BT=8k → 194 vs 983 ms at BT=65k, a ~5× lead). CCE's own peak is a hair below cutedsl (it stores nothing but a running LSE; 3422 vs 3714 MB at V=128k), so the honest framing is: cutedsl and CCE tie on memory, and cutedsl is 4–5× faster.

vs quack — quack is the fastest kernel measured (~1.3× faster than cutedsl — the 0.6–0.8× column — roughly flat across the sweep, since its 3-GEMM chunked-store does less total work), but it pays ~1.5–2× the peak memory (chunked-store class). A clean speed-for-memory trade: quack wins throughput, cutedsl wins memory at near-matched throughput and is the only kernel that is both store-free and faster than Triton/CCE.

…riton

Adds the CuTe DSL fused-linear-cross-entropy (forward + backward),
signature-compatible with the Triton LigerFusedLinearCrossEntropyFunction and
selected via LIGER_KERNEL_IMPL=cutedsl.

The forward is store-free: built on quack's 2-SM (CtaGroup.TWO) tcgen05 GEMM, it
computes only the per-row LSE + the target logit in a fused epilogue and never
materializes the (BT, V) logits (not even per chunk). The backward recomputes the
masked softmax on the fly, tiled over the vocabulary so the grad_weight GEMM
contracts over K=BT and writes each slice once. Net on B200 (bf16): up to ~5x
faster than the Triton FLCE at LLM vocab sizes and ~1.5-1.9x less peak memory.

fp32 / non-Blackwell / softcap / argmax-metrics delegate to the upstream Triton
FLCE (the 2-SM GEMM is 16-bit-input only), so those paths are byte-identical to
and exactly as fast/lean as Triton.

Full feature parity with the Triton FLCE (ce_weight, bias, softcap,
label_smoothing, z-loss, ignore_index, token-accuracy, predicted-tokens,
use_token_scaling, accum_dtype, mean/sum, forward-only reduction='none').
reduction='none' with grad is refused by design (matches the Triton contract).

Tests: 277 passed (LIGER_KERNEL_IMPL=cutedsl). The AMP+bias=False parity tests
exercise a Triton addmm-under-autocast path fixed by linkedin#1283; stack this on linkedin#1283.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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