Repository navigation
feat(lowering): optionally run multi-query attention against the unrepeated key/value head - #4921
Merged
Merged
Conversation
shoumikhin
force-pushed
the
fold-repeated-kv-matmul
branch
4 times, most recently
from
October 25, 2026 23:19
2b85550 to
d9fd8e9
Compare
…eates replace_fused_rms_norm swaps aten._fused_rms_norm for plain mul, mean, add and rsqrt nodes, but left those replacement nodes without a fake value (meta["val"]). A later lowering pass that reads shapes from meta["val"] finds no shape on them, so such a pass had to run before this one. Each new node now gets meta["val"] computed from its inputs' fake values, when all of its inputs have one.
…peated key/value head Hugging Face models with one key/value head repeat it once per query head before attention (repeat_kv). TensorRT then copies the repeated tensors and runs its fused attention kernel, which is slow at head size 256 on some GPUs (A100 and H100). The new fold_shared_kv_attention setting runs such attention as two plain matmuls against the single key/value head, with the query heads folded into the rows. The math is the same, but float16 results round differently; use_fp32_acc accumulates each matmul in float32. Only static shapes are rewritten, and scaled_dot_product_attention only with decompose_attention. Under torch.compile with use_distributed_mode_trace, matmuls are lowered to bmm first, so nothing is rewritten there. When nothing matches, the pass says so in the log. It is off by default because other GPUs (Jetson Orin) run the fused kernel faster.
shoumikhin
force-pushed
the
fold-repeated-kv-matmul
branch
from
October 26, 2026 18:01
d9fd8e9 to
a2273a6
Compare
zewenli98
approved these changes
Oct 27, 2026
zewenli98
left a comment
Collaborator
There was a problem hiding this comment.
It's a great addition. LGTM
lanluo-nvidia
pushed a commit
that referenced
this pull request
Oct 28, 2026
…peated key/value head (#4921)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is wrong today
In multi-query attention, all query heads share one key head and one value head (Gemma 2B, PaliGemma, pi0, pi0.5). Hugging Face code first copies the shared head once per query head (
repeat_kv), then runs two batched matrix multiplications over the copies. TensorRT copies the repeated keys and values and runs its fused attention kernel, which is slow at head size 256 on some GPUs, such as A100 and H100.What this change does
A new setting,
fold_shared_kv_attention(defaultFalse), turns on a lowering pass. It folds the query heads into the rows and multiplies against the single head:Things to know:
use_fp32_acc=Truebrought it to 0.9 to 2.3 times.decompose_attention=True.torch.compilewithuse_distributed_mode_trace.It is off by default because a Jetson Orin runs the fused kernel faster.
The first commit is a small fix it builds on:
replace_fused_rms_normnow sets the fake value (meta["val"]) on the nodes it creates, so a later pass can read shapes after an RMSNorm (root mean square normalization) layer.Performance
8 query heads, 1 key/value head, head size 256, kernel time:
pi0.5 on an A100: 59.5 to 47.0 ms per action chunk. On a Jetson Orin Nano a denoising step got slower, 14.4 to 15.4 ms.
What was tested
The tests run the lowering pipeline and check the folded shapes, also after an RMSNorm. They cover decomposed SDPA (plain,
use_fp32_acc,enable_gqa) and attention compiled withtorch_tensorrt.dynamo.compileand withtorch.compile, compared against PyTorch. They also check graphs that must stay unchanged: several key/value heads, dynamic lengths, and ten graphs that look likerepeat_kvbut must not be folded. Removing any one check of the pass makes at least one test fail.They pass on a Jetson AGX Thor and on x86-64 with an H100 (43 passed), both with TensorRT 11.4 and a PyTorch 2.16 nightly.