Repository navigation
[WebGPU] Optimize MatMulNBits wide-tile with subgroup shuffle - #31703
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
|
Add some performance data for next step. |
|
Update the description w/ perf number. |
7fe2e13 to
20f8f2f
Compare
There was a problem hiding this comment.
Pull request overview
This PR optimizes the WebGPU MatMulNBits wide-tile (Block32 prefill) shader by reducing redundant workgroup-memory reads of the A tile using subgroup shuffles when supported, while providing a safe fallback path when subgroups are unavailable or undesirable (e.g., NVIDIA).
Changes:
- Added
subgroup_min_sizespecialization to select between full-tile shuffle, chunked shuffle, or direct reduction in the wide-tile WGSL template. - Changed wide-tile A-tile storage layout to pack K vectors as contiguous pairs for more efficient reads during dequantized dot-products.
- Made
tile_madaptive to element size (f32: 16, f16: 32) and updated program cache hinting accordingly.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| onnxruntime/contrib_ops/webgpu/quantization/matmul_nbits.h | Extends wide-tile program specialization parameters to include subgroup_min_size. |
| onnxruntime/contrib_ops/webgpu/quantization/matmul_nbits.cc | Selects tile_m based on A datatype and passes adapter subgroup minimum size into shader specialization + cache key. |
| onnxruntime/contrib_ops/webgpu/quantization/matmul_nbits_wide_tile.wgsl.template | Implements subgroup-shuffle broadcast strategy (with safe sg_id capping) and reorganizes A-tile layout for paired reads. |
π‘ Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The MacOS CI Pipeline fails because of network connectivity issue. |
Fail again as network connectivity issue. Rebase to try. |
Broadcast each lane's A vecs across the subgroup via `subgroupShuffle` instead of having every lane re-read the full A tile from workgroup memory. Each lane loads its own row once and shuffles it to the other rows that need it. The workgroup tile is stored as `[KAVecSizeForBlock32/2][kTileM][2]` pairs so the two vecs consumed per dequantized weight column are read in one contiguous access. `tile_m` now adapts to the element size (16 for f32, 32 for f16, since f16 packs twice as many elements per register), and the compute-stage reduction strategy is chosen from the adapter's guaranteed minimum subgroup size: - `subgroup_min_size >= tile_m`: single full-tile `subgroupShuffle` band - `tile_m % subgroup_min_size == 0` (subgroup_min_size > 0): chunked `subgroupShuffle` bands - otherwise (including when Subgroups isn't supported): direct reduction, no shuffle The actual runtime subgroup size can exceed the adapter's guaranteed minimum, so a raw `sg_id` can index `a_data_tile` out of bounds in both shuffle branches. Cap it via `capped_sg_id = min(sg_id, kTileM - 1u)` before the main loop and use it in place of `sg_id` in both branches. Reduces redundant workgroup-memory reads for the A operand in the MatMulNBits wide-tile prefill kernel, and lets it run correctly with the best available strategy across adapters with varying or no subgroup support.
f711ecb to
312d5f4
Compare
|
Hariharan Seshadri (@hariharans29) PTAL. The |
microsoft#31703 rewrote the MatMulNBits wide-tile compute stage: the A tile is stored as pairs of vecs and the reduction now has three variants picked from subgroup_min_size (full-tile subgroupShuffle band, chunked bands, or a direct read). Re-apply the acc_element_t accumulator on top of that shape. - results is array<acc_element_t, kTileM>, and all three reduction branches widen the dot products; the subgroupShuffle itself stays in the workgroup-memory element type, so the broadcast payload does not grow. - MatMulNBitsWideTileProgram carries both subgroup_min_size and acc_f32, and the cache hint carries both, so the variants cannot collide. - With acc_f32 = false the casts are identities and the generated shader is main's, checked by diffing wgsl_gen output against 1bc68c1.
Description
Broadcast each lane's A vecs across the subgroup via
subgroupShuffleinstead of having every lane re-read the full A tile from workgroup memory. Each lane loads its own row once and shuffles it to the other rows that need it. The workgroup tile is stored as[KAVecSizeForBlock32/2][kTileM][2]pairs so the two vecs consumed per dequantized weight column are read in one contiguous access.tile_mnow adapts to the element size (16 for f32, 32 for f16, since f16 packs twice as many elements per register), and the compute-stage reduction strategy is chosen from the adapter's guaranteed minimum subgroup size:subgroup_min_size >= tile_m: single full-tilesubgroupShufflebandtile_m % subgroup_min_size == 0(subgroup_min_size > 0): chunkedsubgroupShufflebandsThe actual runtime subgroup size can exceed the adapter's guaranteed minimum, so a raw
sg_idcan indexa_data_tileout of bounds in both shuffle branches. Cap it viacapped_sg_id = min(sg_id, kTileM - 1u)before the main loop and use it in place ofsg_idin both branches.Reduces redundant workgroup-memory reads for the A operand in the MatMulNBits wide-tile prefill kernel, and lets it run correctly with the best available strategy across adapters with varying or no subgroup support.
[1] https://huggingface.co/onnx-community/Phi-4-mini-instruct-ONNX
Motivation and Context
See above.