Skip to content

Add BFloat16 support for CUDA GatedDeltaNet - #32307

Merged
Tianlei Wu (tianleiwu) merged 4 commits into
mainfrom
copilot/add-bfloat16-support-tests
Aug 28, 2026
Merged

Tianlei Wu (tianleiwu) merged 4 commits into
mainfrom
copilot/add-bfloat16-support-tests

Conversation

Copilot AI commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Description

  • Register BFloat16 for the GatedDeltaNet schema and CUDA kernel.
  • Route BFloat16 execution through the recurrent engine while retaining FP16-only tensor-core paths.
  • Add BFloat16 numerical and planner coverage.
  • Guard mma.sync.m16n8k16 generation to SM80+, allowing pre-Ampere builds to compile.
  • Update operator documentation.

Motivation and Context

GatedDeltaNet supported float and float16 CUDA inputs but not BFloat16. Its unguarded SM80 MMA instruction also caused ptxas failures when compiling for older targets such as compute_61.

Copilot AI and others added 3 commits August 28, 2026 06:32
Co-authored-by: tianleiwu <30328909+tianleiwu@users.noreply.github.com>
Co-authored-by: tianleiwu <30328909+tianleiwu@users.noreply.github.com>
Co-authored-by: tianleiwu <30328909+tianleiwu@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

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

Adds CUDA BFloat16 support for GatedDeltaNet while retaining FP16-only tensor-core engines.

Changes:

  • Registers and tests BFloat16 recurrent execution.
  • Guards SM80 MMA assembly for older CUDA targets.
  • Updates schemas and operator documentation.

Reviewed changes

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

Show a summary per file
File Description
onnxruntime/test/contrib_ops/gated_delta_net_op_test.cc Adds BFloat16 numerical and planner tests.
onnxruntime/core/graph/contrib_ops/bert_defs.cc Adds BFloat16 to the schema.
onnxruntime/contrib_ops/cuda/cuda_contrib_kernels.cc Registers the BFloat16 kernel.
onnxruntime/contrib_ops/cuda/bert/gated_delta_net.cc Maps and dispatches BFloat16 inputs.
onnxruntime/contrib_ops/cuda/bert/gated_delta_net_split_impl.cu Rejects unsupported BFloat16 split execution.
onnxruntime/contrib_ops/cuda/bert/gated_delta_net_mma.cuh Guards SM80-only MMA assembly.
onnxruntime/contrib_ops/cuda/bert/gated_delta_net_impl.cu Instantiates recurrent BFloat16 execution.
docs/OperatorKernels.md Updates generated kernel documentation.
docs/ContribOperators.md Updates generated schema documentation.
docs/contrib_ops/cuda/gdn.md Documents BFloat16 constraints.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@tianleiwu Tianlei Wu (tianleiwu) 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.

Nice, tightly-scoped enablement. Verified locally on SM90: the branch builds clean and all 36 GatedDeltaNet* / GatedDeltaNetPlanTest* tests pass.

A couple of things I checked and liked:

  • The ToCudaType -> OrtToCudaType switch is required, not cosmetic: ToCudaType<BFloat16>::MappedType is ORT's own BFloat16, while OrtToCudaType<BFloat16>::type is __nv_bfloat16, which is what VariantPack<CudaT> and the (float) conversions in the kernels need. MLFloat16/float map identically in both traits, so existing types are unaffected.
  • No planner change was needed because IoType::kBFloat16, type_ok = (io_type == kFloat16) and the "chunked engine requires float16 input" reject reason already existed. That means ORT_GDN_PLAN=chunked with bf16 fails with an actionable message instead of silently degrading. Good reuse.
  • ToTensorType<T> is a net simplification of RunTypedCase, and the planner test is host-only so the routing contract is asserted even where the numeric tests skip.

Main concern

I agree with the existing thread on gated_delta_net_mma.cuh: the #else branch makes the tensor-core GEMM a silent no-op rather than a hard failure. Details and a suggested fix are in that thread.

Other notes

Inline comments below cover:

  1. bf16 instantiating a GatedDeltaNetChunkedKernel that SelectPlan can never select (measured +14.6% object size).
  2. The float64 reference being computed from unrounded fp32 inputs, so the 4e-2 tolerance mostly measures bf16 input quantization rather than kernel error, plus the bf16 coverage gaps.
  3. A nitpick on the nested ternary for desc.io_type.

None of 1-3 are blocking.

Keep the chunked engine's mma kernels out of the non-float16 instantiations,
document why the pre-Ampere MmaM16N8K16 body is a no-op, and make the typed
test harness round its reference inputs through the operator's input type so
the tolerances measure kernel error instead of input quantization.

- Guard the kChunked branch of LaunchGatedDeltaNet with if constexpr. SelectPlan
  only routes float16 there, so float/bfloat16 were emitting cubin that can
  never launch (gated_delta_net_impl.cu.o: 362,464 -> 330,256 bytes).
- Replace the nested io_type ternary with an if constexpr chain and include
  <type_traits> directly instead of relying on a transitive include.
- Add RoundToTensorType so Reference() sees the values the kernel reads. The
  bfloat16 output tolerance drops from 4e-2 to 6e-3 and the float32 final_state
  tolerance from 4e-2 to 2e-3.
- Add bfloat16 coverage for the decode kernel and the ragged path.
@tianleiwu
Tianlei Wu (tianleiwu) merged commit c5f91da into main Aug 28, 2026
90 checks passed
@tianleiwu
Tianlei Wu (tianleiwu) deleted the copilot/add-bfloat16-support-tests branch August 28, 2026 19:58
Tianlei Wu (tianleiwu) added a commit to microsoft/onnxruntime-genai that referenced this pull request Aug 28, 2026
## Summary
- export packed Qwen3.5/3.8 linear-attention layers with
`VarlenCausalConvWithState` and `GatedDeltaNet`
- replace dense recurrent checkpoints with compact convolution values
and GDN transition capsules
- export raw `A_log`/`dt_bias` for native Qwen gate arithmetic, without
`arithmetic_mode`
- keep committed paged recurrent state unwindowed, FP32, and V-major
while preserving the dense `LinearAttention` default

The existing `state_groups` schema is unchanged in this PR. Parsing
compact update metadata and replaying these transitions is handled by
the stacked runtime PR #2454.

## Dependencies
- microsoft/onnxruntime#32282
- microsoft/onnxruntime#32290
- microsoft/onnxruntime#32307

## Testing
- `python3 -m pytest test/python/builder/test_decoder_state_groups.py
test/python/builder/test_qwen_vl.py -q` (50 passed)
- `python3 -m pytest test/python/builder -q` (493 passed, 3 skipped)
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.

4 participants