Skip to content

[CUDA] Optimize CUDA prefill and quantized paged attention - #34117

Merged
Tianlei Wu (tianleiwu) merged 19 commits into
mainfrom
kvaishnavi/mai-coder-dflash
Nov 13, 2026
Merged

Tianlei Wu (tianleiwu) merged 19 commits into
mainfrom
kvaishnavi/mai-coder-dflash

Conversation

@kunal-vaishnavi

@kunal-vaishnavi kunal-vaishnavi commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Description

  • Extend CUDA VarlenCausalConvWithState with dynamic grouped two-tap convolutions, including per-token coefficient deltas, boundary masking, and schema validation.
  • Add opt-in grouped BF16 INT3 MoE prefill, with a compatible SIMT path and guarded tensor-core acceleration using canonical recomputation for rounding-sensitive outputs.
  • Extend H128/GQA6 XQA support with BF16 INT8/INT4 speculative verification and BF16 INT4 single-token decoding.
  • Optimize INT8 KV prefill through sliding-window-aware gathering, vectorized dequantization, and revised FlashAttention query tiling.
  • Add BF16 shadow caching for global INT8 KV prefill. Retain a dequantized prefix across prefill chunks and convert only newly appended tokens, avoiding repeated full-prefix gathering. The optional prefill_cache_control input supplies an epoch, capacity, and final-chunk flag to explicitly govern reuse.
  • Add shadow-cache lifecycle safeguards: epoch invalidation, CUDA-stream synchronization, failure handling, and final-chunk buffer release. Missing controls, CUDA graph capture, multi-request batches, and unsupported configurations retain stateless behavior.
  • Expand correctness, dispatch, fallback, and cache-lifecycle coverage; update operator documentation and CUDA build dependencies.

Motivation and Context

There are existing bottlenecks in INT3 expert computation and repeated quantized-KV gathering, alongside gaps in BF16 quantized-cache speculative attention. These changes provide the CUDA primitives needed to reduce prefill overhead and support lower-memory speculative decoding.

Optimizations are restricted to eligible configurations, with grouped INT3 prefill remaining opt-in. Prefix reuse requires explicit caller-managed lifetimes and trades temporary BF16 workspace for reduced dequantization work. The companion GenAI changes supply the necessary graph lowering and lifecycle controls. Model weights are not requantized; quantized-KV accuracy still requires application-specific qualification.

Benchmarks

Notes:

  1. H200, batch 1, 512 output tokens, one warmup and five measured requests. The below benchmarks compare BF16 KV cache and INT8 KV cache versions of the RCs. TTFT and decode are medians; peak GPU memory includes weights and reserved pools, in decimal GB. INT8 KV cache versions use the optimized tile64 + shadow path; draft KV remains BF16 throughout.

  2. Memory usage is not a fair comparison between ORT GenAI and llama.cpp because ORT GenAI tries to capture as much GPU memory as it can for future conversations. Memory usage is simply noted here for recording purposes.

INT3 RC

Context BF16 TTFT (s) INT8 TTFT (s) BF16 Decode (tok/s) INT8 Decode (tok/s) BF16 Peak (GB) INT8 Peak (GB)
8K* 1.313 1.325 446.396 480.024 105.429 89.860
16K* 2.684 2.706 227.500 469.024 105.429 89.860
32K* 5.600 5.594 195.555 453.775 105.429 90.933
64K 12.157 12.041 302.338 428.886 105.429 92.141
128K 28.123 27.547 211.742 379.787 105.429 95.363

INT2 RC

Context BF16 TTFT (s) INT8 TTFT (s) BF16 Decode (tok/s) INT8 Decode (tok/s) BF16 Peak (GB) INT8 Peak (GB)
8K 1.236 1.225 280.496 420.968 84.122 68.622
16K 2.490 2.502 405.926 441.012 84.122 68.620
32K 5.184 5.174 357.272 429.573 84.122 69.763
64K 11.297 11.127 275.144 391.317 84.122 70.834
128K 26.356 25.621 197.655 346.274 84.122 74.056

llama.cpp (2-bit)

Prompt TTFT (s) Decode (tok/s) Peak GPU (GB)
8K 1.387 319.586 60.888
16K 2.839 330.673 61.250
32K 5.919 324.807 61.924
64K 13.035 317.357 63.268
128K 31.620 290.775 65.952

