Skip to content

[CuteDSL] Add CuTe DSL fused-linear-cross-entropy (FLCE) - #1287

Closed
justinhh4 wants to merge 4 commits into
linkedin:mainfrom
justinhh4:justinhh4/cutedsl-flce
Closed

[CuteDSL] Add CuTe DSL fused-linear-cross-entropy (FLCE)#1287
justinhh4 wants to merge 4 commits into
linkedin:mainfrom
justinhh4:justinhh4/cutedsl-flce

Conversation

@justinhh4

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #1279 (the CuTe DSL cross-entropy PR, whose body notes: "The fused-linear-CE
(FLCE) variant will follow in a later PR."
). This is that PR, stacked on
justinhh4/cutedsl-cross-entropy.

Adds the CuTe DSL FLCE (forward + backward), signature-compatible with the Triton
LigerFusedLinearCrossEntropyFunction. It reuses the CuTe DSL CE kernel from #1279 per
token-chunk and mirrors the upstream Triton FLCE control flow byte-for-byte — memory-minimal
chunk sizing, fp32 loss/grad accumulators, torch.addmm(out_dtype=fp32) grad_weight accumulation
(PR #1239), and the token-scaling transform — so the only difference between the two FLCE
backends is the CE kernel.

Feature parity: ce_weight, softcap, label_smoothing, z-loss, 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 in the forward and cannot honor a per-token
upstream grad — refusing loudly beats silently mis-scaling), matching the documented FLCE
contract.

Files

  • src/liger_kernel/ops/cutedsl/ops/fused_linear_cross_entropy.py — the FLCE Function + fwd/bwd.
  • src/liger_kernel/ops/cutedsl/ops/__init__.py — export the FLCE symbols (so
    LIGER_KERNEL_IMPL=cutedsl rewires the public FLCE too).
  • test/transformers/test_cutedsl_fused_linear_cross_entropy.py — parity suite (below).

Correctness

test/transformers/test_cutedsl_fused_linear_cross_entropy.py186 passed, 1 skipped (the
skip is the LIGER_KERNEL_IMPL=cutedsl dispatch test, which passes when that env varis set).
Full parity vs the Triton FLCE (and vs torch where applicable) across dtype × reduction × every
feature × grad, plus: AMP/autocast, 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.

Benchmarks — CuTe DSL vs Triton FLCE (NVIDIA B200)

Interleaved A/B, median p50 of the full (fwd+bwd) mode, H=4096.
speedup = Triton ÷ CuteDSL (>1.00 → CuteDSL faster). Reference config: BT=8192, V=128256.

FLCE is matmul-dominated (the x @ Wᵀ logits + the two backward GEMMs dwarf the CE kernel), and
those matmuls are the same cuBLAS calls on both backends — so CuteDSL FLCE runs at parity
with Triton FLCE, edging ahead where the CE kernel's slightly-faster forward shows through (large
BT bf16).

bf16

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

vocab cutedsl (ms) triton (ms) speedup
32000 10.8580 10.9799 1.011×
102400 54.8335 55.3742 1.010×
128256 122.9192 123.3724 1.004×
152064 145.9202 146.5442 1.004×
201088 192.7750 193.5171 1.004×
262144 484.5868 484.8187 1.000×

full — BT sweep (V=128256, H=4096)

BT cutedsl (ms) triton (ms) speedup
1024 117.1424 117.0788 0.999×
2048 117.8946 117.8111 0.999×
4096 119.7726 119.7058 0.999×
8192 122.9192 123.3724 1.004×
16384 136.8713 137.6336 1.006×
32768 169.2146 171.1585 1.011×
65536 236.0707 239.6154 1.015×

fp32

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

vocab cutedsl (ms) triton (ms) speedup
32000 106.9791 106.8459 0.999×
102400 352.7466 352.9529 1.001×
128256 479.7558 479.8976 1.000×
152064 572.4296 572.8672 1.001×
201088 746.6234 747.0672 1.001×
262144 1122.6364 1122.9476 1.000×

full — BT sweep (V=128256, H=4096)

