Skip to content

[CUDA] Support FP16 KV cache and FP16 query/key in DynamicSparseAttention<BFloat16> and FP32 rotary cache / FP16 output in RMSNormRotary - #33997

Open
Tianlei Wu (tianleiwu) wants to merge 2 commits into
mainfrom
tlwu/20261108/qwen38-fp16kv-fp32-rotary
Open

Tianlei Wu (tianleiwu) wants to merge 2 commits into
mainfrom
tlwu/20261108/qwen38-fp16kv-fp32-rotary

Conversation

@tianleiwu

Copy link
Copy Markdown
Contributor

Description

Add native FP16 (MLFloat16) KV cache and FP16 query/key support to DynamicSparseAttention<BFloat16> and FP32 rotary cache / FP16 output support to RMSNormRotary for long-context Qwen3.8-Flash-Next models:

  1. com.microsoft::RMSNormRotary (bert_defs.cc, rms_norm_rotary.h/.cc/.cu):

    • Extend TCos type constraint to allow tensor(float) in addition to tensor(bfloat16) so 256K RoPE cos_cache/sin_cache can be stored in exact FP32.
    • Add output_fp16 attribute (0 default BFloat16, 1 MLFloat16) and extend TOut type constraint to tensor(bfloat16) and tensor(float16).
    • Add RMSNormRotaryExactKernel<kFp32Cos, kOutputFp16> which computes RMSNorm + RoPE in exact FP32 without intermediate BFloat16 rounding before converting to the output type, reducing Q/K relative RMSE by 9.4x (0.1954% -> 0.0207%) and @32K attention logit RMSE by 9.5x (0.2789% -> 0.0293%).
  2. com.microsoft::DynamicSparseAttention (bert_defs.cc, dynamic_sparse_attention.cc, dynamic_sparse_attention_impl.h/.cu, dynamic_sparse_attention_prefill.cuh):

    • Extend TCache to support tensor(float16) and TQuery to support tensor(float16) in DynamicSparseAttention<BFloat16>, while fixing output 0 (output) type inference to propagate from input 2 (value) when present.
    • Instantiate Tensor Core MmaFp16 prefill kernel (DynamicSparseAttentionPrefillKernel<false, true>, mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32) and grouped decode kernel (GroupedDecodeDynamicSparseAttentionKernel<..., kFp16Cache=true>) for BFloat16 activations with FP16 KV cache.
    • Skip PrepareQueryKernel when query already matches the target attention dtype (need_prepare_query == false), making the FP16 KV cache path slightly faster than BF16 (143 us vs 152 us per layer at S=8, L=32K..192K).
  3. Unit Tests:

    • Added RMSNormRotaryFp32CosAndFp16OutputParity in onnxruntime/test/contrib_ops/rms_norm_rotary_test.cc.
    • Added SelectedOnlyFp16CacheBFloat16_CUDA and MLFloat16 cache/query coverage in SelectedOnlyPrefillBFloat16_CUDA, FloatingCacheGroupedReferenceParity_CUDA, and GroupedDecodeRatio12ReferenceParity_CUDA in onnxruntime/test/contrib_ops/dynamic_sparse_attention_op_test.cc.

…tion<BFloat16> and FP32 rotary cache / FP16 output in RMSNormRotary

- RMSNormRotary (bert_defs.cc, rms_norm_rotary.h/.cc/.cu):
  - Extend TCos type constraint to allow tensor(float) in addition to tensor(bfloat16) so 256K RoPE cos/sin caches can be kept in FP32.
  - Add output_fp16 attribute and TOut type constraint (tensor(bfloat16), tensor(float16)) and instantiate RMSNormRotaryExactKernel<kFp32Cos, kOutputFp16>, computing RMSNorm + RoPE in exact FP32 without intermediate BFloat16 rounding and outputting full 10-bit mantissa FP16 Q/K tensors.
- DynamicSparseAttention (bert_defs.cc, dynamic_sparse_attention.cc, dynamic_sparse_attention_impl.h/.cu, dynamic_sparse_attention_prefill.cuh):
  - Extend TCache to support tensor(float16) and TQuery to support tensor(float16) in DynamicSparseAttention<BFloat16>.
  - Instantiate Tensor Core MmaFp16 prefill kernel (DynamicSparseAttentionPrefillKernel<false, true>) and grouped decode kernel (GroupedDecodeDynamicSparseAttentionKernel<..., kFp16Cache=true>) for BFloat16 activations with FP16 KV cache.
  - Skip PrepareQueryKernel when query is already in the target attention dtype (need_prepare_query == false).
- Add unit tests in rms_norm_rotary_test.cc and dynamic_sparse_attention_op_test.cc.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 9, 2026 03:06

@github-actions github-actions Bot 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.

You can commit the suggested changes from lintrunner.

