Skip to content

[WebGPU] Unroll bounded NVIDIA FlashAttention reductions in WGSL - #33395

Open
Yang Gu (gyagp) wants to merge 3 commits into
microsoft:mainfrom
gyagp:codex/webgpu-nvidia-flash-unroll-20261015
Open

Yang Gu (gyagp) wants to merge 3 commits into
microsoft:mainfrom
gyagp:codex/webgpu-nvidia-flash-unroll-20261015

Conversation

@gyagp

@gyagp Yang Gu (gyagp) commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Change

Unroll the QK/PV reductions in NVIDIA's dense shared-memory FlashAttention prefill path for head sizes up to 128. Preserve FP32 score accumulation, accumulation order and cache specialization.

Selection: is_nvidia_ && use_shm_path_ && qkv_head_size_ / 4 <= 32. Larger heads retain rolled reductions; removing the cap regressed the isolated head-size-512 kernel substantially. The implementation has no shader-model gate.

Aion performance

Aion-1.1-Instruct, RTX 5080 / NVIDIA 617.42, verified D3D12 SM 6.10, Agility SDK 721 and compatible DXC dc3e6c48. ORT main 1e4bfba8eeda255d97ffeade88616cd5f06ed777 plus this change; GenAI 743cc24c739cd19aa953e9d1ea4bc2cd8495b07e. The model uses 28 GroupQueryAttention layers, head size 128 and a 2048-token local attention window. No named adapter was loaded.

Median prefill tokens/s over three alternating process rounds, one warmup and five measured repetitions per process. Batch 1, 1024 default input tokens, 32 generated tokens, maximum length 8192, graph capture and generator reuse. Binary-identical off/on variants use a test-only unroll switch and identical fixed-seed input IDs. Timing excludes initialization, profiling and shader logging.

Input Mode D3D12 before D3D12 after Change Vulkan before Vulkan after Change
1024 DP4A 6,506.29 7,688.99 +18.18% 8,454.67 8,452.34 -0.03%
1024 Subgroup Matrix 10,120.60 13,293.90 +31.35% 13,358.20 13,331.80 -0.20%
512 DP4A 6,339.39 7,123.81 +12.37% 7,746.84 7,730.22 -0.21%
512 Subgroup Matrix 8,942.17 10,608.80 +18.64% 10,317.40 10,323.20 +0.06%
2048 DP4A 5,451.56 7,334.27 +34.54% 8,136.46 8,126.96 -0.12%
2048 Subgroup Matrix 7,794.68 12,386.80 +58.91% 12,848.70 12,853.60 +0.04%
4096 DP4A 4,648.10 6,917.43 +48.82% 7,655.92 7,646.72 -0.12%
4096 Subgroup Matrix 6,225.58 11,211.10 +80.08% 11,628.30 11,631.80 +0.03%

DP4A/Subgroup Matrix describe the model GEMMs; the optimized attention kernel uses shared memory in both modes. Subgroup Matrix support is not required for this optimization. SM denotes the applied DirectX shader target, not CUDA compute capability. Aion was tested on SM 6.10 only; SM 6.8/6.9 and other GPUs were not measured. Vulkan shows no consistent improvement.

Both variants include the same existing Dawn/DXC compatibility prerequisites: the documented HLSL workgroup-bitcast and SPIR-V initialization fixes, compatible DXC and Agility integration. No additional ORT precision patches were included in these timings.

Validation

  • 89 WGSL template tests and six native high-magnitude test executions pass. Head-size coverage is 64/128/192/256/512.
  • All 32 Aion numerical cases using the exact benchmark prompts pass, including finite logits, changed-input checks and exact graph replay.
  • All 144 paired saved output/token files match byte-for-byte between unroll off/on. Minimum cross-mode/backend logit cosine is 0.999563.
  • Independent CPU-reference checks on the actual 1024-token benchmark prompt pass, with minimum cosine 0.999720.
  • Shader and dispatch captures verify the NVIDIA shared-memory attention path, requested DP4A/Subgroup Matrix GEMMs and graph replay.

A separate repeated-token stress fixture exposes a pre-existing Aion accuracy gap (DP4A vs Subgroup Matrix cosine 0.89272; CPU vs Subgroup Matrix 0.98395 and CPU vs DP4A 0.93533). All 36 additional stress output/token pairs are byte-identical with unroll off/on. This gap remains unresolved; diagnostic precision patches were excluded from accepted timings. The validation above establishes preservation and agreement on the measured workload, not arbitrary-prompt model quality.

@azure-pipelines

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

@gyagp
Yang Gu (gyagp) force-pushed the codex/webgpu-nvidia-flash-unroll-20261015 branch from fe63446 to 2648fb0 Compare October 16, 2026 03:36
@gyagp
Yang Gu (gyagp) marked this pull request as ready for review October 16, 2026 03:36
@gyagp

Copy link
Copy Markdown
Contributor Author

/run bots

@qjia7

Copy link
Copy Markdown
Contributor

The request was received. Approve workflow runs. Thanks.

@gyagp
Yang Gu (gyagp) force-pushed the codex/webgpu-nvidia-flash-unroll-20261015 branch from 2648fb0 to f041c21 Compare October 16, 2026 05:02
@gyagp

Copy link
Copy Markdown
Contributor Author

/run bots

@gyagp

Copy link
Copy Markdown
Contributor Author

Jiajia Qin (@qjia7) Please review

@qjia7

Copy link
Copy Markdown
Contributor

The request was received. Approve workflow runs. Thanks.