BT cutedsl (ms) triton (ms) speedup
1024 122.8811 122.8840 1.000×
2048 163.8892 163.9028 1.000×
4096 288.1390 288.1517 1.000×
8192 479.7558 479.8976 1.000×
16384 886.2119 886.3500 1.000×
32768 1693.9213 1694.2659 1.000×
65536 3200.2612 3200.9146 1.000×

Peak memory (the FLCE headline win) — full, BT=8192, V=128256, H=4096

provider bf16 fp32
cutedsl FLCE 6638 MB 9007 MB
Triton FLCE 6638 MB 9007 MB
torch (materialized logits) 14911 MB 17146 MB

CuteDSL FLCE matches Triton FLCE's peak memory exactly (both avoid materializing the BT×V logits),
~2.2× under the stock Linear → CrossEntropy path.

justinhh4 and others added 4 commits June 30, 2026 17:53
Add an optional CuTe DSL (nvidia-cutlass-dsl) backend implementing Liger
cross-entropy forward+backward as a single fused kernel, signature-compatible
with the Triton LigerCrossEntropyFunction. Supports z-loss, softcap, label
smoothing, class weights, ignore_index, token-accuracy and predicted-tokens,
across bf16/fp16/fp32.

B200 optimizations: online-softmax pass with a cp.async multi-stage pipeline,
FMA-folded exp2 arguments, power-of-2 ring-buffer stage advance, and reduced
per-call host overhead (cached CUstream, batched validation D2H sync, and
reused DLPack handles for unused optional outputs).

Adds the `cutedsl` optional dependency (nvidia-cutlass-dsl) and parity tests
against the Triton kernel (158 cases). The fused-linear-CE (FLCE) variant will
follow in a later PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Follow-up to linkedin#1279 (the CE PR promised the FLCE variant "in a later PR").

Adds the CuTe DSL FLCE (forward + backward), signature-compatible with the Triton
LigerFusedLinearCrossEntropyFunction. It reuses the CuTe DSL CE kernel per token-chunk
and mirrors the upstream Triton FLCE control flow byte-for-byte — memory-minimal chunk
sizing, fp32 loss/grad accumulators, torch.addmm(out_dtype=fp32) grad_weight accumulation
(PR linkedin#1239), and the token-scaling transform — so the ONLY difference between the two FLCE
backends is the CE kernel. Full feature parity: ce_weight, softcap, label_smoothing,
z-loss, ignore_index, token-accuracy, predicted-tokens, use_token_scaling, accum_dtype,
reductions mean/sum, plus forward-only reduction='none' (none+grad is refused by design,
since the fused pass accumulates grad_weight over tokens and cannot honor a per-token
upstream grad).

- src/liger_kernel/ops/cutedsl/ops/fused_linear_cross_entropy.py: the kernel wrapper.
- src/liger_kernel/ops/cutedsl/ops/__init__.py: export the FLCE Function + fwd/bwd.
- test/transformers/test_cutedsl_fused_linear_cross_entropy.py: cutedsl-vs-Triton (and
  vs torch) parity across dtype x reduction x every feature x grad, AMP, functional API +
  structured output, dispatch wiring, production vocab (V=32000 tail predication), and the
  host-side validation / by-design-refusal contracts. 186 passed, 1 skipped on B200.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The bf16 atol=1e-8 (rtol-only) bar failed test_ce_token_accuracy_matches_triton[mean-bf16]
on B200: the logit=50 peaked-softmax rows produce near-zero grad entries where bf16's coarse
mantissa lands ~8.7e-6 from Triton and rtol*|ref| collapses toward zero, so atol must carry
them. Set bf16 -> (5e-5, 5e-2): covers the measured ~8.7e-6 with ~5.7x headroom, still 100x
tighter than the original 5e-3. Same near-zero-grad phenomenon already documented for fp16/fp32.

Full suite: 158 passed, 1 skipped on B200.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@justinhh4

Copy link
Copy Markdown
Contributor Author

Superseded by #1291

@justinhh4 justinhh4 closed this Jul 7, 2026
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