Repository navigation
Add BFloat16 support for CUDA GatedDeltaNet - #32307
Conversation
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: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
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.
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
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->OrtToCudaTypeswitch is required, not cosmetic:ToCudaType<BFloat16>::MappedTypeis ORT's ownBFloat16, whileOrtToCudaType<BFloat16>::typeis__nv_bfloat16, which is whatVariantPack<CudaT>and the(float)conversions in the kernels need.MLFloat16/floatmap 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 meansORT_GDN_PLAN=chunkedwith bf16 fails with an actionable message instead of silently degrading. Good reuse. ToTensorType<T>is a net simplification ofRunTypedCase, 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:
- bf16 instantiating a
GatedDeltaNetChunkedKernelthatSelectPlancan never select (measured +14.6% object size). - The float64 reference being computed from unrounded fp32 inputs, so the
4e-2tolerance mostly measures bf16 input quantization rather than kernel error, plus the bf16 coverage gaps. - 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.
## 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)
Description
BFloat16for the GatedDeltaNet schema and CUDA kernel.mma.sync.m16n8k16generation to SM80+, allowing pre-Ampere builds to compile.Motivation and Context
GatedDeltaNet supported float and float16 CUDA inputs but not BFloat16. Its unguarded SM80 MMA instruction also caused
ptxasfailures when compiling for older targets such ascompute_61.