Comment on lines +22 to +37
#define REGISTER_KERNEL_TYPED(T, ...) \
ONNX_OPERATOR_TYPED_KERNEL_EX( \
DynamicSparseAttention, \
kMSDomain, \
1, \
T, \
kCudaExecutionProvider, \
(*KernelDefBuilder::Create()) \
.TypeConstraint("T", DataTypeImpl::GetTensorType<T>()) \
.TypeConstraint("TQuery", BuildKernelDefConstraints<__VA_ARGS__>())\
.TypeConstraint("TCache", BuildKernelDefConstraints<__VA_ARGS__>())\
.TypeConstraint("S", DataTypeImpl::GetTensorType<float>()) \
.TypeConstraint("M", DataTypeImpl::GetTensorType<int32_t>()) \
.MayInplace(3, 1) \
.MayInplace(4, 2) \
.InputMemoryType(OrtMemTypeCPUInput, 10), \

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.

Suggested change
#define REGISTER_KERNEL_TYPED(T, ...) \
ONNX_OPERATOR_TYPED_KERNEL_EX( \
DynamicSparseAttention, \
kMSDomain, \
1, \
T, \
kCudaExecutionProvider, \
(*KernelDefBuilder::Create()) \
.TypeConstraint("T", DataTypeImpl::GetTensorType<T>()) \
.TypeConstraint("TQuery", BuildKernelDefConstraints<__VA_ARGS__>())\
.TypeConstraint("TCache", BuildKernelDefConstraints<__VA_ARGS__>())\
.TypeConstraint("S", DataTypeImpl::GetTensorType<float>()) \
.TypeConstraint("M", DataTypeImpl::GetTensorType<int32_t>()) \
.MayInplace(3, 1) \
.MayInplace(4, 2) \
.InputMemoryType(OrtMemTypeCPUInput, 10), \
#define REGISTER_KERNEL_TYPED(T, ...) \
ONNX_OPERATOR_TYPED_KERNEL_EX( \
DynamicSparseAttention, \
kMSDomain, \
1, \
T, \
kCudaExecutionProvider, \
(*KernelDefBuilder::Create()) \
.TypeConstraint("T", DataTypeImpl::GetTensorType<T>()) \
.TypeConstraint("TQuery", BuildKernelDefConstraints<__VA_ARGS__>()) \
.TypeConstraint("TCache", BuildKernelDefConstraints<__VA_ARGS__>()) \
.TypeConstraint("S", DataTypeImpl::GetTensorType<float>()) \
.TypeConstraint("M", DataTypeImpl::GetTensorType<int32_t>()) \
.MayInplace(3, 1) \
.MayInplace(4, 2) \
.InputMemoryType(OrtMemTypeCPUInput, 10), \

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.

🟡 Changes recommended

Grouped attention can overflow BF16 auxiliary values when converting to FP16, while the schema change permits unsupported mixed types that generate invalid WebGPU shaders.

4 open issues
What changed in this PR

This PR extends ONNX Runtime’s CUDA contrib operators for long-context models, adding mixed-precision sparse attention and higher-precision RMS normalization with rotary embeddings.

Changes:

  • Add FP16 query/key and KV-cache support to DynamicSparseAttention, including prefill/decode kernels and a query-preparation bypass.
  • Add FP32 rotary caches and optional FP16 output to RMSNormRotary using FP32 intermediate arithmetic.
  • Update operator schemas, output type inference, and CUDA parity tests.
File Description
onnxruntime/​test/​contrib_ops/​rms_norm_rotary_test.cc Adds FP32-cache and FP16-output parity coverage.
onnxruntime/​test/​contrib_ops/​dynamic_sparse_attention_op_test.cc Adds mixed query/cache dtype coverage.
onnxruntime/​core/​graph/​contrib_ops/​bert_defs.cc Extends schemas and updates output type inference.
onnxruntime/​contrib_ops/​cuda/​bert/​rms_norm_rotary.h Extends launch parameters and output configuration.
onnxruntime/​contrib_ops/​cuda/​bert/​rms_norm_rotary.cu Implements the FP32-intermediate normalization/rotary kernel.
onnxruntime/​contrib_ops/​cuda/​bert/​rms_norm_rotary.cc Registers new types and dispatches cache/output modes.
onnxruntime/​contrib_ops/​cuda/​bert/​dynamic_sparse_attention.cc Registers and validates mixed-precision inputs.
onnxruntime/​contrib_ops/​cuda/​bert/​dynamic_sparse_attention_prefill.cuh Adds FP16 Tensor Core prefill support.
onnxruntime/​contrib_ops/​cuda/​bert/​dynamic_sparse_attention_impl.h Adds mixed-precision execution flags.
onnxruntime/​contrib_ops/​cuda/​bert/​dynamic_sparse_attention_impl.cu Updates preparation, cache writes, attention kernels, and dispatch.

Review effort: Balanced

Comment on lines +947 to +948
shared_key[shared_offset + d] = FromFloat<half>(ToFloat(auxiliary_key[offset + d]));
shared_value[shared_offset + d] = FromFloat<half>(ToFloat(auxiliary_value[offset + d]));
Comment on lines +704 to +705
"TQuery")
.Input(1, "key", "Current main key [batch, sequence, kv_num_heads * head_size].", "TQuery",
Comment on lines +400 to +401
result_f32 = ApplyRotaryFloat(reduction, h, static_cast<int>(position), cos_cache, sin_cache,
rotary_dim, rotary_offset, rotary_interleaved);
Comment on lines +6427 to +6428
.TypeConstraint("TCos", {"tensor(bfloat16)", "tensor(float)"}, "BF16 or FP32 rotary cache.")
.TypeConstraint("TOut", {"tensor(bfloat16)", "tensor(float16)"}, "BF16 or FP16 output.")
…nd FP16 X input in RMSNormRotary

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

This branch has not been deployed

No deployments
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.

2 participants