Append optional per-token grouped deltas and numeric previous-term masks. Preserve staged rounding, static behavior, raw state capture, and in-place state ownership. Reject the dynamic mode on WebGPU and update schema documentation and focused tests.

Validation: CUDA provider and operator test binary build successfully; three CPU schema tests pass. CUDA numerical equivalence, graph replay, legacy regressions, and benchmarks remain pending an isolated GPU.
Keep grouped BF16 block32 tensor-core prefill opt-in, guard rounding boundaries, and recompute ambiguous outputs with baseline arithmetic. Add exact-output and shadow checks and explicit CUDA header rebuild dependencies.

Validated on H200 with 11 tests and 306 subtests, full 512-token baseline comparisons across 8K-128K, and zero missed corrections in the qualified shadow checks. Guard windows remain empirical; no universal numerical guarantee or across-context 3.6x performance claim.
Add GQA6 XQA kernel specializations, dispatch, and resource checks while preserving unsupported-format fallbacks. Cover RC geometry, long contexts, graph replay, cache writes, scale modes, and fallback boundaries.
Skip invisible local-window prefix tiles and vectorize H128/GQA6 per-channel gathers while preserving dense offsets, FlashAttention arithmetic, cache writes, and decode dispatch.

Add default-on switches with scalar/full-prefix opt-outs, exact parity and dispatch tests, and operator documentation.

Validated on H200: 49 quantized tests (169 subtests), 36 native regressions, and 150 measured requests. All 50 INT8 A/B pairs preserve exact 512-token outputs and speculative counters.
Use regular FlashAttention 64-query/64-key tiles for eligible causal global and local INT8 prefill while retaining the existing key tiling and exact tested arithmetic.

Add optional CPU epoch/capacity/final-chunk control for bounded dequantized prefill shadows, append-only suffix conversion, and guarded stateless fallbacks. Preserve INT8 decode caches and dispatch.

Add operator documentation and exact parity, epoch/reset/release, invalid-control, fallback, and graph replay coverage. Existing H200 validation: 54 quantized tests (186 subtests), 36 native regressions, and 200 measured requests with 100 exact token/counter comparison pairs.
@kunal-vaishnavi kunal-vaishnavi changed the title [CUDA] Optimize MAI Code 1.1 Flash [CUDA] Optimize CUDA prefill and quantized paged attention Nov 11, 2026
# Conflicts:
#	onnxruntime/test/contrib_ops/causal_conv_with_state_op_test.cc

@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.

@kunal-vaishnavi
kunal-vaishnavi marked this pull request as ready for review November 12, 2026 01:53
Copilot AI balanced review requested due to automatic review settings November 12, 2026 01:53

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

Unresolved critical issues can cause CUDA misaligned-address failures for valid INT8 cache bindings and silently incorrect CPU results when dynamic convolution inputs are ignored.

5 open issues
What changed in this PR

This PR extends ONNX Runtime’s CUDA inference paths to reduce quantized prefill overhead and support additional BF16 paged-attention configurations.

Changes:

  • Adds dynamic grouped two-tap convolution and opt-in grouped INT3 MoE prefill with guarded tensor-core acceleration.
  • Extends BF16 INT8/INT4 XQA support and optimizes INT8 prefill gathering, query tiling, and controlled prefix reuse.
  • Expands correctness, dispatch, fallback, and lifecycle tests, alongside operator documentation and CUDA build dependencies.
