[FlyDSL] Add optional ROCm/AMD FlyDSL backend with cross-entropy and fused linear cross-entropy#1297
[FlyDSL] Add optional ROCm/AMD FlyDSL backend with cross-entropy and fused linear cross-entropy#1297kashif wants to merge 4 commits into
Conversation
Same bug as the Triton backend (see linkedin#1298): grad_weight is a sum over tokens, so a per-token grad_output has to be folded into the logits gradient before the sum, not applied to the result afterwards. Defer the gradient to backward for reduction='none' and fold grad_output in per row. mean/sum are unchanged. Adds a regression test with a non-uniform grad_output.
|
While reviewing this I found that the Short version: with I have pushed the equivalent fix to this branch (facf21f) so the FlyDSL backend does not ship with the same bug: for #1298 is independent of this PR and can land on its own. |
Summary
Adds an optional FlyDSL backend for AMD/ROCm, starting with cross-entropy and fused linear cross-entropy. FlyDSL is a Python DSL + MLIR compiler targeting CDNA/RDNA, so this gives ROCm users a non-Triton path for the two ops that matter most for LLM training memory.
It's strictly opt-in — nothing changes unless you ask for it:
pip install -e ".[flydsl]" LIGER_KERNEL_IMPL=flydsl python train.pyOps that aren't implemented fall back to the default Triton kernels, and unsupported options (class weights, token accuracy, predicted tokens, token scaling) raise
NotImplementedErrorrather than silently diverging.Design notes
Two things worth calling out, because they're where most of the performance lives:
Normalizers stay on the device.
n_non_ignoreis passed to the kernel as a small fp32 tensor rather than a host float, so the mean reduction is folded into the loss and the gradient inside the kernel. No.item(), no D2H sync, and no rescale pass over the full(BT, V)tensor afterwards. As a side effect the stored per-row losses stay at ~L/Ninstead of ~L, which is what keepssum(loss_1d)from overflowing fp16 at large batch sizes.The gradient is produced in backward, not forward. Forward saves only the per-row
lse(BT floats); backward rebuildssoftmax(x) = exp2((x - lse) * log2e)and foldsgrad_outputstraight into the coefficient. That means we never materialize an unscaled gradient, so there's no second pass to rescale it. Total traffic is 2 reads + 1 write of the logits — the same as the Triton kernel's online softmax — with no host sync anywhere.The kernel itself follows the FlyDSL norm/softmax kernels: online softmax (one sweep for max+sum), 128-bit buffer copies over the whole row with a single masked tail vector, and a
shuffle_xorbutterfly block reduction. Indexing by vector element rather than by tile matters in practice — real vocab sizes (32000, 128256, 152064) aren't multiples ofBLOCK * vec_width, and gating on that would drop every production shape onto a scalar, one-element-per-lane path.FLCE chunks over the batch dimension using the same
aspect_ratiorule PyTorch'sLinearCrossEntropyOptionsuses (next_pow2(ceil(BT / ceil(V/H)))), so peak logits memory ischunk×Vrather thanBT×V.Benchmarks
AMD Radeon 890M (gfx1150, RDNA 3.5, wave32), ROCm 7.2, bf16. Timed with FlyDSL's own
do_bench(CUDA events, warmup + median); peak memory measured in isolated subprocesses.Cross-entropy, fwd+bwd — vs the Triton kernel:
Fused linear CE — BT=8192, H=2048, V=128256 (Llama-3 lm_head), full logits would be 2101 MB:
F.cross_entropy(lm_head(h))torch.nn.functional.linear_cross_entropySo on this hardware FLCE lands 24% below Triton on memory and 23% faster, and beats PyTorch's chunked
linear_cross_entropyon speed by 2.2× while using more memory than it.CE is still ~1.2–1.5× behind Triton on raw kernel time. It's latency-bound rather than bandwidth-bound (cutting HBM traffic 33% via register-buffering the row changed nothing), so the next lever is occupancy/scheduling — I'd want an
rocprofv3ATT trace before guessing further. Happy to land that as a follow-up.Tests
132 tests across
test/transformers/test_flydsl_cross_entropy.pyandtest_flydsl_fused_linear_cross_entropy.py, all passing on gfx1150. They check parity against the Triton kernel across dtypes (fp32/fp16/bf16), reductions (mean/sum/none),ignore_index,label_smoothing,softcap,z_loss, non-contiguous inputs, and the all-ignored edge case; plus parity againstF.linear_cross_entropy, the chunk-size heuristic, and a memory invariant asserting FLCE never allocates the fullBT×Vlogits.Everything skips cleanly when FlyDSL isn't installed or there's no AMD GPU, so this is a no-op for existing CI.