You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[CUDA] Extend GatedDeltaNet and CausalConvWithState for S<=8 speculative decode - #33471
Extends com.microsoft::CausalConvWithState and com.microsoft::GatedDeltaNet on the CUDA Execution Provider to support $S \le 8$ speculative decoding, packed-channel / packed-QKV projections, tiled $K=4$ prefill convolution, and compact state_update capture (state_update_capacity in [0, 8]).
Implements the extended packed-channel CUDA path with: (1) CausalConvProjectionS2K4Kernel for short decode ($1 \le S \le 8$, kernel_size=4, state_length=4, dilation=1), (2) CausalConvProjectionTiledK4Kernel (8 positions per CTA with sliding registers) for prefill ($S \ge 16$), and (3) a graph-capturable cudaMemset2DAsync zero-fill for unused state_update rows when length < state_update_capacity
Adds TG type constraint so decay and beta can match the query element type (float, float16, bfloat16), and documents gate_activation="softplus" with "qwen" retained as a legacy alias
Supports packed QKV without intermediate unpacking on short decode ($S \le 8$), templated GateT for decay/beta, compact state_update_capacity capture (0..8), and the 8-warp QwenS2 recurrent decode specialization (ORT_GDN_S2_SPECIALIZE=1 by default) for single-request $1 \le S \le 8$, 16 Q/K heads, 48 V heads, and head size 128
Supports "softplus" and legacy alias "qwen" for gate_activation
Tests and Documentation
onnxruntime/test/contrib_ops/causal_conv_with_state_op_test.cc: Covers packed-channel extended history across lengths {1, 2, 3, 5, 8, 9, 16, 17, 24} (exercising both CausalConvProjectionS2K4Kernel and CausalConvProjectionTiledK4Kernel), full and partial channel blocks, both state layouts, present/absent past history, FP32/FP16/BF16, state_update capture verification, and tail zeroing when state_update_capacity > sequence_length.
onnxruntime/test/contrib_ops/gated_delta_net_op_test.cc: Covers input-type decay/beta (FP16/BF16), QwenS2 ($S \in {1, 2, 4, 8}$) packed-QKV parity, state_update_capacity capture, empty requests in recurrent state, and bit-exact parity between gate_activation="softplus" and "qwen" on CUDA and WebGPU.
docs/ContribOperators.md, docs/OperatorKernels.md, docs/contrib_ops/cuda/gdn.md: Syncs operator schemas/kernel tables and documents ORT_GDN_S2_SPECIALIZE / ORT_CAUSAL_CONV_S2_SPECIALIZE eligibility and H200 op-level & end-to-end benchmarks.
Performance
Measured on NVIDIA H200 (SM90, CUDA 13.0) at Qwen3.8-Flash-Next RC3 geometry ((B, S, 10240) BF16, state_update_capacity=7):
GatedDeltaNet (QwenS2): 1.15x–1.25x faster across $S \in {1, 2, 4, 8}$ (18.82 us vs 21.60 us at $B=1, S=8$), saving ~0.10–0.15 ms per DFlash2 $S=8$ verification cycle across 36 layers.
CausalConvWithState: 1.19x–1.37x faster on short decode ($S \le 8$) and 4.06x faster on prefill at $S=2048$ (55.13 us vs 223.97 us), reducing end-to-end TTFT by 2.9–3.1% at 2K–32K context lengths.
Testing
Run provider unit tests: onnxruntime_provider_test --gtest_filter="CausalConvWithStateTest.*:GatedDeltaNetTest.*:GatedDeltaNetWebGpuTest.*"
End-to-end verification on qwen_flash_next_int4_int8_rc3_contrib_dflash2 ($S=8$, state_update_capacity=7).
Checklist
Tests added/updated
No breaking changes (or documented in description)
Initialize mid_state for empty ragged requests in both recurrent kernels, correct the softplus reference alias, and add regression coverage. Use the adapter-supported output-count API and synchronize operator documentation with the extended schemas.
The weight offset uses signed 32-bit multiplication. With 65,537 channels, a 32,768-tap kernel, and dilation 1, the extended-path dimension checks pass, but channel 65,536 computes c * kernel_size == 2^31. This overflows the index and can cause an out-of-bounds device read. Cast c to int64_t before multiplication, as the input offsets already do.
Detect optional output presence instead of output count
An omitted optional output may be encoded as outputs=["output", "present_state", ""]. Its placeholder still counts toward getNumOutputs(), so this condition rejects a valid default channels-first convolution as requiring extended mode. The CPU constructor, CUDA extended-path selection, and WebGPU output guard repeat the same count-only check. Check actual output presence throughout: use ctx.hasOutput(2) in inference, NodeArg::Exists() where available, and a plugin-compatible presence check for WebGPU (for example, checking Output(2, state_shape) after the shape is known). Add an AddOptionalOutputEdge regression covering these provider paths.
Both new rejection tests can terminate no-exception test runs. With CUDA available, PackedChannelsInvalidConfigurations reaches fail_shape_inference; PackedChannelsRejectsUnsupportedCpu reaches the constructor's ORT_ENFORCE even in CPU-only builds. Under ORT_NO_EXCEPTIONS, these paths abort rather than return a failure that kExpectFailure can check. Wrap both tests (lines 403–441) in #ifndef ORT_NO_EXCEPTIONS, following the existing shape-inference rejection tests below, or add a conditional GTEST_SKIP() before constructing each tester.
Added exact FP16/BF16 packed-QKV parity tests and documented eligibility/fallbacks.
No multi-draft state outputs, dense-prefill normalization experiments, weights, binaries, or benchmark artifacts were included.
Validation of the publishing sources: all changed CUDA translation units compile. Process-isolated native tests pass with specialization switches both off and on: 6 focused GDN packed/input-type tests and 53 convolution tests per mode. This is an incremental validation against retained build dependencies, not a clean full upstream build.
The broader GatedDeltaNetTest.RecurrentMidStatePreservesEmptyRequests test fails in this incremental build. I also compiled the unmodified PR-head GDN kernel and reproduced an invalid-argument failure (32-wide case). The broader run can poison later CUDA tests, so it is not reported as clean. Clean-build CI is still required; this update does not attempt an unrelated baseline fix.
Historical experimental-package benchmarks improved 8K decode from 213.51 to 228.06 TPS and 16K from 227.85 to 242.60 TPS in a controlled bracket. These numbers are not a re-benchmark of the transplanted PR tip. Full model accuracy remains deferred, and this is not a claim of sustained 250 TPS at 8K.
Fresh-build integration validation update (no source changes pushed to this ORT PR in this update):
Rebuilt ORT from scratch for H200 (sm90a), CUDA provider as a plugin, with FP4 QMoE, FP8/INT4 KV cache, GQA value layout, and FPA-INTB GEMM enabled. onnxruntime_QUICK_BUILD=OFF; no retained build objects were reused.
Rebuilt GenAI against the fresh ORT headers/libraries, and rebuilt the fold custom library.
Sparse/indexer/FP8 suite plus the isolated QSA capacity test: 140 additional passes.
Known limitations: QSA and CSA MalformedStateLengthsAreClamped tests still fail, including with specialized top-k disabled. These are not being claimed as passing. This is selected-suite validation, not a clean full-suite qualification.
Selected GenAI Qwen/executor/weight-pool tests: 69 passed, including the opt-in real-GPU mapped-weight test with proper CUDA plugin registration. One additional test is disabled.
The latest fused INT2 model completed one warmup plus three measured requests at each of 2K/8K/16K/32K, generating 512 tokens each. All 16 requests matched the previous-runtime token IDs exactly, with zero generation failures or standard fallbacks. Runtime-map inspection confirmed only the fresh ORT/GenAI libraries were used.
Prompt
Pooled decode TPS
Mean TTFT
2048
269.90
0.584 s
8192
250.17
2.448 s
16384
266.92
5.065 s
32768
264.12
10.578 s
Evidence scope: this is the full dirty experimental source stack containing this PR's optimizations plus other local work, not a standalone build/benchmark of this exact PR head. It is not an isolated speedup attribution, a fresh paired reference comparison, or proof of cross-process sustained 250 TPS. Full model accuracy qualification remains deferred.
The rebuild exposed local expert-cache function-pointer and INT2 direct-link test wiring issues, which were fixed locally. Those expert-cache extensions are not in the published PR and have deliberately not been mixed into this update.
…e seq_len==0 blocks in GatedDeltaNet
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tianlei Wu (tianleiwu)
changed the title
Extend GDN and causal convolution fused state handling
[CUDA] Extend GatedDeltaNet and CausalConvWithState for S<=8 decode and prefix states
Oct 24, 2026
…2 QMoE, and PR cleanups
- Extend FoldMid2/FoldTail to head_rows<=8 and pack tokens[4..7] into ring record words 1, 2, 3, and 5.
- Add state_update_capacity (0..8) support to CausalConvWithState and enable state_update_capacity > 0 in QwenS2 GatedDeltaNetDecodeWarpKernel.
- Raise kMaxProfiledExpandedRows to 80 in moe_gemv.h and kFusedRouteMaxRows to 8 in qmoe_fused_route.cuh; add fast INT3 and offline SM80 INT2/INT4 prepack QMoE support.
- Eliminate pq[8][ROWS] local register array in HcUpPreKernel and sync PR cleanups (#33469, #33470, #33471, #33472, #33474, #33548, #33569).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… GatedDeltaNet
- Remove unused first_state/second_state/third_state outputs from CausalConvWithState and name output 2 state_update (used when state_update_capacity > 0).
- Remove unused state_output attribute and mid_state/mid_state2/mid_state3 outputs from GatedDeltaNet, keeping compact state_update capture.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tianlei Wu (tianleiwu)
changed the title
[CUDA] Extend GatedDeltaNet and CausalConvWithState for S<=8 decode and prefix states
[CUDA] Extend GatedDeltaNet and CausalConvWithState for S<=8 speculative decode
Oct 25, 2026
…ync docs
- Use standard SelectPlan engine and BF16 precision selection for unpacked packed-QKV prefill so packed and unpacked QKV take the same chunked path at tokens >= 32 (fixing GatedDeltaNetTest.DirectPackedQkv{Float16,BFloat16}ExactParity).
- Sync docs/ContribOperators.md with gen_contrib_doc.py output for CausalConvWithState state_update.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…prefill
Add CausalConvProjectionTiledK4Kernel for length >= 16 when kernel_size == 4,
dilation == 1, and state_length == 4, reusing weights, bias, and the 3-element
input halo in registers across 8 consecutive token positions per CTA. Cuts
36-layer GDN CausalConvProjection prefill at L=2048 on H200 from 8.53 ms to
2.03 ms (4.10x faster).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Treat an empty trailing state_update output as omitted in shape inference
and the CPU, CUDA and WebGPU CausalConvWithState kernels.
- Remove the sequence-length veto that silently disabled uniform
GatedDeltaNet state capture when S > state_update_capacity + 1.
- Use 64-bit weight offsets in the extended CausalConv projection kernels.
- Add regression tests for all three.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The CUDA and WebGPU CausalConvWithState kernels called
info.node().OutputDefs() to detect the optional state_update output.
ep::adapter::Node (used by the CUDA plugin EP and WebGPU plugin/static
plugin builds) has no OutputDefs(), so those builds failed to compile.
Add causal_conv_with_state_helper::HasOutput, which uses the adapter's
OutputCount()/OutputExists() when available and falls back to
OutputDefs() for the in-tree Node, and use it in the CPU, CUDA and
WebGPU kernels.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The default-enabled packed QwenS2 specialization lacks compact-capture coverage: TestQwenS2PackedParity uses capacity zero, while the capture regression uses separate Q/K/V and different head counts. Add FP16/BF16 cases with S=8, capacity=7, and full, partial, and inactive capture to validate speculative rollback data. Compare defined captured prefixes against separate-QKV recurrent execution or independent prefix replay, without comparing the unspecified unused suffix.
…' only
The alias was introduced alongside 'softplus' in this PR. Use a single
spelling across the schema, CUDA/WebGPU kernels, docs and tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rows of the fixed-capacity state_update output beyond min(length, capacity)
were never written. Zero them with a graph-capturable strided memset in the
launcher. Document that ORT_CAUSAL_CONV_S2_SPECIALIZE also gates the tiled
length>=16 kernel and that the activation attribute is honored as configured.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…roing
Cover lengths 16/17/24 (CausalConvProjectionTiledK4Kernel) across both state
layouts, present/absent history, full and partial channel blocks, and
FP32/FP16/BF16, with positive state_update capacity in short-K4 and tiled
cases. Add a regression where capacity exceeds the sequence length.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Revert the alias removal: CUDA and WebGPU accept 'qwen' again with behavior
identical to 'softplus'. Schema and docs now name 'softplus' canonical and
'qwen' a legacy alias; error messages use the canonical spelling. Tests use
'softplus' and add alias coverage (CUDA bit-exact vs softplus, WebGPU ragged).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n-convolution
Adapt the split-engine exact-BF16 fallback added by #33523 to this branch's type-erased gate buffers. VariantPack::decay and beta are now const void*, so RunScanStepExactBf16Fallback must read them through LoadGate<T> rather than indexing them directly. The merge is textually clean but nvcc rejects it ("expression must be a pointer to a complete object type" in gated_delta_net_split_impl.cu), which failed every CUDA CI job that builds the merge ref.
Add a regression test that drives that fallback with float and input-type (BF16) gates and requires bit-identical results.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This branch has not been deployed
No deployments
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
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.
Description
Extends$S \le 8$ speculative decoding, packed-channel / packed-QKV projections, tiled $K=4$ prefill convolution, and compact
com.microsoft::CausalConvWithStateandcom.microsoft::GatedDeltaNeton the CUDA Execution Provider to supportstate_updatecapture (state_update_capacityin[0, 8]).Summary of Changes
com.microsoft::CausalConvWithStateExtended Projection, Tiled Prefill & Compactstate_updateonnxruntime/core/graph/contrib_ops/bert_defs.ccinput_channels,input_channel_offset,state_length,state_channels_last, andstate_update_capacity(0..8) attributes plus optionalOutput(2, "state_update")([B, state_update_capacity, C]), documenting that unused tail rows beyondmin(sequence_length, state_update_capacity)are zero-filledonnxruntime/contrib_ops/cuda/bert/causal_conv_with_state.{h,cc},causal_conv_with_state_impl.{h,cu}CausalConvProjectionS2K4Kernelfor short decode (kernel_size=4,state_length=4,dilation=1), (2)CausalConvProjectionTiledK4Kernel(8 positions per CTA with sliding registers) for prefill (cudaMemset2DAsynczero-fill for unusedstate_updaterows whenlength < state_update_capacityonnxruntime/contrib_ops/cpu/bert/causal_conv_with_state.{cc,helper.h},onnxruntime/contrib_ops/webgpu/bert/causal_conv_with_state.cccom.microsoft::GatedDeltaNetPacked QKV,TGGates &QwenS2Decodeonnxruntime/core/graph/contrib_ops/bert_defs.ccTGtype constraint sodecayandbetacan match the query element type (float,float16,bfloat16), and documentsgate_activation="softplus"with"qwen"retained as a legacy aliasonnxruntime/contrib_ops/cuda/bert/gated_delta_net.{h,cc},gated_delta_net_impl.{h,cu},gated_delta_net_split_impl.cu,gated_delta_net_mma.cuh,gated_delta_net_plan.hGateTfordecay/beta, compactstate_update_capacitycapture (0..8), and the 8-warpQwenS2recurrent decode specialization (ORT_GDN_S2_SPECIALIZE=1by default) for single-requestonnxruntime/contrib_ops/webgpu/bert/gated_delta_net.cc"softplus"and legacy alias"qwen"forgate_activationTests and Documentation
onnxruntime/test/contrib_ops/causal_conv_with_state_op_test.cc: Covers packed-channel extended history across lengths{1, 2, 3, 5, 8, 9, 16, 17, 24}(exercising bothCausalConvProjectionS2K4KernelandCausalConvProjectionTiledK4Kernel), full and partial channel blocks, both state layouts, present/absent past history,FP32/FP16/BF16,state_updatecapture verification, and tail zeroing whenstate_update_capacity > sequence_length.onnxruntime/test/contrib_ops/gated_delta_net_op_test.cc: Covers input-typedecay/beta(FP16/BF16),QwenS2(state_update_capacitycapture, empty requests in recurrent state, and bit-exact parity betweengate_activation="softplus"and"qwen"on CUDA and WebGPU.docs/ContribOperators.md,docs/OperatorKernels.md,docs/contrib_ops/cuda/gdn.md: Syncs operator schemas/kernel tables and documentsORT_GDN_S2_SPECIALIZE/ORT_CAUSAL_CONV_S2_SPECIALIZEeligibility and H200 op-level & end-to-end benchmarks.Performance
Measured on NVIDIA H200 (SM90, CUDA 13.0) at Qwen3.8-Flash-Next RC3 geometry (
(B, S, 10240)BF16,state_update_capacity=7):GatedDeltaNet(QwenS2):1.15x–1.25xfaster across18.82 usvs21.60 usat0.10–0.15 msper DFlash2CausalConvWithState:1.19x–1.37xfaster on short decode (4.06xfaster on prefill at55.13 usvs223.97 us), reducing end-to-end TTFT by2.9–3.1%at2K–32Kcontext lengths.Testing
onnxruntime_provider_test --gtest_filter="CausalConvWithStateTest.*:GatedDeltaNetTest.*:GatedDeltaNetWebGpuTest.*"qwen_flash_next_int4_int8_rc3_contrib_dflash2(state_update_capacity=7).Checklist