Skip to content

[CUDA] GroupQueryAttention: dequantize the K and V caches in one launch - #31480

Merged
Tianlei Wu (tianleiwu) merged 1 commit into
mainfrom
tlwu/20260802/gqa_fused_kv_dequant
Aug 6, 2026
Merged

[CUDA] GroupQueryAttention: dequantize the K and V caches in one launch#31480
Tianlei Wu (tianleiwu) merged 1 commit into
mainfrom
tlwu/20260802/gqa_fused_kv_dequant

Conversation

@tianleiwu

Copy link
Copy Markdown
Contributor

Description

When GroupQueryAttention runs with a quantized KV cache, the K cache and the V cache were dequantized by two separate kernel launches. They have identical shapes and identical per-head scale layouts, so the second launch adds a full grid setup and a second pass over the same index arithmetic for no reason.

This change dequantizes both caches in a single launch. The kernel is moved into a new group_query_attention_qdq.cuh header and given a 2 * grid in the cache dimension, so one block range covers K and the other covers V; the buffer pointers and scale pointers are selected from the block index.

Output is bit-identical to the two-launch path -- the per-element arithmetic is unchanged, only the launch geometry differs.

Motivation and Context

This is on the decode path of speculative-decoding (MTP) workloads, where the launch is issued every layer, every step, and the per-launch fixed cost is a meaningful fraction of a short kernel. Halving the number of launches removes that fixed cost without changing numerics.

Measured on H200 (SM90) with a Qwen3.6-35B-A3B MTP configuration.

The quantized-KV decode fallback dequantized the two caches back to back. Each
launch only exposes batch_size * kv_num_heads independent (batch, head) slices --
two for a batch-1 model with two KV heads -- so most of the device sat idle.

Add DequantizeKVPairKernel, which selects the tensor with blockIdx.z and is
otherwise identical to DequantizeKVVectorizedKernel. That doubles the CTA count,
halves the launches, and lets both caches share the memory pipeline. kVecSize is
8 rather than 32 because at decode lengths the kernel is latency bound, not
instruction bound, so more threads with narrower vectors wins.

Bit-identical to the two-launch path. INT4, non-16-bit outputs and head sizes
that do not tile into the fused block shape still take the old path.

Measured on Qwen3.6-35B MTP N=3: 21.7 -> 10.8 launches/step and
59.8 -> 24.8 us/step.

Opt out with ORT_DISABLE_FUSED_KV_DEQUANT=1.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the CUDA GroupQueryAttention decode fallback path for quantized KV cache by fusing K-cache and V-cache dequantization into a single kernel launch, reducing per-step launch overhead.

Changes:

  • Add a fused CUDA kernel (DequantizeKVPairKernel) plus a new launcher (LaunchDequantizeKVPair) to dequantize K and V caches in one launch (with fallback to the prior two-launch behavior when unsupported).
  • Switch the FlashAttention dequantize-fallback path to call the fused launcher instead of launching two separate dequant kernels.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
onnxruntime/contrib_ops/cuda/bert/group_query_attention_qdq.cuh Introduces fused K+V dequantization kernel/launcher and keeps a fallback path for unsupported formats.
onnxruntime/contrib_ops/cuda/bert/group_query_attention_impl.cu Routes quantized-cache FlashAttention fallback dequantization through the new fused launcher.

Comment thread onnxruntime/contrib_ops/cuda/bert/group_query_attention_qdq.cuh

@hariharans29 Hariharan Seshadri (hariharans29) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing no tests are needed as it will be taken care of by the GQA tests ?

@tianleiwu

Copy link
Copy Markdown
Contributor Author

I am guessing no tests are needed as it will be taken care of by the GQA tests ?

Yes. It's covered by existing tests.

@tianleiwu
Tianlei Wu (tianleiwu) merged commit c003602 into main Aug 6, 2026
88 checks passed
@tianleiwu
Tianlei Wu (tianleiwu) deleted the tlwu/20260802/gqa_fused_kv_dequant branch August 6, 2026 05:15
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.

3 participants