File Description
onnxruntime/​test/​python/​transformers/​test_qmoe_shared_direct.py Tests grouped INT3 prefill and tensor-core compatibility.
onnxruntime/​test/​python/​transformers/​test_paged_attention_int4.py Tests quantized attention dispatch, parity, and shadow-cache lifecycle.
onnxruntime/​test/​contrib_ops/​cuda_kernels/​softmax_topk_kernel_test.cc Extends grouped-kernel selection coverage.
onnxruntime/​test/​contrib_ops/​causal_conv_with_state_op_test.cc Tests dynamic convolution validation, arithmetic, and state aliasing.
onnxruntime/​core/​graph/​contrib_ops/​bert_defs.cc Extends convolution and paged-attention schemas.
onnxruntime/​contrib_ops/​webgpu/​bert/​varlen_causal_conv_with_state.cc Rejects CUDA-only dynamic convolution inputs.
onnxruntime/​contrib_ops/​cuda/​moe/​qmoe_kernels.h Adds grouped-prefill and compatibility parameters.
onnxruntime/​contrib_ops/​cuda/​moe/​qmoe_kernels.cu Optimizes grouped kernels and shared-memory dispatch.
onnxruntime/​contrib_ops/​cuda/​moe/​qmoe_int3_tc.cuh Adds guarded tensor-core computation and canonical recomputation.
onnxruntime/​contrib_ops/​cuda/​moe/​moe_quantization.cc Integrates opt-in grouped prefill and workspace budgeting.
onnxruntime/​contrib_ops/​cuda/​bert/​xqa/​xqa_paged_spec_dec_bf16_int8_128.cu Adds BF16 H128 INT8 speculative specialization.
onnxruntime/​contrib_ops/​cuda/​bert/​xqa/​xqa_paged_spec_dec_bf16_int4_128.cu Adds BF16 H128 INT4 speculative specialization.
onnxruntime/​contrib_ops/​cuda/​bert/​xqa/​xqa_paged_loader.cu Extends XQA dispatch and resource queries.
onnxruntime/​contrib_ops/​cuda/​bert/​xqa/​xqa_paged_bf16_int4_128.cu Adds BF16 H128 INT4 single-token specialization.
onnxruntime/​contrib_ops/​cuda/​bert/​varlen_causal_conv_with_state.h Declares dynamic convolution parameters.
onnxruntime/​contrib_ops/​cuda/​bert/​varlen_causal_conv_with_state.cc Validates and forwards dynamic convolution inputs.
onnxruntime/​contrib_ops/​cuda/​bert/​varlen_causal_conv_with_state_impl.cu Implements dynamic grouped convolution.
onnxruntime/​contrib_ops/​cuda/​bert/​paged_attention.h Adds optimization flags and synchronized shadow state.
onnxruntime/​contrib_ops/​cuda/​bert/​paged_attention.cc Integrates quantized dispatch and prefix-cache lifecycle controls.
onnxruntime/​contrib_ops/​cuda/​bert/​paged_attention_impl.cu Adds window-aware and vectorized INT8 gathering.
onnxruntime/​contrib_ops/​cuda/​bert/​flash_attention/​flash_fwd_hdim128_bf16_causal_sm80.cu Adds the 64-query-row FlashAttention kernel.
onnxruntime/​contrib_ops/​cuda/​bert/​flash_attention/​flash_api.h Exposes optional tile64 selection.
onnxruntime/​contrib_ops/​cuda/​bert/​flash_attention/​flash_api.cc Validates and dispatches tile64 attention.
onnxruntime/​contrib_ops/​cuda/​bert/​attention_kernel_options.h Declares prefill-aware debug reporting.
onnxruntime/​contrib_ops/​cuda/​bert/​attention_kernel_options.cc Identifies optimized INT8 prefill in telemetry.
onnxruntime/​contrib_ops/​cuda/​bert/​attention_data.h Carries gather, tiling, and prefix-offset settings.
docs/​OperatorKernels.md Updates convolution input listings.
docs/​ContribOperators.md Updates dynamic convolution attributes and input listings.
docs/​contrib_ops/​cuda/​paged_attention.md Documents quantized paths and shadow-cache lifetime requirements.
cmake/​onnxruntime_providers_cuda.cmake Adds explicit MoE header dependencies for incremental builds.

Review effort: Balanced

Comment thread docs/ContribOperators.md Resolved

@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.

Thanks for the detailed docs and the strong exactness coverage on the INT8 gather paths. I reviewed this for benefit per feature, default-path impact, backward compatibility, GPU generality, and precision. I also ran a couple of micro-benchmarks on an A100 (sm80) to check behavior off H200.

Per-feature benefit