// The NVIDIA shared-memory path benefits from constant private-array indices.
// Bound source expansion to at most 32 vec4 terms per reduction. Both inputs
// to this choice already participate in the dense-prefill program cache hint.
const bool unroll_shm_inner = is_nvidia_ && qkv_head_size_ / 4 <= 32;

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
const bool unroll_shm_inner = is_nvidia_ && qkv_head_size_ / 4 <= 32;
const bool unroll_shm_inner = is_nvidia_ && use_shm_path_;

Please also check whether we can remove qkv_head_size_ / 4 <= 32 for unroll_shm_inner.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the explicit use_shm_path_ guard and checked removal of the head-size limit on current main f42c839f56.

I retained the limit because expanding head size 512 causes substantial regressions on RTX 5080/NVIDIA 617.42. At 1024 tokens (24 Q heads/8 KV heads, causal FP16), isolated GPU time changes as follows; positive percentages mean slower:

Target Head size Rolled GPU time (ms) Expanded GPU time (ms) GPU time change
D3D12 / SM 6.10 128 0.850 0.748 -11.99%
D3D12 / SM 6.10 192 1.384 1.372 -0.86%
D3D12 / SM 6.10 256 1.943 1.944 +0.05%
D3D12 / SM 6.10 512 7.812 17.273 +121.10%
Vulkan 128 0.712 0.712 -0.12%
Vulkan 192 1.088 1.085 -0.25%
Vulkan 256 2.285 2.288 +0.16%
Vulkan 512 9.521 25.782 +170.79%
D3D12 / SM 6.8 128 1.309 1.375 +5.05%
D3D12 / SM 6.8 192 3.332 3.333 +0.01%
D3D12 / SM 6.8 256 2.521 2.522 +0.01%
D3D12 / SM 6.8 512 18.507 18.493 -0.08%
D3D12 / SM 6.9 128 0.850 0.748 -12.02%
D3D12 / SM 6.9 192 1.384 1.371 -0.93%
D3D12 / SM 6.9 256 1.940 1.943 +0.14%
D3D12 / SM 6.9 512 7.815 17.300 +121.37%

Three alternating process rounds per measurement, identical inputs. D3D12 uses native DXIL GPU-timestamp replay; Vulkan uses native Dawn GPU-timestamp replay linked to the same Dawn build. Their sampling harnesses differ, so the comparisons above are within each API. This is kernel timing rather than full-model throughput. The 512/2048-token checks also show a head size 512 regression.

The final selection is is_nvidia_ && use_shm_path_ && qkv_head_size_ / 4 <= 32. I expanded the 65-token high-magnitude test to heads 64/128/192/256/512 and verified actual shaders:64/128 expand,192/256/512 retain loops. All three native tests, 89 template tests and 48 shader compilations pass. All 64 shader numerical cases pass. Current-main Phi-4 graph capture also passes at 1024 with Subgroup Matrix on both APIs, including exact A/B/A replay and finite/reference-checked logits.

I also refreshed the PR description with the dated October 29 full-model A/B, including Subgroup Matrix. The known SM 6.8 tradeoff and limited architecture coverage remain disclosed.

@gyagp
Yang Gu (gyagp) force-pushed the codex/webgpu-nvidia-flash-unroll-20261015 branch from f041c21 to a33792f Compare November 3, 2026 01:55
Copilot AI balanced review requested due to automatic review settings November 3, 2026 01:55

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.

🔵 Needs a closer look

Maintainer judgment is needed on NVIDIA-wide unrolling because reported SM 6.8 regressions and limited architecture coverage leave the cross-target performance tradeoff unresolved.

No open issues 🎉

What changed in this PR

Optimizes WebGPU FlashAttention prefill on NVIDIA by unrolling shared-memory reductions for head sizes up to 128, while preserving accumulation precision and order. Larger heads and other attention paths retain their existing behavior.

Changes:

  • Generates bounded, unrolled QK and PV reduction helpers for eligible NVIDIA shaders.
  • Selects unrolled or loop-based reductions in the WGSL template.
  • Adds high-magnitude FP16 coverage for partial tiles and head sizes 64–512.
File Description
onnxruntime/​test/​contrib_ops/​group_query_attention_op_test.cc Adds head-size boundary coverage using 65-token high-magnitude FP16 inputs.
onnxruntime/​contrib_ops/​webgpu/​bert/​flash_attention.wgsl.template Conditionally invokes unrolled reduction helpers.
onnxruntime/​contrib_ops/​webgpu/​bert/​flash_attention.cc Generates helpers and gates unrolling to NVIDIA shared-memory paths with heads up to 128.

Review effort: Balanced

@gyagp
Yang Gu (gyagp) force-pushed the codex/webgpu-nvidia-flash-unroll-20261015 branch 4 times, most recently from 4ac803e to c95092f Compare November 3, 2026 03:08
Aion-1.1 median prefill on RTX 5080 / D3D12 SM 6.10, with graph
capture, 1024 input tokens and 32 generated tokens:
- DP4A: 6,506.29 -> 7,688.99 tokens/s (+18.18%).
- Subgroup Matrix: 10,120.60 -> 13,293.90 tokens/s (+31.35%).
@gyagp
Yang Gu (gyagp) force-pushed the codex/webgpu-nvidia-flash-unroll-20261015 branch from c95092f to 0d7e0cd Compare November 3, 2026 03:13
@gyagp

Copy link
Copy Markdown
Contributor Author

/run bots

@gyagp

Copy link
Copy Markdown
Contributor Author

Jiajia Qin (@qjia7) please take another look

@qjia7

Copy link
Copy Markdown
Contributor

The request was received. Approve workflow runs. Thanks.

@qjia7
Jiajia Qin (qjia7) enabled auto-merge (squash) November 3, 2026 04:52
@gyagp

Copy link
Copy Markdown
Contributor Author

/run bots

@qjia7

Copy link
Copy Markdown
Contributor

The request was received. Approve workflow runs. Thanks.

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.

3 participants