Skip to content

Commit c81bfc2

Browse files
committed
Update
[ghstack-poisoned]
2 parents d75d10a + fd498fc commit c81bfc2

3 files changed

Lines changed: 75 additions & 26 deletions

File tree

backends/webgpu/runtime/ops/reduce/Reduce.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ void reduce_impl(
111111

112112
const uint32_t wg_size =
113113
utils::clamp_workgroup_size(device, kReduceWorkgroupSizeX);
114-
const uint32_t workgroup_count = utils::compute_1d_workgroup_count(
115-
device, static_cast<uint32_t>(outputs), wg_size, op_name);
114+
// Cooperative reduction: one workgroup per output element (2D-folded grid).
115+
const utils::WgCount workgroup_count = utils::compute_2d_workgroup_count(
116+
device, static_cast<uint32_t>(outputs), 1u, op_name);
116117

117118
ReduceParams params = {};
118119
params.outer = outer;
@@ -190,8 +191,8 @@ void reduce_impl(
190191
bg_desc.entries = bg_entries;
191192
WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device, &bg_desc);
192193

193-
const size_t dispatch_idx =
194-
graph.add_dispatch({pipeline, bind_group, workgroup_count, op_name});
194+
const size_t dispatch_idx = graph.add_dispatch(
195+
{pipeline, bind_group, workgroup_count.x, op_name, workgroup_count.y});
195196

196197
// Dynamic shapes: recompute the decomposition for the reduced dim + dispatch.
197198
WGPUBuffer params_buf = uniform_buffer;
@@ -205,7 +206,6 @@ void reduce_impl(
205206
keepdim,
206207
is_mean_u,
207208
build_outputs,
208-
wg_size,
209209
dispatch_idx,
210210
params_buf](WebGPUGraph& g) {
211211
const auto& d = g.cur_dims(in_id);
@@ -225,12 +225,13 @@ void reduce_impl(
225225
p.inner = n;
226226
p.is_mean = is_mean_u;
227227
wgpuQueueWriteBuffer(g.queue(), params_buf, 0, &p, sizeof(p));
228-
g.dispatch_at(dispatch_idx).workgroup_count_x =
229-
utils::compute_1d_workgroup_count(
230-
g.device(),
231-
static_cast<uint32_t>(live_outputs),
232-
wg_size,
233-
"reduce(resize)");
228+
const utils::WgCount wgc = utils::compute_2d_workgroup_count(
229+
g.device(),
230+
static_cast<uint32_t>(live_outputs),
231+
1u,
232+
"reduce(resize)");
233+
g.dispatch_at(dispatch_idx).workgroup_count_x = wgc.x;
234+
g.dispatch_at(dispatch_idx).workgroup_count_y = wgc.y;
234235
// Propagate reduced output dims for downstream resize hooks.
235236
int64_t nd = static_cast<int64_t>(d.size());
236237
int64_t rd = dim < 0 ? dim + nd : dim;

backends/webgpu/runtime/ops/reduce/reduce.wgsl

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,46 @@ struct Params {
1111

1212
override wg_size: u32 = 256;
1313

14+
// Cooperative shared-memory reduction, one workgroup per output element: each
15+
// thread sums a strided slice of the reduced dim into a shared partial, then
16+
// thread 0 folds the partials. Same one-workgroup-per-row shared-memory shape as
17+
// Vulkan's reduce_per_row_buffer.glsl. Fixed 256 upper bound >= any clamped
18+
// wg_size; only [0, wg_size) is used.
19+
var<workgroup> partials: array<f32, 256>;
20+
1421
@compute @workgroup_size(wg_size)
15-
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
16-
let t = gid.x;
22+
fn main(
23+
@builtin(workgroup_id) wid: vec3<u32>,
24+
@builtin(local_invocation_id) lid: vec3<u32>,
25+
@builtin(num_workgroups) num_workgroups: vec3<u32>) {
26+
// One workgroup per output; 2D-fold lifts the 65535 grid cap. `t` is uniform
27+
// across the workgroup, so the early return keeps the barrier in uniform flow.
28+
let t = wid.x + wid.y * num_workgroups.x;
1729
let outs = params.outer_ * params.inner_;
1830
if (t >= outs) {
1931
return;
2032
}
2133
let oo = t / params.inner_;
2234
let ii = t % params.inner_;
2335
let base = oo * params.r_ * params.inner_ + ii;
36+
2437
var acc: f32 = 0.0;
25-
for (var r: u32 = 0u; r < params.r_; r = r + 1u) {
26-
acc = acc + inp[base + r * params.inner_];
38+
var k: u32 = lid.x;
39+
while (k < params.r_) {
40+
acc = acc + inp[base + k * params.inner_];
41+
k = k + wg_size;
2742
}
28-
if (params.is_mean == 1u) {
29-
acc = acc / f32(params.r_);
43+
partials[lid.x] = acc;
44+
workgroupBarrier();
45+
46+
if (lid.x == 0u) {
47+
var s: f32 = partials[0];
48+
for (var i: u32 = 1u; i < wg_size; i = i + 1u) {
49+
s = s + partials[i];
50+
}
51+
if (params.is_mean == 1u) {
52+
s = s / f32(params.r_);
53+
}
54+
out[t] = s;
3055
}
31-
out[t] = acc;
3256
}

backends/webgpu/runtime/ops/reduce/reduce_wgsl.h

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

1515
// @generated from reduce.wgsl - DO NOT EDIT.
16-
// wgsl-sha256: e657d5252093de9e416188ef96acaeea739d188673b4d1e3cce3d6b3d91afd68
16+
// wgsl-sha256: b4a40b67af55986ea5136f06c260cf3625e4db7809975fc616aeace16b479b2d
1717
inline constexpr const char* kReduceWGSL = R"(
1818
struct Params {
1919
outer_: u32,
@@ -28,24 +28,48 @@ struct Params {
2828
2929
override wg_size: u32 = 256;
3030
31+
// Cooperative shared-memory reduction, one workgroup per output element: each
32+
// thread sums a strided slice of the reduced dim into a shared partial, then
33+
// thread 0 folds the partials. Same one-workgroup-per-row shared-memory shape as
34+
// Vulkan's reduce_per_row_buffer.glsl. Fixed 256 upper bound >= any clamped
35+
// wg_size; only [0, wg_size) is used.
36+
var<workgroup> partials: array<f32, 256>;
37+
3138
@compute @workgroup_size(wg_size)
32-
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
33-
let t = gid.x;
39+
fn main(
40+
@builtin(workgroup_id) wid: vec3<u32>,
41+
@builtin(local_invocation_id) lid: vec3<u32>,
42+
@builtin(num_workgroups) num_workgroups: vec3<u32>) {
43+
// One workgroup per output; 2D-fold lifts the 65535 grid cap. `t` is uniform
44+
// across the workgroup, so the early return keeps the barrier in uniform flow.
45+
let t = wid.x + wid.y * num_workgroups.x;
3446
let outs = params.outer_ * params.inner_;
3547
if (t >= outs) {
3648
return;
3749
}
3850
let oo = t / params.inner_;
3951
let ii = t % params.inner_;
4052
let base = oo * params.r_ * params.inner_ + ii;
53+
4154
var acc: f32 = 0.0;
42-
for (var r: u32 = 0u; r < params.r_; r = r + 1u) {
43-
acc = acc + inp[base + r * params.inner_];
55+
var k: u32 = lid.x;
56+
while (k < params.r_) {
57+
acc = acc + inp[base + k * params.inner_];
58+
k = k + wg_size;
4459
}
45-
if (params.is_mean == 1u) {
46-
acc = acc / f32(params.r_);
60+
partials[lid.x] = acc;
61+
workgroupBarrier();
62+
63+
if (lid.x == 0u) {
64+
var s: f32 = partials[0];
65+
for (var i: u32 = 1u; i < wg_size; i = i + 1u) {
66+
s = s + partials[i];
67+
}
68+
if (params.is_mean == 1u) {
69+
s = s / f32(params.r_);
70+
}
71+
out[t] = s;
4772
}
48-
out[t] = acc;
4973
}
5074
)";
5175

0 commit comments

Comments
 (0)