Feature Default Evidence Assessment
Window-aware + vectorized INT8 gather on §8.10: 128K gather 3.01 s → 0.71 s Clear win. Gating is hard-coded to H128 / kv_heads=8 / GQA6 (see inline)
H128/GQA6 BF16 INT8/INT4 XQA (decode + spec-dec) on for matching shapes decode table Plausible. The table is confounded by speculative acceptance: BF16 decode is non-monotonic in context (446→227→195→302→211 tok/s). Please add verify-step latency or acceptance rate
64-row FA tile (ORT_ENABLE_PAGED_INT8_TILED_PREFILL) on, all GPUs not isolated; H200 only GPU-specific. A100 global 8K chunks are 3–7% slower. On sm86/sm89 it changes the local-window kernel and is not bit-exact (inline)
BF16 prefill shadow (prefill_cache_control) graph opt-in, env default on not isolated Questionable. Upper bound ≈0.71 s of 27.5 s TTFT at 128K vs ~3.76 GB retained, plus a stateful kernel (inline)
Grouped INT3 MoE prefill + TC compatibility repair env opt-in none in PR Please share prefill numbers to justify ~400 lines of kernel code and 5 env knobs
Dynamic grouped two-tap conv graph opt-in functional enabler Needed, but the prefill kernel is serial per channel: 12–14x slower than token-parallel (inline)

Default-path impact

  • Without prefill_cache_control, the shadow adds no allocation, lock, or work. Grouped INT3 prefill is env-gated. Dynamic conv only branches on kernel_delta. 👍
  • Default-on changes: windowed gather, vectorized gather, and tile64. The first two are bit-exact and tested. tile64 is the risky one (inline).
  • qmoe_kernels.cu relaxes grouped-kernel eligibility for all callers, including the non-opt-in qmoe_offload.cc path (inline).
  • The INT3 env vars are parsed on every ComputeInt3 call (inline).

Backward compatibility

  • New inputs are optional trailing inputs and new attributes have defaults, so existing models are unaffected. 👍
  • prefill_cache_control is a pure hint; other EPs and older graphs keep stateless behavior. 👍
  • Main gap: the CPU VarlenCausalConvWithState kernel silently ignores kernel_delta/previous_mask. I agree with the existing open thread. OperatorKernels.md now advertises those inputs for CPU too.
  • Design concern: prefill_cache_control makes correctness depend on the caller renewing epochs whenever cache bytes or page mappings change. A stale epoch silently returns attention over stale dequantized K/V. That is a new class of contract for an ORT kernel.

GPU generality

Most fast paths are keyed to a single model geometry: H128 with kv_heads=8 and GQA6 (kHiddenSize = 8 * 128), INT3 block 32, and group-6-only XQA. The window skipping and the 8-wide vectorization are generic and would benefit every INT8 GQA model if gated on head_size % 8 == 0 and alignment instead. tile64 and the TC-repair ULP bound were tuned or validated on one GPU. The shadow's ~3.76 GB at 128K is prohibitive on 12–32 GB consumer GPUs.

Precision

  • INT8 gather, vectorized gather, shadow, and tile64 on sm80/sm90 use the same arithmetic (bit-exact). 👍
  • The INT3 SIMT grouped path is bit-exact vs the existing SIMT path, and the test asserts equality. 👍 TC repair is heuristic and arch-dependent (inline).
  • The dynamic conv spec makes four intermediate roundings to T normative (inline).
  • BF16 H128 XQA reuses the fold design. The PER_CHANNEL K scale goes into a BF16 copy of Q, and the V scale is applied to the BF16 output in a second pass. That adds roughly one extra BF16 ULP vs the portable FP32 kernel. The current tolerances cover it. Applying the V scale in the XQA epilogue in FP32 would remove one rounding.

Also agree with the open threads

The INT8 cache uint2 alignment issue (I replied with a shadow-specific caveat), the CPU kernel ignoring the dynamic inputs, the empty-query shadow release, and TensorCore=1 test gating on pre-SM80 devices.

Nits

  • Test names embed an internal codename (rc14, "RC14 four-row ..."). Shape-based names (e.g. h128_gqa6) would age better.
  • The OBJECT_DEPENDS added in onnxruntime_providers_cuda.cmake should be unnecessary, since nvcc depfiles already track these includes. If it works around a specific generator issue, a comment would help.

Requesting changes mainly for: tile64 arch gating plus the sm86/sm89 exactness break, the shadow's cross-run arena ownership, and the serial dynamic-conv prefill kernel. Splitting the shadow cache and the INT3 grouped prefill into follow-up PRs with ablation data would also make this much easier to land.

