Skip to content

Commit d6ad8aa

Browse files
pytorchbotJCNTH
andauthored
[ExecuTorch][WebGPU] Make shader workgroup sizes runtime-configurable (pytorch#21006)
This PR was created by the merge bot to help merge the original PR into the main branch. ghstack PR number: pytorch#20954 by @JCNTH ^ Please use this as the source of truth for the PR details, comments, and reviews ghstack PR base: https://github.com/pytorch/executorch/tree/gh/JCNTH/86/base ghstack PR head: https://github.com/pytorch/executorch/tree/gh/JCNTH/86/head Merge bot PR base: https://github.com/pytorch/executorch/tree/main Merge bot PR head: https://github.com/pytorch/executorch/tree/gh/JCNTH/86/orig @diff-train-skip-merge --------- Co-authored-by: Julian Ng-Thow-Hing <juliannth@meta.com> Co-authored-by: Julian Ng-Thow-Hing <107437036+JCNTH@users.noreply.github.com>
1 parent d6392c2 commit d6ad8aa

27 files changed

Lines changed: 353 additions & 273 deletions

backends/webgpu/runtime/WebGPUUtils.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ inline uint32_t clamp_workgroup_size(WGPUDevice device, uint32_t desired) {
4848
return desired;
4949
}
5050

51+
// Clamp to device limit, then floor to pow2 (reduction kernels halve stride).
52+
inline uint32_t clamp_workgroup_size_pow2(WGPUDevice device, uint32_t desired) {
53+
uint32_t v = clamp_workgroup_size(device, desired);
54+
uint32_t p = 1u;
55+
while (p <= (v >> 1u)) {
56+
p <<= 1u;
57+
}
58+
return p;
59+
}
60+
5161
struct WgCount {
5262
uint32_t x;
5363
uint32_t y;

backends/webgpu/runtime/ops/quantized_linear/QuantizedLinear.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ void q4gsw_linear_impl(WebGPUGraph& graph, const std::vector<int>& args) {
244244
const uint32_t wg_size =
245245
utils::clamp_workgroup_size(device, kQ4gswLinearWorkgroupSizeX);
246246
const bool use_gemv = (M == 1u && K % 8u == 0u && gs % 8u == 0u);
247+
// GEMV (bicol) is a pow2 tree reduction; compute its size only when used.
248+
const uint32_t gemv_wg_size = use_gemv
249+
? utils::clamp_workgroup_size_pow2(
250+
device, kQ4gswLinearCoop4BicolWorkgroupSizeX)
251+
: 0u;
247252
// steel (256-thread) is the preferred M>1 prefill GEMM; 0 count = ineligible.
248253
const bool use_steel = !use_gemv && steel_supported(device) &&
249254
steel_workgroup_count(device, M, N, K) > 0u;
@@ -362,16 +367,17 @@ void q4gsw_linear_impl(WebGPUGraph& graph, const std::vector<int>& args) {
362367
WGPUPipelineLayout pipeline_layout =
363368
wgpuDeviceCreatePipelineLayout(device, &pl_desc);
364369

370+
// GEMV/tiled wire an override wg_size; steel (256) + shmem (64) are fixed.
371+
const bool fixed_wg = use_steel || use_shmem_gemm;
365372
WGPUConstantEntry wg_size_constant = {};
366373
wg_size_constant.key = {"wg_size", WGPU_STRLEN};
367-
wg_size_constant.value = static_cast<double>(wg_size);
374+
wg_size_constant.value =
375+
static_cast<double>(use_gemv ? gemv_wg_size : wg_size);
368376

369377
WGPUComputePipelineDescriptor pipeline_desc = {};
370378
pipeline_desc.layout = pipeline_layout;
371379
pipeline_desc.compute.module = shader;
372380
pipeline_desc.compute.entryPoint = {"main", WGPU_STRLEN};
373-
// Only tiled GEMM overrides wg_size; GEMV/shmem (64) + steel (256) are fixed.
374-
const bool fixed_wg = use_gemv || use_steel || use_shmem_gemm;
375381
pipeline_desc.compute.constantCount = fixed_wg ? 0u : 1u;
376382
pipeline_desc.compute.constants = fixed_wg ? nullptr : &wg_size_constant;
377383
WGPUComputePipeline pipeline =

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_coop4.wgsl

Lines changed: 0 additions & 87 deletions
This file was deleted.

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_coop4_bicol.wgsl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ struct Params {
1717
@group(0) @binding(5) var<uniform> params: Params;
1818

1919
// Cooperative-over-K GEMV, 2 output columns/workgroup; input read once, reused.
20-
const WG: u32 = 64u;
21-
var<workgroup> partial: array<vec2<f32>, WG>;
20+
// wg_size must be a power of two (the tree reduction halves the stride).
21+
override wg_size: u32 = 64u;
22+
var<workgroup> partial: array<vec2<f32>, wg_size>;
2223

23-
@compute @workgroup_size(WG, 1, 1)
24+
@compute @workgroup_size(wg_size, 1, 1)
2425
fn main(
2526
@builtin(workgroup_id) wid: vec3<u32>,
2627
@builtin(num_workgroups) ngrp: vec3<u32>,
@@ -69,12 +70,12 @@ fn main(
6970
acc1 = acc1 + in0 * f32(i32(b1 & 0x0Fu) - 8) * scale1;
7071
acc1 = acc1 + in1 * f32(i32((b1 >> 4u) & 0x0Fu) - 8) * scale1;
7172
}
72-
w = w + WG;
73+
w = w + wg_size;
7374
}
7475

7576
partial[lid.x] = vec2<f32>(acc0, acc1);
7677
workgroupBarrier();
77-
var s: u32 = WG >> 1u;
78+
var s: u32 = wg_size >> 1u;
7879
loop {
7980
if (s == 0u) {
8081
break;

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_coop4_bicol_wgsl.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
namespace executorch::backends::webgpu {
1414

1515
// @generated from q4gsw_linear_coop4_bicol.wgsl - DO NOT EDIT.
16-
// wgsl-sha256: 44c53ca7879f7a3382a45703a67cbc6fc221ecbcada58f8c413df50abbc6e544
16+
// wgsl-sha256: ef677c2771c3d4a73704f4725bb490174149b971bc62e696595b8d13941c979e
1717
inline constexpr const char* kQ4gswLinearCoop4BicolWGSL = R"(
1818
@group(0) @binding(0) var<storage, read_write> t_out: array<f32>;
1919
@group(0) @binding(1) var<storage, read> t_input: array<f32>;
@@ -34,10 +34,11 @@ struct Params {
3434
@group(0) @binding(5) var<uniform> params: Params;
3535
3636
// Cooperative-over-K GEMV, 2 output columns/workgroup; input read once, reused.
37-
const WG: u32 = 64u;
38-
var<workgroup> partial: array<vec2<f32>, WG>;
37+
// wg_size must be a power of two (the tree reduction halves the stride).
38+
override wg_size: u32 = 64u;
39+
var<workgroup> partial: array<vec2<f32>, wg_size>;
3940
40-
@compute @workgroup_size(WG, 1, 1)
41+
@compute @workgroup_size(wg_size, 1, 1)
4142
fn main(
4243
@builtin(workgroup_id) wid: vec3<u32>,
4344
@builtin(num_workgroups) ngrp: vec3<u32>,
@@ -86,12 +87,12 @@ fn main(
8687
acc1 = acc1 + in0 * f32(i32(b1 & 0x0Fu) - 8) * scale1;
8788
acc1 = acc1 + in1 * f32(i32((b1 >> 4u) & 0x0Fu) - 8) * scale1;
8889
}
89-
w = w + WG;
90+
w = w + wg_size;
9091
}
9192
9293
partial[lid.x] = vec2<f32>(acc0, acc1);
9394
workgroupBarrier();
94-
var s: u32 = WG >> 1u;
95+
var s: u32 = wg_size >> 1u;
9596
loop {
9697
if (s == 0u) {
9798
break;

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_coop4_wgsl.h

Lines changed: 0 additions & 111 deletions
This file was deleted.

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_gemm_shmem.wgsl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ struct Params {
1616
}
1717
@group(0) @binding(5) var<uniform> params: Params;
1818

19-
override wg_size: u32 = 64u;
19+
// Fixed 64: 8x8 grid + 512-elem shared tiles are hard-locked to it (no knob).
20+
const wg_size: u32 = 64u;
2021

2122
// Shmem-staged tiled GEMM (M>1): dequant weight into shmem once per K-tile.
2223
const WG_M: u32 = 32u; // output rows per workgroup

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_gemm_shmem_wgsl.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
namespace executorch::backends::webgpu {
1414

1515
// @generated from q4gsw_linear_gemm_shmem.wgsl - DO NOT EDIT.
16-
// wgsl-sha256: 0a1219d3b6781315a21066089fca3f92235587e8af8eb734185f35ea4bfc8a52
16+
// wgsl-sha256: f0180cad41701807b34912b57d9715c02588c9447783bbf691f93836168fe981
1717
inline constexpr const char* kQ4gswLinearGemmShmemWGSL = R"(
1818
@group(0) @binding(0) var<storage, read_write> t_out: array<f32>;
1919
@group(0) @binding(1) var<storage, read> t_input: array<f32>;
@@ -33,7 +33,8 @@ struct Params {
3333
}
3434
@group(0) @binding(5) var<uniform> params: Params;
3535
36-
override wg_size: u32 = 64u;
36+
// Fixed 64: 8x8 grid + 512-elem shared tiles are hard-locked to it (no knob).
37+
const wg_size: u32 = 64u;
3738
3839
// Shmem-staged tiled GEMM (M>1): dequant weight into shmem once per K-tile.
3940
const WG_M: u32 = 32u; // output rows per workgroup

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_gemm_steel.wgsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct Params {
3434
const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 16u;
3535
var<workgroup> As: array<${buffer_scalar_type(DTYPE)}, 1024>; // BM*BK
3636
var<workgroup> Bs: array<${buffer_scalar_type(DTYPE)}, 1024>; // BK*BN
37+
// 16x16 = 256 threads, bound to the 64x64 tile + 4x4 reg tile (not a knob).
3738
@compute @workgroup_size(16, 16)
3839
fn main(@builtin(workgroup_id) wid: vec3<u32>,
3940
@builtin(local_invocation_id) lid: vec3<u32>) {

backends/webgpu/runtime/ops/quantized_linear/q4gsw_linear_gemm_steel_half_pwdq_f16acc_wgsl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
namespace executorch::backends::webgpu {
1414

1515
// @generated from q4gsw_linear_gemm_steel.wgsl - DO NOT EDIT.
16-
// wgsl-sha256: 36b3d3f9dd08a529909c13ec7d66cd0cf392c347ca047a4d38453b3c295f72ce
16+
// wgsl-sha256: fe8b71afc5634e7db5607deb58f30c6517f93e1d66370ea017bcd9071201c6ca
1717
inline constexpr const char* kQ4gswLinearGemmSteelHalfPwdqF16accWGSL = R"(
1818
enable f16;
1919
@group(0) @binding(0) var<storage, read_write> t_out: array<f32>;
@@ -50,6 +50,7 @@ struct Params {
5050
const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 16u;
5151
var<workgroup> As: array<f16, 1024>; // BM*BK
5252
var<workgroup> Bs: array<f16, 1024>; // BK*BN
53+
// 16x16 = 256 threads, bound to the 64x64 tile + 4x4 reg tile (not a knob).
5354
@compute @workgroup_size(16, 16)
5455
fn main(@builtin(workgroup_id) wid: vec3<u32>,
5556
@builtin(local_invocation_id) lid: vec3<u32>) {

0 commit comments

Comments
 (0)