Repository navigation
[Perf] Fuse GatedDeltaNet q/k/v short convolutions in the dense no-cache path - #972
Conversation
Use a single dense short-convolution call for GatedDeltaNet q/k/v projections in the no-cache path and add layer-level coverage and benchmarking. Co-authored-by: Cursor <cursoragent@cursor.com>
Move the standalone GatedDeltaNet benchmark into its own PR so this branch only contains the fused qkv convolution runtime change and regression test. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a fused QKV convolution optimization in GatedDeltaNet when there is no cache or variable-length state, and the Q, K, and V convolutions share the same configuration. This is achieved by concatenating the projections and weights to execute a single causal_conv1d instead of three separate convolutions. A comprehensive unit test is also added to verify correctness and ensure the fused path is correctly triggered. Feedback suggests replacing rearrange with native PyTorch .squeeze(1) to avoid einops overhead in the forward pass.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
Thank you for your contribution. |
Extract the dense no-cache q/k/v short-convolution fusion introduced for GatedDeltaNet (#972) into a shared helper, `fused_qkv_short_conv`, plus the `can_fuse_qkv_short_conv` guard, in `fla.modules.convolution`, and route every q/k/v short-conv layer through it. The helper reads the split sizes from the conv weights, so it transparently handles standard, grouped (GQA), and multi-householder channel counts, and accepts an optional attention mask that mirrors `ShortConvolution.forward`. Each layer keeps its own cache / varlen guard and any layer-specific post-activation, so the fallback and semantics are unchanged. Covered layers: GatedDeltaNet, GDN2, KDA, Comba, GatedDeltaProduct, DeltaNet, GLA, MultiscaleRetention, GSA, ABC, SimpleGLA, LightNet. MoM (varlen / single-kv path) and MesaNet (q/k only) are intentionally left untouched. Adds a model-agnostic test comparing the fused helper against three separate ShortConvolution calls (forward and backward, across split / bias / activation / mask variants) and updates the GatedDeltaNet fusion test to patch the new guard predicate.
Extract the dense no-cache q/k/v short-convolution fusion introduced for GatedDeltaNet (#972) into a shared helper, `fused_qkv_short_conv`, plus the `can_fuse_qkv_short_conv` guard, in `fla.modules.convolution`, and route every q/k/v short-conv layer through it. The helper reads the split sizes from the conv weights, so it transparently handles standard, grouped (GQA), and multi-householder channel counts, and accepts an optional attention mask that mirrors `ShortConvolution.forward`. Each layer keeps its own cache / varlen guard and any layer-specific post-activation, so the fallback and semantics are unchanged. Covered layers: GatedDeltaNet, GDN2, KDA, Comba, GatedDeltaProduct, DeltaNet, GLA, MultiscaleRetention, GSA, ABC, SimpleGLA, LightNet. MoM (varlen / single-kv path) and MesaNet (q/k only) are intentionally left untouched. Adds a model-agnostic test comparing the fused helper against three separate ShortConvolution calls (forward and backward, across split / bias / activation / mask variants) and updates the GatedDeltaNet fusion test to patch the new guard predicate.
…che path (fla-org#972) * [Perf] Fuse GDN qkv short convolution Use a single dense short-convolution call for GatedDeltaNet q/k/v projections in the no-cache path and add layer-level coverage and benchmarking. Co-authored-by: Cursor <cursoragent@cursor.com> * [Misc] Split GDN layer benchmark tooling Move the standalone GatedDeltaNet benchmark into its own PR so this branch only contains the fused qkv convolution runtime change and regression test. Co-authored-by: Cursor <cursoragent@cursor.com> * add assertion for gradients * extract condition to fn so we can mock * harden test * address comment --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Zhiyuan Li <uniartisan2017@gmail.com>
Summary
Hi folks. Today I offer you an optimization to the GDN Layer.
This PR adds a guarded fast path in
GatedDeltaNet.forwardthat runs theq/k/vshort convolutions as a singlecausal_conv1dcall. This drops from three conv kernel launches from the layer's convolution prologue to a single one + a cat/split without changing the math.We concatenate the
q/k/vprojection outputs along the channel axis, concatenate the matching depthwise conv weights (and bias, if any), and run onecausal_conv1d(same underlying kernel). Finally, we then split the result back intoq/k/v.Why
A short depthwise convolution is launch-bound at the default GatedDeltaNet layer shape: the three
q/k/vconv calls each do little work, but pay full launch overhead. Since all three are depthwise (groups=channels) convolutions with the same backend, activation, and kernel size, there's no cross-channel mixing, and so, concatenating along the channel axis and running one wider depthwise convolution is mathematically identical to running the three separately while paying the launch cost only once.This is a layer-prologue change and is independent of the GDN kernels, so it stacks cleanly with kernel optimizations.
Benchmark / Performance
This is a layer-prologue change, so it is most visible at the layer level. Full
benchmark_training_throughputdilutes it to noise (~1.003x end-to-end), and the op runnerbenchmarks.ops.run --op chunk_gated_delta_ruledoes not see it since the GDN kernels are unchanged. The removal of twocausal_conv1dlaunches shows up in a per-kernel breakdown (benchmark_training_throughput --profile), but does not provide speedup measurements. See PR#952 for a discussion around this. Therefore, we use the script below to get the speedup values:B200 (CC 10.0, CUDA 12.8, PyTorch 2.11)
B2-T2048-H2048-hd256bf16 (representative)B1-T128-H512-hd64bf16B2-T100-H512-hd64bf16B2-T256-H512-hd64fp16Test plan
Added a regression test at
test_gated_deltanet.pythat compares the fused path with the original one over a few shapes/dtypes. The forward output and the input gradient match exactly and every parameter gradient matches within5e-3. Only the q/k/v conv weights differ at all (by≲1e-3) due to accumulation noise from regrouping the concatenated conv weight's backward.Breaking changes
None. Public API and layer outputs are unchanged.
Risk / scope
The fast path is intentionally narrow and is guarded to activate only when:
use_short_convis enabledlast_state is None,use_cacheis falsy)cu_seqlens is None)q/k/vconvolutions share the same backend, activation, and kernel sizeIf any are not true, we don't apply the optimization. No new Triton kernels are introduced and we use the existing
causal_conv1dfor the fused path.