Recognize SM121 in the SM120 kernel family and add a native CUDA plug-in registration smoke utility. Add guarded compact-group scale handling and BF16 group-64 small-matrix tiling controls with regression coverage.

Compact-group expansion and non-default tiling remain experimental and disabled by default; neither is promoted by the RC performance results. Validated with focused ARM64 provider builds, native output comparisons, and CPU scale-equivalence checks.
…o kvaishnavi/mai-coder-dflash

# Conflicts:
#	cmake/external/cuda_configuration.cmake

@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.

Remove compact-group scale expansion and the BF16 group-64 small-matrix row/column tile overrides, along with their controls, helper header, and experiment-specific tests. Restore the normal fixed dispatch and original layout tests while retaining checked scale allocation and the BF16 GEMV group-size safety guard.

@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.

Expose six session keys for INT8 shadow reuse and INT3 QMoE tuning. Resolve controls during kernel construction with session values overriding the environment, and validate boolean and floating-point values.

Keep eligible INT8 prefill gathering and tiling automatic without switches. Default shadow reuse off, update parity and configuration tests, and document GenAI session configuration.

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.

Reviewed the current 41-file change at 4751cc1, including attention dispatch/gather/shadow lifetime, BF16 INT4/INT8 XQA wiring, dynamic convolution/schema/provider behavior, grouped INT3 MoE packing and rounding, and related build/test changes.

No additional distinct actionable bugs were substantiated beyond the existing review findings. Instead of opening duplicate threads, I added source-level corroboration and concrete regression guidance inline for:

These correctness issues should be addressed before merging.

Validation: the documentation test file passed all 20 tests and 13 subtests. Host-side checks covered packing/workspace offsets, BF16 rounding, address alignment, and source-level lifetime/provider behavior. CUDA tooling/GPU execution was unavailable; the installed local CPU wheel also lacks this convolution kernel, so this is not a claim of full CUDA or expanded-schema CPU runtime validation. The observed React Native iOS CI failure occurred during npm dependency bootstrap with HTTP 504, before application tests.

Allow explicit CUDA session settings to override the XQA environment switches for PagedAttention. Add dispatch and validation regressions and document the GenAI configuration keys. Verified with nine focused tests and a Release compile probe.

@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.

Thanks for the quick iteration. I re-reviewed at 47a5a0c (7 commits since my previous review at 9d05498).

Addressed

  • The BF16 prefill shadow and prefill_cache_control are removed cleanly. That resolves the arena-ownership and benefit/cost threads.
  • The QMoE INT3 knobs and the PagedAttention XQA switches are resolved once in the constructor, with strict validation.
  • The tile64 test now checks against the numerical reference.
  • ContribOperators.md keeps the schema descriptions.
  • The bot threads on empty-query shadow release and docs regeneration are now moot or addressed and can be resolved.

Blocking

  1. MatMulNBits BF16 INT4 GEMV guard (new, inline). This is a default-path change outside the PR's scope. In the default compact build it disables GEMV for every eligible BF16 INT4 node.
  2. tile64 has no opt-out (reply on the existing thread). d4a1cd1 removed the env switch, so it is on for every SM but was measured only on H200.
  3. Still open from round 1 (existing threads):
    • The CPU VarlenCausalConvWithState kernel silently ignores kernel_delta/previous_mask, while OperatorKernels.md lists them under CPU.
    • The vectorized INT8 gather issues uint2 loads without checking cache-pointer alignment.

Should fix or track as follow-ups (existing threads unless noted)

  • Dynamic conv prefill is serial per channel.
  • The TensorCore=1 assertions fail when the TC path correctly falls back.
  • Grouped QMoE eligibility is widened for the offload path.
  • The TC-compatibility ULP bound is arch-dependent.
  • The kInt8PrefillTileAlignment/kBlockN invariant, the hard-coded kHiddenSize, and the duplicated FP16 tile64 TU are unchanged.
  • New inline suggestions:
    • size the vectorized gather's grid per row
    • keep the diagnostic QMoE knobs env-only
    • drop the Windows-only plugin smoke tool
    • restore the deleted needs_dense_kv comment
  • There are still no prefill numbers for the opt-in INT3 grouped path (SIMT grouped vs. TC+repair vs. the default TC prefill).

PR description

  • Bullets 5-6 still describe the shadow cache.
  • The benchmark notes say the INT8 runs used "tile64 + shadow", so the 64K/128K INT8 TTFT rows may not reflect this head.
  • The description doesn't mention:
    • the MatMulNBits guard
    • the 7 new public ep.cuda.* session keys
    • the offload grouped-eligibility change
    • the plugin smoke tool
    • the gen_contrib_doc.py check

Please update the description and re-run the INT8 TTFT rows.

Reject unsupported CPU dynamic convolution, guard and generalize INT8 gather, preserve architecture-specific tile/GEMV choices, parallelize long dynamic convolution without changing rounding, and retain legacy offload eligibility. Clarify experimental compatibility controls and regenerate docs. Local CUDA and C++ regressions pass; full quantlm before/after runs preserve all tokens and counters with timing changes below 0.3%. SM121 device compilation is verified, not hardware performance qualification.
@kunal-vaishnavi

Copy link
Copy Markdown
Contributor Author

From Copilot:

Description

  • Extend CUDA VarlenCausalConvWithState with dynamic grouped two-tap convolution. Preserve the eager FP16/BF16 per-operation rounding contract, numeric boundary masks, capture state, and in-place continuation. Retain the short/decode kernel and use an alias-safe prefix/state launch plus token-parallel body for long sequences. CPU and WebGPU reject unsupported dynamic mode explicitly.
  • Add opt-in grouped BF16 INT3 MoE prefill, canonical SIMT computation, and experimental TensorCore compatibility repair. Keep the existing package-calibrated per-model presets. Diagnostic tolerances/verification are internal experimental overrides rather than permanent public session-key declarations; repair is best-effort across GPU architectures, not an unconditional exactness guarantee.
  • Extend H128/GQA6 XQA support with BF16 INT8/INT4 speculative verification and BF16 INT4 single-token decode. Explicit session switches override the environment; legacy nonzero-integer XQA environment values retain their old meaning.
  • Generalize window-aware/vectorized INT8 gather beyond the original geometry. Require both cache pointers to be aligned, retain scalar fallback for unaligned bindings, preserve the original H128/eight-KV-head specialization, size vector grids per sequence, and enforce the gather/key-tile alignment invariant at compile time.
  • Scope the 64-query Flash tile to SM90 and SM121, preserving both H200 and RTX Spark tuning. Other architectures retain their default Flash dispatch. FP16/BF16 tile entries are compiled in their respective dtype translation units. Exact-order claims are architecture-qualified, not extended to sm86/sm89 local-window defaults.
  • Restore normal BF16 INT4 GEMV eligibility outside SM121 while retaining the Spark-specific selection guard. Preserve legacy offload grouped-workspace eligibility; compact workspace accounting is opt-in.
  • Remove the BF16 prefill shadow and prefill_cache_control contract. Remove the unrelated Windows-only smoke utility. Regenerate contrib schema docs with descriptions preserved and verify affected CPU/CUDA kernel-table sections.

Performance Preservation

The tuning boundaries are explicit: commits through 9d05498ef006d27ccacc6a13ab88b72c7d47656c are the H200 baseline; ef4575a00777b2eb286754bac859c0ce94a532a4 and subsequent tuning commits are the RTX Spark baseline. Architecture-policy tests preserve the distinct selections. Model graphs, weights, package configuration, and calibrated presets were not changed by this review follow-up.

The following is a before/after comparison of the review follow-up on H200/SM90, not a BF16-versus-INT8 cache comparison. Both quantized-LM packages use INT8 target KV, BF16 draft KV, and their existing quantized heads. Batch 1; summary prompts; 512 output tokens; one warmup and three measured requests per case; identical GPU, package hashes, and presets. TTFT and decode values are medians. All 30 paired measured generations have exact token and speculative-counter parity, so acceptance changes do not explain the timings.

Package Context TTFT Before (s) TTFT After (s) Decode Before (tok/s) Decode After (tok/s)
INT3 8K 1.023 1.024 508.14 507.59
INT3 16K 2.092 2.092 541.08 540.09
INT3 32K 4.352 4.360 542.60 541.15
INT3 64K 9.413 9.422 502.63 503.08
INT3 128K 21.640 21.690 433.19 433.66
W2/W4 8K 0.594 0.594 439.77 438.89
W2/W4 16K 1.227 1.229 345.89 345.62
W2/W4 32K 2.616 2.612 513.55 513.58
W2/W4 64K 5.918 5.909 474.56 474.70
W2/W4 128K 14.672 14.675 402.94 403.33

Absolute TTFT and decode changes are below 0.3% on these runs. This preserves the measured H200 behavior; it is not a new speedup claim or model-quality qualification.

Grouped-Prefill Ablation

H200/SM90, BF16, hidden 1536, intermediate 2688, two experts, top-k 2, block size 32. GPU-resident whole-op wall latency includes casts/routing; prepacking is disabled. Seven samples per cell; medians below in milliseconds. Dispatch was checked separately from timed runs. These are fixture measurements, not model throughput.

Rows Canonical SIMT Default TC Prefill Grouped SIMT TC + Compatibility Repair
32 0.1623 0.1123 0.1915 0.1063
256 1.4968 0.1538 1.2314 0.2910
2048 11.7339 0.5165 9.4617 1.8857

Grouped SIMT and TC+repair match the canonical output exactly on these fixtures. Default TC is faster for larger rows but numerically different (relative error about 0.0031-0.0040), so it is not reported as canonical parity. The repair mode uses the package-calibrated 1.5/4 tolerances; its extra absolute-value MMA, workspace clear, and repair launch costs are documented. It remains opt-in and requires application/device-specific qualification.

Validation and Limits

  • Local H200 CUDA suites: 68 tests passed, 533 subtests passed. The two skips are optional candidate-bank slicing and CUPTI profiling, not the numerical paths above. Supported-device TC dispatch and zero missed repairs were explicitly checked.
  • Focused C++ regressions: 21 passed, including CPU rejection, exact long-prefill output/state/capture, aliased continuation, legacy offload eligibility, and SM90/SM121 policy matrices.
  • Changed gather, QMoE, dynamic-convolution, and FP16/BF16 tile64 sources emitted SM121 device images. No SM121 device was available for execution or timing; H200 results do not establish Spark numerical/performance qualification.
  • Local all-files lintrunner passed. Contrib docs were regenerated with full schema strings; affected CPU/CUDA kernel-table sections match the current registrations.

Lossy KV quantization and experimental TC compatibility still need separate model-quality qualification. The removed shadow path is not part of any current result above.

@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.

Thanks for the thorough follow-up. I re-reviewed at b3be80f and checked each resolved thread against the code, not only the replies.

Verified fixed

  • tile64: gated to SM90/SM121 via UseInt8PrefillTile64ForDevice; other architectures keep their default Flash dispatch. §8.11/§8.12 no longer claim universal exact order.
  • Gather/tile invariant: GetPagedKvGatherAlignment plus the static_assert in run_flash_fwd cover every regular kernel. The gather's window start (local_window_size - 1) matches the window_size_left passed to mha_varlen_fwd, so Flash never reads a skipped tile. The split-KV traits (kBlockN 128/256) aren't covered by the assert, but they can't be reached here because varlen split-KV requires pure decode.
  • Vectorized INT8 gather: requires 8-byte-aligned K and V base pointers. With head_size % 8 == 0, every uint2 load and uint4 store is then aligned. The generic path matches the scalar gather bit-for-bit (dequant, page indexing, missing pages), and the grid is sized per sequence from max_kv_len. The offset-binding tests cover this well.
  • Dynamic conv: the prefix plus token-parallel body matches the serial kernel term for term (taps, group, delta indices, mask, capture positions, final_state = input[end - 1]). It is alias-safe: only MayInplace(4, 1) is declared, the prefix reads initial_state before writing final_state, and the body never reads state.
  • CPU: rejects the dynamic attributes at construction and inputs 6/7 in Compute. Input<Tensor>() returns null for an out-of-range index, so six-input nodes are unaffected.
  • QMoE: legacy grouped eligibility keeps the float FC2 accounting. Compact accounting applies only to the opt-in path, where it matches the kernel's sizeof(T) staging; that staging is numerically neutral. The diagnostic knobs are internal-only, and the ablation table answers the benefit question.
  • XQA env fallback accepts legacy nonzero values, TC tests tolerate the SIMT fallback, and the smoke tool is removed. The dtype-split tile64 TUs, copyright headers, and needs_dense_kv comment are all in.

Remaining (non-blocking)

  • SM121 BF16 INT4 GEMV guard (inline): please add the rationale. In the default compact build the block_size == 64 exception can't apply, so the guard turns off BF16 INT4 GEMV on SM121 for every eligible node.
  • Inline nits: the opt-in TC canary in the QMoE test, and the ef4575a hash in the docs.
  • Carried-over nits:
    • RC14 remains in paged_attention.md §8.10, DynamicBFloat16RC14, and the "RC14 four-row grouped prefill" selector case.
    • The OBJECT_DEPENDS addition in onnxruntime_providers_cuda.cmake still has no comment explaining why nvcc depfiles aren't enough.

The blocking items from my previous reviews are resolved. CI was still running at review time (27 pass, 55 pending, none failed).

@tianleiwu
Tianlei Wu (tianleiwu) enabled auto-merge (squash) November 13, 2026 03:44
@tianleiwu
Tianlei Wu (tianleiwu) merged commit fca9447 into main Nov 13, 2026
111 of 112 checks passed
@tianleiwu
Tianlei Wu (tianleiwu) deleted the kvaishnavi/mai-coder-dflash branch November 13, 2026 05:35
Tianlei Wu (tianleiwu) added a commit that referenced this pull request Nov 13, 2026
This cherry-picks the following commits for the release:

| Commit ID | PR Number | Commit Title |
|-----------|-----------|-------------|
| 85dcb54 | #32475 | Update Eigen to 5.0.1 + SSE2 fix (broken Clip &
Sign) |
| 1ea552d | #33976 | [CUDA/CPU] Add hybrid GPU/CPU expert offload for
MoEPacked block-packed weights |
| 3a8b8f2 | #34066 | [CUDA] Optimize multi-token MatMulPacked GGUF
GEMV (M=2..8) and eliminate ReadU32Dev warp divergence |
| 64506ea | #34098 | [CUDA] Guard tests requiring SM80 on older GPUs
|
| 74f861c | #34067 | [MoE] Optimize multi-token (M=4, 8) MoEPacked
CPU hybrid cold routes and GPU FC2 decode |
| 6aa9b27 | #33963 | [CUDA] Extend MoEPacked for top-k, dispatch and
fused MoE calculation |
| 8fb1b27 | #33969 | [CUDA] Add Mamba2Verify CUDA Kernel |
| cedae42 | #33994 | [CUDA] Update `MoEGroupRouter` for routing to
`MoEPacked` |
| 158d0c0 | #34002 | [CUDA] Speed up native Bolt packed projections
and IQ2 experts |
| 38c6dce | #34003 | [CUDA] Reduce native packed decode tile and FC2
loader overhead |
| d5704db | #34147 | [CUDA] Optimize MoEPacked, MoEGroupRouter, and
MatMulPacked for Nemotron Bolt |
| 7cca0e5 | #34126 | Fix Windows junction detection in model_package
for non-MSVC standard libraries |
| 2bf400a | #34099 | Fix external data validation for Hugging Face
Hub shared blob store |
| d2f77cd | #34164 | [CUDA] Accelerate Nemotron Bolt large-chunk
prefill across contrib ops |
| ca7ea48 | #28457 | Add set_default_logger_callback Python API for
custom ORT logging |
| fca9447 | #34117 | [CUDA] Optimize CUDA prefill and quantized paged
attention |
| 1804c6f | #34114| [CUDA] Optimize
MoEPacked GGUF offload 3-stage prefill pipeline and F16C/Batch4/40-core
cold decode |

---------

Signed-off-by: Christian Bourjau <christian.bourjau@quantco.com>
Signed-off-by: rui-ren <ruiren1225@gmail.com>
Co-authored-by: Christian Bourjau <cbourjau@users.noreply.github.com>
Co-authored-by: BBaditi <bbaditi@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Rui Ren <ruiren1225@gmail.com>
Co-authored-by: Mustapha Jaber <mustjab@users.noreply.github.com>
Co-authored-by: Mustapha Jaber <mustjab+microsoft@microsoft.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Copilot-Session: f4d0e1a3-a389-4cc8-9c85-0fd0e3ffce5b
Copilot-Session: c8741c5a-3e9e-4860-b846-7d6fb0025040
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants