Xenova HF Staff commited on
Commit
a95e415
·
verified ·
1 Parent(s): 9de99a0

sync 6fdf6301e2bb

Browse files
README.md CHANGED
@@ -48,6 +48,9 @@ Default values (overridable per request):
48
 
49
  One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
50
 
 
 
 
51
  - `strided_axis_serial` — Flatten a single non-last reduction axis into outer/axis/inner geometry. Compile its strides and loop bound, keep one output per lane and float32 accumulation, and cap the workgroup by the device limits.
52
 
53
  ## Device requirements
@@ -59,7 +62,7 @@ Some implementation variants require `subgroups`. These are route-specific capab
59
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
60
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
61
  - [`test.json`](build/webgpu/test.json) — correctness cases
62
- - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
63
  - [`reduce-axis-split-reduce.wgsl.jinja`](build/webgpu/reduce-axis-split-reduce.wgsl.jinja)
64
  - [`reduce-axis0-splitk-combine.wgsl.jinja`](build/webgpu/reduce-axis0-splitk-combine.wgsl.jinja)
65
  - [`reduce-axis0-splitk-reduce.wgsl.jinja`](build/webgpu/reduce-axis0-splitk-reduce.wgsl.jinja)
@@ -76,7 +79,7 @@ Some implementation variants require `subgroups`. These are route-specific capab
76
  ## Use with `@huggingface/kernels`
77
 
78
  ```sh
79
- npm install --save-exact @huggingface/kernels@0.0.1-preview.2
80
  ```
81
 
82
  Outputs with inferable metadata are allocated automatically. Explicit `outputs` entries request optional results or provide metadata that cannot be inferred from the supplied inputs and attributes.
@@ -95,7 +98,7 @@ import { getKernel } from "@huggingface/kernels";
95
 
96
  const kernel = await getKernel("webgpu-kernels/ai.onnx.ReduceSumSquare", { version: 1 });
97
  // Explicit destinations request optional results or supply metadata that cannot be inferred.
98
- const { y } = await kernel({ x: { data: xData, shape: [] } }, {
99
- outputs: { y: { shape: [], dtype: "float32" } },
100
  });
101
  ```
 
48
 
49
  One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
50
 
51
+ - `axis0_splitk_i32` — Partitions a long rank-two axis-zero integer reduction across workgroups and combines exact int32 partials. It applies when the reduced row dimension is too large for one pass to expose enough parallelism.
52
+ - `subgroup_last_axis_vec4` — Reduces each contiguous last-axis row with subgroup collectives and vec4-packed reads.
53
+ - `subgroup_last_axis` — Reduces each contiguous last-axis row with subgroup collectives and scalar reads for an unaligned row width.
54
  - `strided_axis_serial` — Flatten a single non-last reduction axis into outer/axis/inner geometry. Compile its strides and loop bound, keep one output per lane and float32 accumulation, and cap the workgroup by the device limits.
55
 
56
  ## Device requirements
 
62
  - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
63
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
64
  - [`test.json`](build/webgpu/test.json) — correctness cases
65
+ - [`bench.json`](build/webgpu/bench.json) — benchmark cases
66
  - [`reduce-axis-split-reduce.wgsl.jinja`](build/webgpu/reduce-axis-split-reduce.wgsl.jinja)
67
  - [`reduce-axis0-splitk-combine.wgsl.jinja`](build/webgpu/reduce-axis0-splitk-combine.wgsl.jinja)
68
  - [`reduce-axis0-splitk-reduce.wgsl.jinja`](build/webgpu/reduce-axis0-splitk-reduce.wgsl.jinja)
 
79
  ## Use with `@huggingface/kernels`
80
 
81
  ```sh
82
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.3
83
  ```
84
 
85
  Outputs with inferable metadata are allocated automatically. Explicit `outputs` entries request optional results or provide metadata that cannot be inferred from the supplied inputs and attributes.
 
98
 
99
  const kernel = await getKernel("webgpu-kernels/ai.onnx.ReduceSumSquare", { version: 1 });
100
  // Explicit destinations request optional results or supply metadata that cannot be inferred.
101
+ const { y } = await kernel({ x: { data: xData, shape: [3, 2, 2] } }, {
102
+ outputs: { y: { shape: [1, 1, 1], dtype: "float32" } },
103
  });
104
  ```
build/webgpu/bench.json CHANGED
@@ -70,7 +70,7 @@
70
  "name": "reducesumsquare-rank3-spatial-axes12-f32-128x256x256-pathology",
71
  "preset": "stress",
72
  "provenance": {
73
- "source": "repository-authored",
74
  "notes": "Rank-3 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
75
  },
76
  "attrs": { "axes": [1, 2], "keepdims": 1 },
@@ -82,7 +82,7 @@
82
  "name": "reducesumsquare-spatial-axes23-f32-2x64x256x256-pathology",
83
  "preset": "stress",
84
  "provenance": {
85
- "source": "repository-authored",
86
  "notes": "Rank-4 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
87
  },
88
  "attrs": { "axes": [2, 3], "keepdims": 1 },
 
70
  "name": "reducesumsquare-rank3-spatial-axes12-f32-128x256x256-pathology",
71
  "preset": "stress",
72
  "provenance": {
73
+ "source": "synthetic",
74
  "notes": "Rank-3 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
75
  },
76
  "attrs": { "axes": [1, 2], "keepdims": 1 },
 
82
  "name": "reducesumsquare-spatial-axes23-f32-2x64x256x256-pathology",
83
  "preset": "stress",
84
  "provenance": {
85
+ "source": "synthetic",
86
  "notes": "Rank-4 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
87
  },
88
  "attrs": { "axes": [2, 3], "keepdims": 1 },
build/webgpu/manifest.json CHANGED
@@ -35,6 +35,7 @@
35
  "MULTI_AXIS_COOP_MIN_REDUCED": { "default": 256 }
36
  },
37
  "derive": {
 
38
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
39
  "reduceWorkgroupSize": "min(tunables.WORKGROUP_SIZE, deviceWorkgroupCap)",
40
  "treeWorkgroupOk": "reduceWorkgroupSize > 0 and pow2ceil(reduceWorkgroupSize) == reduceWorkgroupSize and reduceWorkgroupSize * dtypeBytes(\"float32\") <= device.limits.maxComputeWorkgroupStorageSize",
@@ -60,141 +61,132 @@
60
  "flatScratchBytes": "flatSplitCount * dtypeBytes(\"float32\")",
61
  "flatPathFits": "treeWorkgroupOk and flatSplitCount <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and flatScratchBytes <= device.limits.maxStorageBufferBindingSize and flatScratchBytes <= device.limits.maxBufferSize",
62
  "flatParallelCovered": "(dtypes.T == \"f32\" or dtypes.T == \"f16\") and f16Ok(dtypes.T) and numel(shapes.y) == 1 and numel(shapes.x) >= tunables.FULL_REDUCE_MIN_ELEMENTS and flatPathFits",
63
- "contiguousSuffixParallelCovered": "(dtypes.T == \"f32\" or dtypes.T == \"f16\") and f16Ok(dtypes.T) and numel(shapes.y) > 0 and numel(shapes.x) % numel(shapes.y) == 0 and numel(shapes.x) / numel(shapes.y) >= tunables.CONTIGUOUS_SUFFIX_MIN_COLS and ((ranks.x == 3 and hasAxis(attrs.axes, 0, 3) == false and hasAxis(attrs.axes, 1, 3) and hasAxis(attrs.axes, 2, 3) and numel(shapes.y) == dim(shapes.x, 0)) or (ranks.x == 4 and hasAxis(attrs.axes, 0, 4) == false and hasAxis(attrs.axes, 1, 4) == false and hasAxis(attrs.axes, 2, 4) and hasAxis(attrs.axes, 3, 4) and numel(shapes.y) == dim(shapes.x, 0) * dim(shapes.x, 1)) or (ranks.x == 4 and hasAxis(attrs.axes, 0, 4) == false and hasAxis(attrs.axes, 1, 4) and hasAxis(attrs.axes, 2, 4) and hasAxis(attrs.axes, 3, 4) and numel(shapes.y) == dim(shapes.x, 0)))"
 
 
64
  },
65
  "bindings": {
66
- "x": { "buffer": "read-only-storage", "elementType": "$vectorScalar" },
67
- "y": { "buffer": "storage", "elementType": "$T" },
68
- "params": {
69
- "buffer": "uniform",
70
  "struct": [
71
  { "name": "rows", "type": "u32", "value": "numel(shapes.y)" },
72
  { "name": "chunkCount", "type": "u32", "value": "numel(shapes.x) / numel(shapes.y) / tunables.VECTOR_WIDTH" }
73
  ]
74
  },
75
- "x_2": { "name": "x", "buffer": "read-only-storage", "elementType": "$T" },
76
- "params_2": {
77
  "name": "params",
78
- "buffer": "uniform",
79
  "struct": [
80
- { "name": "rows", "type": "u32", "value": "numel(shapes.y)" },
81
- { "name": "cols", "type": "u32", "value": "numel(shapes.x) / numel(shapes.y)" }
82
  ]
83
  },
84
- "params_3": {
85
  "name": "params",
86
- "buffer": "uniform",
87
- "struct": [{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }]
88
  },
89
- "params_4": {
90
  "name": "params",
91
- "buffer": "uniform",
92
- "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }]
93
  },
94
- "params_5": {
 
 
 
95
  "name": "params",
96
- "buffer": "uniform",
97
  "struct": [
98
  { "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
99
- { "name": "chunkCount", "type": "u32", "value": "dim(shapes.x, ranks.x - 1) / tunables.VECTOR_WIDTH" }
100
  ]
101
  },
102
- "params_6": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  "name": "params",
104
- "buffer": "uniform",
105
  "struct": [
106
  { "name": "rows", "type": "u32", "value": "1" },
107
  { "name": "cols", "type": "u32", "value": "1" },
108
  { "name": "outCount", "type": "u32", "value": "1" }
109
  ]
110
  },
111
- "params_7": {
112
  "name": "params",
113
- "buffer": "uniform",
114
  "struct": [
115
  { "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
116
  { "name": "cols", "type": "u32", "value": "1" },
117
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
118
  ]
119
  },
120
- "params_8": {
121
  "name": "params",
122
- "buffer": "uniform",
123
  "struct": [
124
  { "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
125
  { "name": "cols", "type": "u32", "value": "dim(shapes.x, ranks.x - 1)" }
126
  ]
127
  },
128
  "partials": { "buffer": "storage", "elementType": "$partialElement" },
129
- "params_9": {
130
  "name": "params",
131
- "buffer": "uniform",
132
  "struct": [
133
  { "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
134
  { "name": "inner", "type": "u32", "value": "axisSplitInner" },
135
  { "name": "outputs", "type": "u32", "value": "axisSplitOutputs" }
136
  ]
137
  },
138
- "partials_2": { "name": "partials", "buffer": "read-only-storage", "elementType": "$partialElement" },
139
- "params_10": {
140
- "name": "params",
141
- "buffer": "uniform",
142
- "struct": [{ "name": "cols", "type": "u32", "value": "axisSplitOutputs" }]
143
- },
144
- "params_11": {
145
  "name": "params",
146
- "buffer": "uniform",
147
  "struct": [
148
  { "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
149
  { "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }
150
  ]
151
  },
152
- "params_12": {
153
- "name": "params",
154
- "buffer": "uniform",
155
- "struct": [{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }]
156
- },
157
- "partials_3": { "name": "partials", "buffer": "storage", "elementType": "f32" },
158
- "params_13": {
159
  "name": "params",
160
- "buffer": "uniform",
161
  "struct": [
162
  { "name": "count4", "type": "u32", "value": "floor(numel(shapes.x) / tunables.VECTOR_WIDTH)" },
163
  { "name": "numel", "type": "u32", "value": "numel(shapes.x)" }
164
  ]
165
  },
166
- "partials_4": { "name": "partials", "buffer": "read-only-storage", "elementType": "f32" },
167
- "params_14": { "name": "params", "buffer": "uniform", "struct": [{ "name": "cols", "type": "u32", "value": "1" }] },
168
- "params_15": {
169
  "name": "params",
170
- "buffer": "uniform",
171
  "struct": [
172
  { "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
173
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
174
  ]
175
  },
176
- "params_16": {
177
  "name": "params",
178
- "buffer": "uniform",
179
  "struct": [
180
- { "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
181
- { "name": "chunkCount", "type": "u32", "value": "dim(shapes.x, ranks.x - 1)" }
 
182
  ]
183
  },
184
- "params_17": {
185
  "name": "params",
186
- "buffer": "uniform",
187
  "struct": [
188
- { "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
189
  { "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" },
190
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
191
  ]
192
  },
193
- "params_18": {
194
  "name": "params",
195
- "buffer": "uniform",
196
  "struct": [
197
- { "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" },
 
198
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
199
  ]
200
  }
@@ -215,15 +207,9 @@
215
  "id": "main",
216
  "name": "ReduceSumSquare.ContiguousSuffixSubgroupVec4",
217
  "shader": "reduce-row-subgroup.wgsl.jinja",
218
- "derive": {
219
- "op": "\"sumsquare\"",
220
- "vec4": true,
221
- "castF32": "dtypes.T == \"f16\"",
222
- "usesF16Spec": "dtypes.T == \"f16\""
223
- },
224
- "bindings": ["x", "y", "params"],
225
- "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 },
226
- "subgroupCollectivesWidth": "portable"
227
  }
228
  ]
229
  },
@@ -241,13 +227,8 @@
241
  "id": "main",
242
  "name": "ReduceSumSquare.ContiguousSuffixTreeVec4",
243
  "shader": "reduce-row-tree.wgsl.jinja",
244
- "derive": {
245
- "op": "\"sumsquare\"",
246
- "vec4": true,
247
- "castF32": "dtypes.T == \"f16\"",
248
- "usesF16Spec": "dtypes.T == \"f16\""
249
- },
250
- "bindings": ["x", "y", "params"],
251
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
252
  }
253
  ]
@@ -265,8 +246,7 @@
265
  "id": "main",
266
  "name": "ReduceSumSquare.ContiguousSuffixTree",
267
  "shader": "reduce-row-tree.wgsl.jinja",
268
- "derive": { "op": "\"sumsquare\"", "castF32": "dtypes.T == \"f16\"", "usesF16Spec": "dtypes.T == \"f16\"" },
269
- "bindings": ["x_2", "y", "params_2"],
270
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
271
  }
272
  ]
@@ -282,7 +262,6 @@
282
  "name": "ReduceSumSquare.MultiAxisRank3Coop",
283
  "shader": "reduce-multi-axis-coop.wgsl.jinja",
284
  "derive": {
285
- "op": "\"sumsquare\"",
286
  "indexing": "\"multiaxis\"",
287
  "rank": 3,
288
  "reduce": ["hasAxis(attrs.axes, 0, 3)", "hasAxis(attrs.axes, 1, 3)", "hasAxis(attrs.axes, 2, 3)"],
@@ -290,11 +269,9 @@
290
  "outputShape": "shapes.y",
291
  "outputRank": "ranks.y",
292
  "keepDims": "attrs.keepdims != 0",
293
- "intMode": "dtypes.T == \"i32\"",
294
- "castF32": "dtypes.T == \"f16\"",
295
- "usesF16Spec": "dtypes.T == \"f16\""
296
  },
297
- "bindings": ["x_2", "y"],
298
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
299
  }
300
  ]
@@ -310,7 +287,6 @@
310
  "name": "ReduceSumSquare.MultiAxisRank4Coop",
311
  "shader": "reduce-multi-axis-coop.wgsl.jinja",
312
  "derive": {
313
- "op": "\"sumsquare\"",
314
  "indexing": "\"multiaxis\"",
315
  "rank": 4,
316
  "reduce": ["hasAxis(attrs.axes, 0, 4)", "hasAxis(attrs.axes, 1, 4)", "hasAxis(attrs.axes, 2, 4)", "hasAxis(attrs.axes, 3, 4)"],
@@ -318,11 +294,9 @@
318
  "outputShape": "shapes.y",
319
  "outputRank": "ranks.y",
320
  "keepDims": "attrs.keepdims != 0",
321
- "intMode": "dtypes.T == \"i32\"",
322
- "castF32": "dtypes.T == \"f16\"",
323
- "usesF16Spec": "dtypes.T == \"f16\""
324
  },
325
- "bindings": ["x_2", "y"],
326
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
327
  }
328
  ]
@@ -338,7 +312,6 @@
338
  "name": "ReduceSumSquare.MultiAxisRank3",
339
  "shader": "reduce-serial-axis.wgsl.jinja",
340
  "derive": {
341
- "op": "\"sumsquare\"",
342
  "indexing": "\"multiaxis\"",
343
  "rank": 3,
344
  "reduce": ["hasAxis(attrs.axes, 0, 3)", "hasAxis(attrs.axes, 1, 3)", "hasAxis(attrs.axes, 2, 3)"],
@@ -346,11 +319,9 @@
346
  "outputShape": "shapes.y",
347
  "outputRank": "ranks.y",
348
  "keepDims": "attrs.keepdims != 0",
349
- "intMode": "dtypes.T == \"i32\"",
350
- "castF32": "dtypes.T == \"f16\"",
351
- "usesF16Spec": "dtypes.T == \"f16\""
352
  },
353
- "bindings": ["x_2", "y", "params_3"],
354
  "dispatch": {
355
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
356
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -370,7 +341,6 @@
370
  "name": "ReduceSumSquare.MultiAxisRank4",
371
  "shader": "reduce-serial-axis.wgsl.jinja",
372
  "derive": {
373
- "op": "\"sumsquare\"",
374
  "indexing": "\"multiaxis\"",
375
  "rank": 4,
376
  "reduce": ["hasAxis(attrs.axes, 0, 4)", "hasAxis(attrs.axes, 1, 4)", "hasAxis(attrs.axes, 2, 4)", "hasAxis(attrs.axes, 3, 4)"],
@@ -378,11 +348,9 @@
378
  "outputShape": "shapes.y",
379
  "outputRank": "ranks.y",
380
  "keepDims": "attrs.keepdims != 0",
381
- "intMode": "dtypes.T == \"i32\"",
382
- "castF32": "dtypes.T == \"f16\"",
383
- "usesF16Spec": "dtypes.T == \"f16\""
384
  },
385
- "bindings": ["x_2", "y", "params_3"],
386
  "dispatch": {
387
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
388
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -402,7 +370,7 @@
402
  "name": "ReduceSumSquare.NoopEmptyAxes",
403
  "shader": "reduce-noop-empty-axes.wgsl.jinja",
404
  "derive": { "op": "\"square\"" },
405
- "bindings": ["x_2", "y", "params_4"],
406
  "dispatch": {
407
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
408
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -427,14 +395,12 @@
427
  "id": "main",
428
  "name": "ReduceSumSquare.SubgroupRowsVec4",
429
  "shader": "reduce-row-subgroup-rows.wgsl.jinja",
430
- "derive": { "op": "\"sumsquare\"", "castF32": "dtypes.T == \"f16\"", "usesF16Spec": "dtypes.T == \"f16\"" },
431
- "bindings": ["x", "y", "params_5"],
432
  "dispatch": {
433
  "x": "min(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
434
  "y": "ceilDiv(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
435
  "z": 1
436
- },
437
- "subgroupCollectivesWidth": "portable"
438
  }
439
  ]
440
  },
@@ -453,13 +419,8 @@
453
  "id": "main",
454
  "name": "ReduceSumSquare.TreeRowVec4",
455
  "shader": "reduce-row-tree.wgsl.jinja",
456
- "derive": {
457
- "op": "\"sumsquare\"",
458
- "vec4": true,
459
- "castF32": "dtypes.T == \"f16\"",
460
- "usesF16Spec": "dtypes.T == \"f16\""
461
- },
462
- "bindings": ["x", "y", "params_5"],
463
  "dispatch": { "x": "min(lastAxisRows, 65535)", "y": "ceilDiv(lastAxisRows, 65535)", "z": 1 }
464
  }
465
  ]
@@ -475,14 +436,11 @@
475
  "name": "ReduceSumSquare.Rank0Scalar",
476
  "shader": "reduce-serial-axis.wgsl.jinja",
477
  "derive": {
478
- "op": "\"sumsquare\"",
479
  "indexing": "\"axis2d\"",
480
  "intMode": "dtypes.T == \"i32\"",
481
- "castF32": "dtypes.T == \"f16\"",
482
- "usesF16Spec": "dtypes.T == \"f16\"",
483
  "logicalBool": "tensorDtypes.x == \"bool\""
484
  },
485
- "bindings": ["x_2", "y", "params_6"],
486
  "dispatch": { "x": 1 }
487
  }
488
  ]
@@ -497,14 +455,11 @@
497
  "name": "ReduceSumSquare.Rank1Axis0",
498
  "shader": "reduce-serial-axis.wgsl.jinja",
499
  "derive": {
500
- "op": "\"sumsquare\"",
501
  "indexing": "\"axis2d\"",
502
  "intMode": "dtypes.T == \"i32\"",
503
- "castF32": "dtypes.T == \"f16\"",
504
- "usesF16Spec": "dtypes.T == \"f16\"",
505
  "logicalBool": "tensorDtypes.x == \"bool\""
506
  },
507
- "bindings": ["x_2", "y", "params_7"],
508
  "dispatch": {
509
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
510
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -527,8 +482,7 @@
527
  "id": "main",
528
  "name": "ReduceSumSquare.Axis1Parallel",
529
  "shader": "reduce-row-tree.wgsl.jinja",
530
- "derive": { "op": "\"sumsquare\"", "castF32": "dtypes.T == \"f16\"", "usesF16Spec": "dtypes.T == \"f16\"" },
531
- "bindings": ["x_2", "y", "params_8"],
532
  "dispatch": {
533
  "x": "min(rows(shapes.x, ranks.x - 1), 65535)",
534
  "y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
@@ -553,13 +507,8 @@
553
  "id": "split_reduce",
554
  "name": "ReduceSumSquare.AxisSplitReduce",
555
  "shader": "reduce-axis-split-reduce.wgsl.jinja",
556
- "derive": {
557
- "op": "\"sumsquare\"",
558
- "splitSpec": "splitCount",
559
- "castF32": "dtypes.T == \"f16\"",
560
- "usesF16Spec": "dtypes.T == \"f16\""
561
- },
562
- "bindings": ["x_2", "partials", "params_9"],
563
  "dispatch": {
564
  "x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
565
  "y": "splitCount",
@@ -570,8 +519,8 @@
570
  "id": "combine",
571
  "name": "ReduceSumSquare.AxisSplitCombine",
572
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
573
- "derive": { "op": "\"sumsquare\"", "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
574
- "bindings": ["partials_2", "y", "params_10"],
575
  "dispatch": {
576
  "x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
577
  "y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
@@ -598,14 +547,8 @@
598
  "id": "split_reduce",
599
  "name": "ReduceSumSquare.AxisSplitTiledReduce",
600
  "shader": "reduce-axis0-tilecols.wgsl.jinja",
601
- "derive": {
602
- "op": "\"sumsquare\"",
603
- "splitSpec": "splitCount",
604
- "tileCols": "tunables.AXIS_SPLIT_TILE_COLS",
605
- "castF32": "dtypes.T == \"f16\"",
606
- "usesF16Spec": "dtypes.T == \"f16\""
607
- },
608
- "bindings": ["x_2", "partials", "params_9"],
609
  "dispatch": {
610
  "x": "min(ceilDiv((axisSplitOutputs), (tileCols)), DISPATCH_FOLD_WIDTH)",
611
  "y": "splitCount",
@@ -616,8 +559,8 @@
616
  "id": "combine",
617
  "name": "ReduceSumSquare.AxisSplitCombine",
618
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
619
- "derive": { "op": "\"sumsquare\"", "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
620
- "bindings": ["partials_2", "y", "params_10"],
621
  "dispatch": {
622
  "x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
623
  "y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
@@ -630,6 +573,7 @@
630
  "id": "axis0_splitk",
631
  "priority": 22,
632
  "when": ["not flatParallelCovered", "(dtypes.T == \"f32\" or dtypes.T == \"f16\")", "f16Ok(dtypes.T)", "ranks.x == 2", "reduceAxis == 0", "axis0Rows >= tunables.AXIS0_SPLIT_MIN_ROWS", "dim(shapes.x, 1) > 0", "((attrs.keepdims == 0 and ranks.y == 1 and dim(shapes.y, 0) == dim(shapes.x, 1)) or (attrs.keepdims == 1 and ranks.y == 2 and dim(shapes.y, 0) == 1 and dim(shapes.y, 1) == dim(shapes.x, 1)))", "axis0SplitPathFits"],
 
633
  "derive": {
634
  "splitCount": "axis0SplitCount",
635
  "partialElement": "\"f32\"",
@@ -642,13 +586,8 @@
642
  "id": "split_reduce",
643
  "name": "ReduceSumSquare.Axis0SplitKReduce",
644
  "shader": "reduce-axis0-splitk-reduce.wgsl.jinja",
645
- "derive": {
646
- "op": "\"sumsquare\"",
647
- "splitSpec": "splitCount",
648
- "castF32": "dtypes.T == \"f16\"",
649
- "usesF16Spec": "dtypes.T == \"f16\""
650
- },
651
- "bindings": ["x_2", "partials", "params_11"],
652
  "dispatch": {
653
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
654
  "y": "splitCount",
@@ -659,8 +598,8 @@
659
  "id": "combine",
660
  "name": "ReduceSumSquare.Axis0SplitKCombine",
661
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
662
- "derive": { "op": "\"sumsquare\"", "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
663
- "bindings": ["partials_2", "y", "params_12"],
664
  "dispatch": {
665
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
666
  "y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
@@ -685,8 +624,8 @@
685
  "id": "split_reduce",
686
  "name": "ReduceSumSquare.Axis0SplitKReduceI32",
687
  "shader": "reduce-axis0-splitk-reduce.wgsl.jinja",
688
- "derive": { "op": "\"sumsquare\"", "intMode": true, "splitSpec": "splitCount" },
689
- "bindings": ["x_2", "partials", "params_11"],
690
  "dispatch": {
691
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
692
  "y": "splitCount",
@@ -697,8 +636,8 @@
697
  "id": "combine",
698
  "name": "ReduceSumSquare.Axis0SplitKCombineI32",
699
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
700
- "derive": { "op": "\"sumsquare\"", "intMode": true, "splitSpec": "splitCount" },
701
- "bindings": ["partials_2", "y", "params_12"],
702
  "dispatch": {
703
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
704
  "y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
@@ -717,13 +656,8 @@
717
  "id": "main",
718
  "name": "ReduceSumSquare.Axis0TileCols",
719
  "shader": "reduce-axis0-tilecols.wgsl.jinja",
720
- "derive": {
721
- "op": "\"sumsquare\"",
722
- "intMode": "dtypes.T == \"i32\"",
723
- "castF32": "dtypes.T == \"f16\"",
724
- "usesF16Spec": "dtypes.T == \"f16\""
725
- },
726
- "bindings": ["x_2", "y", "params_11"],
727
  "dispatch": {
728
  "x": "min(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
729
  "y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
@@ -736,23 +670,22 @@
736
  "id": "all_axes_flat",
737
  "priority": 31,
738
  "when": ["flatParallelCovered"],
739
- "derive": { "scalar": "dtypes.T", "workgroupSize": "reduceWorkgroupSize", "split": "flatSplitCount" },
740
  "intermediates": [{ "id": "partials", "dtype": "float32", "shape": "[flatSplitCount]" }],
741
  "passes": [
742
  {
743
  "id": "flat_partial",
744
  "name": "ReduceSumSquare.AllAxesFlatPartial",
745
  "shader": "reduce-flat-partial.wgsl.jinja",
746
- "derive": { "op": "\"sumsquare\"", "castF32": "dtypes.T == \"f16\"", "usesF16Spec": "dtypes.T == \"f16\"" },
747
- "bindings": ["x_2", "partials_3", "params_13"],
748
  "dispatch": { "x": "flatSplitCount" }
749
  },
750
  {
751
  "id": "combine",
752
  "name": "ReduceSumSquare.AllAxesFlatCombine",
753
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
754
- "derive": { "op": "\"sumsquare\"", "outputF16": "dtypes.T == \"f16\"" },
755
- "bindings": ["partials_4", "y", "params_14"],
756
  "dispatch": { "x": 1 }
757
  }
758
  ]
@@ -769,7 +702,6 @@
769
  "name": "ReduceSumSquare.RankNSingleAxisGeneric",
770
  "shader": "reduce-serial-axis.wgsl.jinja",
771
  "derive": {
772
- "op": "\"sumsquare\"",
773
  "indexing": "\"rankn\"",
774
  "rank": "ranks.x",
775
  "axisSpec": "reduceAxis",
@@ -777,11 +709,9 @@
777
  "outputShape": "shapes.y",
778
  "outputRank": "ranks.y",
779
  "keepDims": "attrs.keepdims != 0",
780
- "intMode": "dtypes.T == \"i32\"",
781
- "castF32": "dtypes.T == \"f16\"",
782
- "usesF16Spec": "dtypes.T == \"f16\""
783
  },
784
- "bindings": ["x_2", "y", "params_15"],
785
  "dispatch": {
786
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
787
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -805,19 +735,13 @@
805
  "id": "main",
806
  "name": "ReduceSumSquare.SubgroupRowVec4",
807
  "shader": "reduce-row-subgroup.wgsl.jinja",
808
- "derive": {
809
- "op": "\"sumsquare\"",
810
- "vec4": true,
811
- "castF32": "dtypes.T == \"f16\"",
812
- "usesF16Spec": "dtypes.T == \"f16\""
813
- },
814
- "bindings": ["x", "y", "params_5"],
815
  "dispatch": {
816
  "x": "min(rows(shapes.x, ranks.x - 1), 65535)",
817
  "y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
818
  "z": 1
819
- },
820
- "subgroupCollectivesWidth": "portable"
821
  }
822
  ]
823
  },
@@ -835,19 +759,13 @@
835
  "id": "main",
836
  "name": "ReduceSumSquare.SubgroupRow",
837
  "shader": "reduce-row-subgroup.wgsl.jinja",
838
- "derive": {
839
- "op": "\"sumsquare\"",
840
- "vec4": false,
841
- "castF32": "dtypes.T == \"f16\"",
842
- "usesF16Spec": "dtypes.T == \"f16\""
843
- },
844
- "bindings": ["x_2", "y", "params_16"],
845
  "dispatch": {
846
  "x": "min(rows(shapes.x, ranks.x - 1), 65535)",
847
  "y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
848
  "z": 1
849
- },
850
- "subgroupCollectivesWidth": "portable"
851
  }
852
  ]
853
  },
@@ -860,17 +778,10 @@
860
  "passes": [
861
  {
862
  "id": "main",
863
- "name": "axis0",
864
  "shader": "reduce-serial-axis.wgsl.jinja",
865
- "derive": {
866
- "axis": 0,
867
- "op": "\"sumsquare\"",
868
- "indexing": "\"axis2d\"",
869
- "intMode": "dtypes.T == \"i32\"",
870
- "castF32": "dtypes.T == \"f16\"",
871
- "usesF16Spec": "dtypes.T == \"f16\""
872
- },
873
- "bindings": ["x_2", "y", "params_17"],
874
  "dispatch": {
875
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
876
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -887,17 +798,10 @@
887
  "passes": [
888
  {
889
  "id": "main",
890
- "name": "axis1",
891
  "shader": "reduce-serial-axis.wgsl.jinja",
892
- "derive": {
893
- "axis": 1,
894
- "op": "\"sumsquare\"",
895
- "indexing": "\"axis2d\"",
896
- "intMode": "dtypes.T == \"i32\"",
897
- "castF32": "dtypes.T == \"f16\"",
898
- "usesF16Spec": "dtypes.T == \"f16\""
899
- },
900
- "bindings": ["x_2", "y", "params_18"],
901
  "dispatch": {
902
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
903
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -916,25 +820,8 @@
916
  "id": "main",
917
  "name": "ReduceSumSquare.Rank3AllAxesKeepdims",
918
  "shader": "reduce-serial-axis.wgsl.jinja",
919
- "derive": {
920
- "op": "\"sumsquare\"",
921
- "indexing": "\"axis2d\"",
922
- "intMode": "dtypes.T == \"i32\"",
923
- "castF32": "dtypes.T == \"f16\"",
924
- "usesF16Spec": "dtypes.T == \"f16\""
925
- },
926
- "bindings": [
927
- "x_2",
928
- "y",
929
- {
930
- "name": "params",
931
- "struct": [
932
- { "name": "rows", "type": "u32", "value": "numel(shapes.x)" },
933
- { "name": "cols", "type": "u32", "value": "1" },
934
- { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
935
- ]
936
- }
937
- ],
938
  "dispatch": {
939
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
940
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -953,25 +840,8 @@
953
  "id": "main",
954
  "name": "ReduceSumSquare.Rank3AllAxesNoKeepdims",
955
  "shader": "reduce-serial-axis.wgsl.jinja",
956
- "derive": {
957
- "op": "\"sumsquare\"",
958
- "indexing": "\"axis2d\"",
959
- "intMode": "dtypes.T == \"i32\"",
960
- "castF32": "dtypes.T == \"f16\"",
961
- "usesF16Spec": "dtypes.T == \"f16\""
962
- },
963
- "bindings": [
964
- "x_2",
965
- "y",
966
- {
967
- "name": "params",
968
- "struct": [
969
- { "name": "rows", "type": "u32", "value": "numel(shapes.x)" },
970
- { "name": "cols", "type": "u32", "value": "1" },
971
- { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
972
- ]
973
- }
974
- ],
975
  "dispatch": {
976
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
977
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
@@ -995,7 +865,6 @@
995
  "axisDim": "axisSplitDim",
996
  "innerSize": "axisSplitInner",
997
  "usesF16": "dtypes.T == \"f16\"",
998
- "op": "\"sumsquare\"",
999
  "tailPaddingElements": "numel(shapes.y) % 2 if dtypes.T == \"f16\" else 0"
1000
  },
1001
  "bindings": [{ "arg": "x", "elementType": "$scalar" }, { "arg": "y", "elementType": "$scalar" }],
 
35
  "MULTI_AXIS_COOP_MIN_REDUCED": { "default": 256 }
36
  },
37
  "derive": {
38
+ "reduceOp": "\"sumsquare\"",
39
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
40
  "reduceWorkgroupSize": "min(tunables.WORKGROUP_SIZE, deviceWorkgroupCap)",
41
  "treeWorkgroupOk": "reduceWorkgroupSize > 0 and pow2ceil(reduceWorkgroupSize) == reduceWorkgroupSize and reduceWorkgroupSize * dtypeBytes(\"float32\") <= device.limits.maxComputeWorkgroupStorageSize",
 
61
  "flatScratchBytes": "flatSplitCount * dtypeBytes(\"float32\")",
62
  "flatPathFits": "treeWorkgroupOk and flatSplitCount <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and flatScratchBytes <= device.limits.maxStorageBufferBindingSize and flatScratchBytes <= device.limits.maxBufferSize",
63
  "flatParallelCovered": "(dtypes.T == \"f32\" or dtypes.T == \"f16\") and f16Ok(dtypes.T) and numel(shapes.y) == 1 and numel(shapes.x) >= tunables.FULL_REDUCE_MIN_ELEMENTS and flatPathFits",
64
+ "contiguousSuffixParallelCovered": "(dtypes.T == \"f32\" or dtypes.T == \"f16\") and f16Ok(dtypes.T) and numel(shapes.y) > 0 and numel(shapes.x) % numel(shapes.y) == 0 and numel(shapes.x) / numel(shapes.y) >= tunables.CONTIGUOUS_SUFFIX_MIN_COLS and ((ranks.x == 3 and hasAxis(attrs.axes, 0, 3) == false and hasAxis(attrs.axes, 1, 3) and hasAxis(attrs.axes, 2, 3) and numel(shapes.y) == dim(shapes.x, 0)) or (ranks.x == 4 and hasAxis(attrs.axes, 0, 4) == false and hasAxis(attrs.axes, 1, 4) == false and hasAxis(attrs.axes, 2, 4) and hasAxis(attrs.axes, 3, 4) and numel(shapes.y) == dim(shapes.x, 0) * dim(shapes.x, 1)) or (ranks.x == 4 and hasAxis(attrs.axes, 0, 4) == false and hasAxis(attrs.axes, 1, 4) and hasAxis(attrs.axes, 2, 4) and hasAxis(attrs.axes, 3, 4) and numel(shapes.y) == dim(shapes.x, 0)))",
65
+ "op": "reduceOp",
66
+ "castF32": "dtypes.T == \"f16\""
67
  },
68
  "bindings": {
69
+ "params_suffix_vec4": {
70
+ "name": "params",
 
 
71
  "struct": [
72
  { "name": "rows", "type": "u32", "value": "numel(shapes.y)" },
73
  { "name": "chunkCount", "type": "u32", "value": "numel(shapes.x) / numel(shapes.y) / tunables.VECTOR_WIDTH" }
74
  ]
75
  },
76
+ "params_reduce_row_tree": {
 
77
  "name": "params",
 
78
  "struct": [
79
+ { "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
80
+ { "name": "chunkCount", "type": "u32", "value": "dim(shapes.x, ranks.x - 1) / tunables.VECTOR_WIDTH" }
81
  ]
82
  },
83
+ "params_axis_split_combine": {
84
  "name": "params",
85
+ "struct": [{ "name": "cols", "type": "u32", "value": "axisSplitOutputs" }]
 
86
  },
87
+ "params_axis0_combine": {
88
  "name": "params",
89
+ "struct": [{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }]
 
90
  },
91
+ "partials_flat": { "name": "partials", "buffer": "storage", "elementType": "f32" },
92
+ "partials_flat_read": { "name": "partials", "buffer": "read-only-storage", "elementType": "f32" },
93
+ "params_all_axes_flat": { "name": "params", "struct": [{ "name": "cols", "type": "u32", "value": "1" }] },
94
+ "params_rows_chunk_count": {
95
  "name": "params",
 
96
  "struct": [
97
  { "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
98
+ { "name": "chunkCount", "type": "u32", "value": "dim(shapes.x, ranks.x - 1)" }
99
  ]
100
  },
101
+ "x": { "elementType": "$vectorScalar" },
102
+ "y": { "elementType": "$T" },
103
+ "x_t": { "name": "x", "elementType": "$T" },
104
+ "params_main": {
105
+ "name": "params",
106
+ "struct": [
107
+ { "name": "rows", "type": "u32", "value": "numel(shapes.y)" },
108
+ { "name": "cols", "type": "u32", "value": "numel(shapes.x) / numel(shapes.y)" }
109
+ ]
110
+ },
111
+ "params_out_count": {
112
+ "name": "params",
113
+ "struct": [{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }]
114
+ },
115
+ "params_count": { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] },
116
+ "params_rank0_scalar": {
117
  "name": "params",
 
118
  "struct": [
119
  { "name": "rows", "type": "u32", "value": "1" },
120
  { "name": "cols", "type": "u32", "value": "1" },
121
  { "name": "outCount", "type": "u32", "value": "1" }
122
  ]
123
  },
124
+ "params_rank1_axis0": {
125
  "name": "params",
 
126
  "struct": [
127
  { "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
128
  { "name": "cols", "type": "u32", "value": "1" },
129
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
130
  ]
131
  },
132
+ "params_rows_cols": {
133
  "name": "params",
 
134
  "struct": [
135
  { "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
136
  { "name": "cols", "type": "u32", "value": "dim(shapes.x, ranks.x - 1)" }
137
  ]
138
  },
139
  "partials": { "buffer": "storage", "elementType": "$partialElement" },
140
+ "params_axis_split": {
141
  "name": "params",
 
142
  "struct": [
143
  { "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
144
  { "name": "inner", "type": "u32", "value": "axisSplitInner" },
145
  { "name": "outputs", "type": "u32", "value": "axisSplitOutputs" }
146
  ]
147
  },
148
+ "partials_combine": { "name": "partials", "buffer": "read-only-storage", "elementType": "$partialElement" },
149
+ "params_split_reduce": {
 
 
 
 
 
150
  "name": "params",
 
151
  "struct": [
152
  { "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
153
  { "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }
154
  ]
155
  },
156
+ "params_count4_numel": {
 
 
 
 
 
 
157
  "name": "params",
 
158
  "struct": [
159
  { "name": "count4", "type": "u32", "value": "floor(numel(shapes.x) / tunables.VECTOR_WIDTH)" },
160
  { "name": "numel", "type": "u32", "value": "numel(shapes.x)" }
161
  ]
162
  },
163
+ "params_axis_dim_out_count": {
 
 
164
  "name": "params",
 
165
  "struct": [
166
  { "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
167
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
168
  ]
169
  },
170
+ "params_axis0": {
171
  "name": "params",
 
172
  "struct": [
173
+ { "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
174
+ { "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" },
175
+ { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
176
  ]
177
  },
178
+ "params_axis1": {
179
  "name": "params",
 
180
  "struct": [
 
181
  { "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" },
182
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
183
  ]
184
  },
185
+ "params_all_axes": {
186
  "name": "params",
 
187
  "struct": [
188
+ { "name": "rows", "type": "u32", "value": "numel(shapes.x)" },
189
+ { "name": "cols", "type": "u32", "value": "1" },
190
  { "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
191
  ]
192
  }
 
207
  "id": "main",
208
  "name": "ReduceSumSquare.ContiguousSuffixSubgroupVec4",
209
  "shader": "reduce-row-subgroup.wgsl.jinja",
210
+ "derive": { "vec4": true },
211
+ "bindings": ["x", "y", "params_suffix_vec4"],
212
+ "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
 
 
 
 
 
 
213
  }
214
  ]
215
  },
 
227
  "id": "main",
228
  "name": "ReduceSumSquare.ContiguousSuffixTreeVec4",
229
  "shader": "reduce-row-tree.wgsl.jinja",
230
+ "derive": { "vec4": true },
231
+ "bindings": ["x", "y", "params_suffix_vec4"],
 
 
 
 
 
232
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
233
  }
234
  ]
 
246
  "id": "main",
247
  "name": "ReduceSumSquare.ContiguousSuffixTree",
248
  "shader": "reduce-row-tree.wgsl.jinja",
249
+ "bindings": ["x_t", "y", "params_main"],
 
250
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
251
  }
252
  ]
 
262
  "name": "ReduceSumSquare.MultiAxisRank3Coop",
263
  "shader": "reduce-multi-axis-coop.wgsl.jinja",
264
  "derive": {
 
265
  "indexing": "\"multiaxis\"",
266
  "rank": 3,
267
  "reduce": ["hasAxis(attrs.axes, 0, 3)", "hasAxis(attrs.axes, 1, 3)", "hasAxis(attrs.axes, 2, 3)"],
 
269
  "outputShape": "shapes.y",
270
  "outputRank": "ranks.y",
271
  "keepDims": "attrs.keepdims != 0",
272
+ "intMode": "dtypes.T == \"i32\""
 
 
273
  },
274
+ "bindings": ["x_t", "y"],
275
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
276
  }
277
  ]
 
287
  "name": "ReduceSumSquare.MultiAxisRank4Coop",
288
  "shader": "reduce-multi-axis-coop.wgsl.jinja",
289
  "derive": {
 
290
  "indexing": "\"multiaxis\"",
291
  "rank": 4,
292
  "reduce": ["hasAxis(attrs.axes, 0, 4)", "hasAxis(attrs.axes, 1, 4)", "hasAxis(attrs.axes, 2, 4)", "hasAxis(attrs.axes, 3, 4)"],
 
294
  "outputShape": "shapes.y",
295
  "outputRank": "ranks.y",
296
  "keepDims": "attrs.keepdims != 0",
297
+ "intMode": "dtypes.T == \"i32\""
 
 
298
  },
299
+ "bindings": ["x_t", "y"],
300
  "dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
301
  }
302
  ]
 
312
  "name": "ReduceSumSquare.MultiAxisRank3",
313
  "shader": "reduce-serial-axis.wgsl.jinja",
314
  "derive": {
 
315
  "indexing": "\"multiaxis\"",
316
  "rank": 3,
317
  "reduce": ["hasAxis(attrs.axes, 0, 3)", "hasAxis(attrs.axes, 1, 3)", "hasAxis(attrs.axes, 2, 3)"],
 
319
  "outputShape": "shapes.y",
320
  "outputRank": "ranks.y",
321
  "keepDims": "attrs.keepdims != 0",
322
+ "intMode": "dtypes.T == \"i32\""
 
 
323
  },
324
+ "bindings": ["x_t", "y", "params_out_count"],
325
  "dispatch": {
326
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
327
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
341
  "name": "ReduceSumSquare.MultiAxisRank4",
342
  "shader": "reduce-serial-axis.wgsl.jinja",
343
  "derive": {
 
344
  "indexing": "\"multiaxis\"",
345
  "rank": 4,
346
  "reduce": ["hasAxis(attrs.axes, 0, 4)", "hasAxis(attrs.axes, 1, 4)", "hasAxis(attrs.axes, 2, 4)", "hasAxis(attrs.axes, 3, 4)"],
 
348
  "outputShape": "shapes.y",
349
  "outputRank": "ranks.y",
350
  "keepDims": "attrs.keepdims != 0",
351
+ "intMode": "dtypes.T == \"i32\""
 
 
352
  },
353
+ "bindings": ["x_t", "y", "params_out_count"],
354
  "dispatch": {
355
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
356
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
370
  "name": "ReduceSumSquare.NoopEmptyAxes",
371
  "shader": "reduce-noop-empty-axes.wgsl.jinja",
372
  "derive": { "op": "\"square\"" },
373
+ "bindings": ["x_t", "y", "params_count"],
374
  "dispatch": {
375
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
376
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
395
  "id": "main",
396
  "name": "ReduceSumSquare.SubgroupRowsVec4",
397
  "shader": "reduce-row-subgroup-rows.wgsl.jinja",
398
+ "bindings": ["x", "y", "params_reduce_row_tree"],
 
399
  "dispatch": {
400
  "x": "min(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
401
  "y": "ceilDiv(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
402
  "z": 1
403
+ }
 
404
  }
405
  ]
406
  },
 
419
  "id": "main",
420
  "name": "ReduceSumSquare.TreeRowVec4",
421
  "shader": "reduce-row-tree.wgsl.jinja",
422
+ "derive": { "vec4": true },
423
+ "bindings": ["x", "y", "params_reduce_row_tree"],
 
 
 
 
 
424
  "dispatch": { "x": "min(lastAxisRows, 65535)", "y": "ceilDiv(lastAxisRows, 65535)", "z": 1 }
425
  }
426
  ]
 
436
  "name": "ReduceSumSquare.Rank0Scalar",
437
  "shader": "reduce-serial-axis.wgsl.jinja",
438
  "derive": {
 
439
  "indexing": "\"axis2d\"",
440
  "intMode": "dtypes.T == \"i32\"",
 
 
441
  "logicalBool": "tensorDtypes.x == \"bool\""
442
  },
443
+ "bindings": ["x_t", "y", "params_rank0_scalar"],
444
  "dispatch": { "x": 1 }
445
  }
446
  ]
 
455
  "name": "ReduceSumSquare.Rank1Axis0",
456
  "shader": "reduce-serial-axis.wgsl.jinja",
457
  "derive": {
 
458
  "indexing": "\"axis2d\"",
459
  "intMode": "dtypes.T == \"i32\"",
 
 
460
  "logicalBool": "tensorDtypes.x == \"bool\""
461
  },
462
+ "bindings": ["x_t", "y", "params_rank1_axis0"],
463
  "dispatch": {
464
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
465
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
482
  "id": "main",
483
  "name": "ReduceSumSquare.Axis1Parallel",
484
  "shader": "reduce-row-tree.wgsl.jinja",
485
+ "bindings": ["x_t", "y", "params_rows_cols"],
 
486
  "dispatch": {
487
  "x": "min(rows(shapes.x, ranks.x - 1), 65535)",
488
  "y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
 
507
  "id": "split_reduce",
508
  "name": "ReduceSumSquare.AxisSplitReduce",
509
  "shader": "reduce-axis-split-reduce.wgsl.jinja",
510
+ "derive": { "splitSpec": "splitCount" },
511
+ "bindings": ["x_t", "partials", "params_axis_split"],
 
 
 
 
 
512
  "dispatch": {
513
  "x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
514
  "y": "splitCount",
 
519
  "id": "combine",
520
  "name": "ReduceSumSquare.AxisSplitCombine",
521
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
522
+ "derive": { "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
523
+ "bindings": ["partials_combine", "y", "params_axis_split_combine"],
524
  "dispatch": {
525
  "x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
526
  "y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
 
547
  "id": "split_reduce",
548
  "name": "ReduceSumSquare.AxisSplitTiledReduce",
549
  "shader": "reduce-axis0-tilecols.wgsl.jinja",
550
+ "derive": { "splitSpec": "splitCount" },
551
+ "bindings": ["x_t", "partials", "params_axis_split"],
 
 
 
 
 
 
552
  "dispatch": {
553
  "x": "min(ceilDiv((axisSplitOutputs), (tileCols)), DISPATCH_FOLD_WIDTH)",
554
  "y": "splitCount",
 
559
  "id": "combine",
560
  "name": "ReduceSumSquare.AxisSplitCombine",
561
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
562
+ "derive": { "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
563
+ "bindings": ["partials_combine", "y", "params_axis_split_combine"],
564
  "dispatch": {
565
  "x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
566
  "y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
 
573
  "id": "axis0_splitk",
574
  "priority": 22,
575
  "when": ["not flatParallelCovered", "(dtypes.T == \"f32\" or dtypes.T == \"f16\")", "f16Ok(dtypes.T)", "ranks.x == 2", "reduceAxis == 0", "axis0Rows >= tunables.AXIS0_SPLIT_MIN_ROWS", "dim(shapes.x, 1) > 0", "((attrs.keepdims == 0 and ranks.y == 1 and dim(shapes.y, 0) == dim(shapes.x, 1)) or (attrs.keepdims == 1 and ranks.y == 2 and dim(shapes.y, 0) == 1 and dim(shapes.y, 1) == dim(shapes.x, 1)))", "axis0SplitPathFits"],
576
+ "demoteWhen": ["dtypes.T == \"f32\" and device.adapterInfo.subgroupMinSize == 16 and device.adapterInfo.subgroupMaxSize == 32 and axis0Rows >= 2 * tunables.AXIS0_SPLIT_MIN_ROWS and axis0Cols >= tunables.AXIS0_TILE_MIN_COLS and axis0TilePathFits and ceilDiv(axis0Cols, tunables.AXIS0_TILE_COLS) >= axis0SplitCount"],
577
  "derive": {
578
  "splitCount": "axis0SplitCount",
579
  "partialElement": "\"f32\"",
 
586
  "id": "split_reduce",
587
  "name": "ReduceSumSquare.Axis0SplitKReduce",
588
  "shader": "reduce-axis0-splitk-reduce.wgsl.jinja",
589
+ "derive": { "splitSpec": "splitCount" },
590
+ "bindings": ["x_t", "partials", "params_split_reduce"],
 
 
 
 
 
591
  "dispatch": {
592
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
593
  "y": "splitCount",
 
598
  "id": "combine",
599
  "name": "ReduceSumSquare.Axis0SplitKCombine",
600
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
601
+ "derive": { "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
602
+ "bindings": ["partials_combine", "y", "params_axis0_combine"],
603
  "dispatch": {
604
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
605
  "y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
 
624
  "id": "split_reduce",
625
  "name": "ReduceSumSquare.Axis0SplitKReduceI32",
626
  "shader": "reduce-axis0-splitk-reduce.wgsl.jinja",
627
+ "derive": { "intMode": true, "splitSpec": "splitCount" },
628
+ "bindings": ["x_t", "partials", "params_split_reduce"],
629
  "dispatch": {
630
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
631
  "y": "splitCount",
 
636
  "id": "combine",
637
  "name": "ReduceSumSquare.Axis0SplitKCombineI32",
638
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
639
+ "derive": { "intMode": true, "splitSpec": "splitCount" },
640
+ "bindings": ["partials_combine", "y", "params_axis0_combine"],
641
  "dispatch": {
642
  "x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
643
  "y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
 
656
  "id": "main",
657
  "name": "ReduceSumSquare.Axis0TileCols",
658
  "shader": "reduce-axis0-tilecols.wgsl.jinja",
659
+ "derive": { "intMode": "dtypes.T == \"i32\"" },
660
+ "bindings": ["x_t", "y", "params_split_reduce"],
 
 
 
 
 
661
  "dispatch": {
662
  "x": "min(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
663
  "y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
 
670
  "id": "all_axes_flat",
671
  "priority": 31,
672
  "when": ["flatParallelCovered"],
673
+ "derive": { "workgroupSize": "reduceWorkgroupSize", "split": "flatSplitCount" },
674
  "intermediates": [{ "id": "partials", "dtype": "float32", "shape": "[flatSplitCount]" }],
675
  "passes": [
676
  {
677
  "id": "flat_partial",
678
  "name": "ReduceSumSquare.AllAxesFlatPartial",
679
  "shader": "reduce-flat-partial.wgsl.jinja",
680
+ "bindings": ["x_t", "partials_flat", "params_count4_numel"],
 
681
  "dispatch": { "x": "flatSplitCount" }
682
  },
683
  {
684
  "id": "combine",
685
  "name": "ReduceSumSquare.AllAxesFlatCombine",
686
  "shader": "reduce-axis0-splitk-combine.wgsl.jinja",
687
+ "derive": { "outputF16": "dtypes.T == \"f16\"" },
688
+ "bindings": ["partials_flat_read", "y", "params_all_axes_flat"],
689
  "dispatch": { "x": 1 }
690
  }
691
  ]
 
702
  "name": "ReduceSumSquare.RankNSingleAxisGeneric",
703
  "shader": "reduce-serial-axis.wgsl.jinja",
704
  "derive": {
 
705
  "indexing": "\"rankn\"",
706
  "rank": "ranks.x",
707
  "axisSpec": "reduceAxis",
 
709
  "outputShape": "shapes.y",
710
  "outputRank": "ranks.y",
711
  "keepDims": "attrs.keepdims != 0",
712
+ "intMode": "dtypes.T == \"i32\""
 
 
713
  },
714
+ "bindings": ["x_t", "y", "params_axis_dim_out_count"],
715
  "dispatch": {
716
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
717
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
735
  "id": "main",
736
  "name": "ReduceSumSquare.SubgroupRowVec4",
737
  "shader": "reduce-row-subgroup.wgsl.jinja",
738
+ "derive": { "vec4": true },
739
+ "bindings": ["x", "y", "params_reduce_row_tree"],
 
 
 
 
 
740
  "dispatch": {
741
  "x": "min(rows(shapes.x, ranks.x - 1), 65535)",
742
  "y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
743
  "z": 1
744
+ }
 
745
  }
746
  ]
747
  },
 
759
  "id": "main",
760
  "name": "ReduceSumSquare.SubgroupRow",
761
  "shader": "reduce-row-subgroup.wgsl.jinja",
762
+ "derive": { "vec4": false },
763
+ "bindings": ["x_t", "y", "params_rows_chunk_count"],
 
 
 
 
 
764
  "dispatch": {
765
  "x": "min(rows(shapes.x, ranks.x - 1), 65535)",
766
  "y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
767
  "z": 1
768
+ }
 
769
  }
770
  ]
771
  },
 
778
  "passes": [
779
  {
780
  "id": "main",
781
+ "name": "ReduceSumSquare.Axis0",
782
  "shader": "reduce-serial-axis.wgsl.jinja",
783
+ "derive": { "indexing": "\"axis2d\"", "intMode": "dtypes.T == \"i32\"" },
784
+ "bindings": ["x_t", "y", "params_axis0"],
 
 
 
 
 
 
 
785
  "dispatch": {
786
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
787
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
798
  "passes": [
799
  {
800
  "id": "main",
801
+ "name": "ReduceSumSquare.Axis1",
802
  "shader": "reduce-serial-axis.wgsl.jinja",
803
+ "derive": { "indexing": "\"axis2d\"", "intMode": "dtypes.T == \"i32\"" },
804
+ "bindings": ["x_t", "y", "params_axis1"],
 
 
 
 
 
 
 
805
  "dispatch": {
806
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
807
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
820
  "id": "main",
821
  "name": "ReduceSumSquare.Rank3AllAxesKeepdims",
822
  "shader": "reduce-serial-axis.wgsl.jinja",
823
+ "derive": { "indexing": "\"axis2d\"", "intMode": "dtypes.T == \"i32\"" },
824
+ "bindings": ["x_t", "y", "params_all_axes"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
825
  "dispatch": {
826
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
827
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
840
  "id": "main",
841
  "name": "ReduceSumSquare.Rank3AllAxesNoKeepdims",
842
  "shader": "reduce-serial-axis.wgsl.jinja",
843
+ "derive": { "indexing": "\"axis2d\"", "intMode": "dtypes.T == \"i32\"" },
844
+ "bindings": ["x_t", "y", "params_all_axes"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
845
  "dispatch": {
846
  "x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
847
  "y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
 
865
  "axisDim": "axisSplitDim",
866
  "innerSize": "axisSplitInner",
867
  "usesF16": "dtypes.T == \"f16\"",
 
868
  "tailPaddingElements": "numel(shapes.y) % 2 if dtypes.T == \"f16\" else 0"
869
  },
870
  "bindings": [{ "arg": "x", "elementType": "$scalar" }, { "arg": "y", "elementType": "$scalar" }],
build/webgpu/metadata.json CHANGED
@@ -1,32 +1,32 @@
1
  {
2
  "name": "ai.onnx.ReduceSumSquare",
3
- "id": "_ai_onnx_reducesumsquare_webgpu_548d8b9",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "obfW4pk199RnyQJb8JyyTcmc+W8hfJlCJqtVtaO1IfM=",
11
- "manifest.json": "NyQsjDGkXqU+ymVQxxw1IgoD/fjCkkGNolQjv9t+EcI=",
12
- "reduce-axis-split-reduce.wgsl.jinja": "4+ep9xH4pHZOfaZ8abJhA8SW5M4mUhDC+y1CtDz6vjY=",
13
- "reduce-axis0-splitk-combine.wgsl.jinja": "Yz1hjK55R/kndUrw3ugPqgPaOBwKmYupqZoVdJTHO5Q=",
14
- "reduce-axis0-splitk-reduce.wgsl.jinja": "jB2h58emn6rfKhd8ALzqylSDsmBbrrkEOhtIoNhcQM0=",
15
- "reduce-axis0-tilecols.wgsl.jinja": "PjYkEUQJBeG70td3W2xxmexH9x6XNIfeSzXBY47HbaU=",
16
- "reduce-flat-partial.wgsl.jinja": "+qToL+wFi9QOxvY887aBAEwZK6Xu/eLQkucKvJYNwSk=",
17
- "reduce-multi-axis-coop.wgsl.jinja": "6nzls4ovUMXlLTet/1lWkSOqp0GRoe1pM922IMU/kSg=",
18
- "reduce-noop-empty-axes.wgsl.jinja": "p2m6oLipVBUGRUn6Qrhnj2yqy0akR9fiRQGIpiqkEP0=",
19
- "reduce-row-subgroup-rows.wgsl.jinja": "76u7rAvFoZZKrFDs2A2jk0vkB0uNrPL6twdOBE9b+v8=",
20
- "reduce-row-subgroup.wgsl.jinja": "2mu9LEsk8HfaLvucBCfcB1/ENpXkD6ELiCtRt+5UqiU=",
21
- "reduce-row-tree.wgsl.jinja": "Bwa5xcI0bTmKXb4r9Cc1bfVbM5rNqqpQVrWWVqcb8xA=",
22
- "reduce-serial-axis.wgsl.jinja": "fvUV9htqzKzt4Pg05pYtRmup/5QIHYaUGQHJZnthXKo=",
23
- "reduce-strided-axis.wgsl.jinja": "kSHu8SfXF3m5H0WdjJQvzJpwPi63fhTR3VJdaCVHHEk=",
24
- "test.json": "QocM6dCGrSGJkVrbXmhuZXTT5ftIevLRJwTxKwNGFgc="
25
  }
26
  },
27
- "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
28
  "webgpu": {
29
- "manifestSpec": "2.0",
30
  "variants": {
31
  "contiguous_suffix_subgroup_vec4": ["reduce-row-subgroup.wgsl.jinja"],
32
  "contiguous_suffix_tree_vec4": ["reduce-row-tree.wgsl.jinja"],
 
1
  {
2
  "name": "ai.onnx.ReduceSumSquare",
3
+ "id": "_ai_onnx_reducesumsquare_webgpu_9bc57e2",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "2YakMgGda7wx7sMW4xMeZD570uvu5fxEZMTf2OpAFwE=",
11
+ "manifest.json": "4LP61rFDIK3IkzXYj3qiqSIVNPoYsZz5hGnp7PP4ysw=",
12
+ "reduce-axis-split-reduce.wgsl.jinja": "xo4jwtx/MeHIZkh2KU31ZCR+Ak58xOe4Cg3HWrcvdqo=",
13
+ "reduce-axis0-splitk-combine.wgsl.jinja": "bY5/z/a29FortWA0aT28cnkQX6dzLGr1O1fV/ncfxLs=",
14
+ "reduce-axis0-splitk-reduce.wgsl.jinja": "rW4Vltn1kPBqmbVTl7Za5CWycPaUe7IwaqXoWH+72Tw=",
15
+ "reduce-axis0-tilecols.wgsl.jinja": "dyX/c20cWbPRfgqDdOi5XqfhvFtxyQD8KDJJ2gJ3fqc=",
16
+ "reduce-flat-partial.wgsl.jinja": "gMQg8GcibV+NQadQLAprKk6K3w13GCOdfQvIdCvYDXs=",
17
+ "reduce-multi-axis-coop.wgsl.jinja": "mQldeEk3lvyh67eiW/mfZFaM2gpRc0fUUgpq7PI3SHo=",
18
+ "reduce-noop-empty-axes.wgsl.jinja": "YjsRlY2jCKjUGbfQADoL9id7pEUGvh53UailNEaBPLw=",
19
+ "reduce-row-subgroup-rows.wgsl.jinja": "5cMGE1dmrcawwHdoxQD8kPSurwA1wNHyoAQfxAOE7vU=",
20
+ "reduce-row-subgroup.wgsl.jinja": "7bRBWIQ7oHOq/E54hGrmXIzLznzVH9oTfYaf1jmBzV8=",
21
+ "reduce-row-tree.wgsl.jinja": "a+iEV/cBkrlsVz6NS0GY/TAsUSX5rO4N14wY1dKfDi0=",
22
+ "reduce-serial-axis.wgsl.jinja": "XZthzOtNawNeG2SXq528l1yO5sVPqTTK3Y9GVhJ46lk=",
23
+ "reduce-strided-axis.wgsl.jinja": "K+yHpuxWwKU75+/4M8BuQ8UU2e+gGKoklPj5I/uH5rU=",
24
+ "test.json": "fB26Xly/jHVgC6WYqbNnvlBgSQJzXAQbzzsIM4JFCYQ="
25
  }
26
  },
27
+ "provenance": { "kernel": { "sha": "6fdf6301e2bbcc2f03bf1eaf493b7ad55ef33afc", "dirty": false } },
28
  "webgpu": {
29
+ "manifestSpec": "2.1",
30
  "variants": {
31
  "contiguous_suffix_subgroup_vec4": ["reduce-row-subgroup.wgsl.jinja"],
32
  "contiguous_suffix_tree_vec4": ["reduce-row-tree.wgsl.jinja"],
build/webgpu/reduce-axis-split-reduce.wgsl.jinja CHANGED
@@ -8,29 +8,16 @@
8
  // Each output segment writes three partial planes: its maximum, the sum of
9
  // exp(x - maximum), and a packed NaN marker.
10
  {% endif %}
11
- {% set castF32 = castF32 is defined and castF32 %}
12
- {% set xa = "f32(" if castF32 else "" %}
13
  {% set ax = ")" if castF32 else "" %}
14
- {% if usesF16Spec is defined and usesF16Spec %}
15
- enable f16;
16
- {% endif %}
17
  {{ env.wgsl.resourceDeclarations }}
18
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
19
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
20
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
21
- {% set logicalBoolIdentity = logicalBool is defined and logicalBool %}
22
  fn {{ name }}() -> {{ scalar }} {
23
- {% if scalar == "i32" %}
24
- return {{ "-2147483647i - 1i" if op == "max" else "2147483647i" }};
25
- {% elif scalar == "u32" %}
26
- return {{ "1u" if logicalBoolIdentity and op == "min" else "0u" if op == "max" else "4294967295u" }};
27
- {% else %}
28
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
29
  return bitcast<f32>(bits);
30
- {% endif %}
31
- }
32
- {%- endmacro %}
33
-
34
 
35
  const WG: u32 = {{ workgroupSize }}u;
36
  const SPLIT: u32 = {{ split }}u;
@@ -42,6 +29,7 @@ fn is_nan_f32(value: f32) -> bool {
42
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
43
  }
44
  {% elif op == "max" or op == "min" %}
 
45
  {{ wgsl_minmax_identity("reduction_identity", op) }}
46
  {% endif %}
47
 
 
8
  // Each output segment writes three partial planes: its maximum, the sum of
9
  // exp(x - maximum), and a packed NaN marker.
10
  {% endif %}
11
+ {% set xa = ("vec" ~ columnVectorWidth ~ "<f32>(" if columnVectorWidth is defined else "f32(") if castF32 else "" %}
 
12
  {% set ax = ")" if castF32 else "" %}
 
 
 
13
  {{ env.wgsl.resourceDeclarations }}
14
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
15
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
16
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
 
17
  fn {{ name }}() -> {{ scalar }} {
 
 
 
 
 
18
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
19
  return bitcast<f32>(bits);
20
+ }{% endmacro %}
 
 
 
21
 
22
  const WG: u32 = {{ workgroupSize }}u;
23
  const SPLIT: u32 = {{ split }}u;
 
29
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
30
  }
31
  {% elif op == "max" or op == "min" %}
32
+
33
  {{ wgsl_minmax_identity("reduction_identity", op) }}
34
  {% endif %}
35
 
build/webgpu/reduce-axis0-splitk-combine.wgsl.jinja CHANGED
@@ -2,37 +2,20 @@
2
  // folds the segment partials and applies the selected reduction's final step.
3
  // Segments are folded in ascending order for deterministic results. This order
4
  // differs from the single-pass reduction but remains within the f32 tolerance.
5
- {% set addBias = addBias is defined and addBias %}
6
- {% set biasCols = biasCols | default(0) %}
7
  {% set intMode = intMode is defined and intMode %}
8
  {% set yv = "f16(" if outputF16 else "" %}
9
  {% set vy = ")" if outputF16 else "" %}
10
- {% if outputF16 %}
11
- enable f16;
12
- {% endif %}
13
  {{ env.wgsl.resourceDeclarations }}
14
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
15
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
16
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
17
- {% set logicalBoolIdentity = logicalBool is defined and logicalBool %}
18
  fn {{ name }}() -> {{ scalar }} {
19
- {% if scalar == "i32" %}
20
- return {{ "-2147483647i - 1i" if op == "max" else "2147483647i" }};
21
- {% elif scalar == "u32" %}
22
- return {{ "1u" if logicalBoolIdentity and op == "min" else "0u" if op == "max" else "4294967295u" }};
23
- {% else %}
24
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
25
  return bitcast<f32>(bits);
26
- {% endif %}
27
- }
28
- {%- endmacro %}
29
-
30
 
31
  const WG: u32 = {{ workgroupSize }}u;
32
  const SPLIT: u32 = {{ split }}u;
33
- {% if addBias %}
34
- const BIAS_COLS: u32 = {{ biasCols }}u;
35
- {% endif %}
36
  {% if op == "logsumexp" %}
37
  const F32_MIN: f32 = -3.4028234663852886e38;
38
  const F32_MAX: f32 = 3.4028234663852886e38;
@@ -48,7 +31,9 @@ fn is_nan_f32(value: f32) -> bool {
48
  @compute @workgroup_size(WG, 1, 1)
49
  fn main(@builtin(global_invocation_id) gid: vec3<u32>,
50
  @builtin(num_workgroups) nwg: vec3<u32>) {
51
- let stride = nwg.x * WG;
 
 
52
  let start = (gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG) + gid.x;
53
  for (var col = start; col < params.cols; col = col + stride) {
54
  {% if op == "logsumexp" %}
@@ -101,9 +86,6 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
101
  total = total + p;
102
  {% endif %}
103
  }
104
- {% if addBias %}
105
- total = total + f32(bias[col % BIAS_COLS]);
106
- {% endif %}
107
  {% if op == "l2" %}
108
  y[col] = {{ yv }}sqrt(total){{ vy }};
109
  {% elif op == "logsum" %}
 
2
  // folds the segment partials and applies the selected reduction's final step.
3
  // Segments are folded in ascending order for deterministic results. This order
4
  // differs from the single-pass reduction but remains within the f32 tolerance.
 
 
5
  {% set intMode = intMode is defined and intMode %}
6
  {% set yv = "f16(" if outputF16 else "" %}
7
  {% set vy = ")" if outputF16 else "" %}
 
 
 
8
  {{ env.wgsl.resourceDeclarations }}
9
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
10
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
11
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
 
12
  fn {{ name }}() -> {{ scalar }} {
 
 
 
 
 
13
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
14
  return bitcast<f32>(bits);
15
+ }{% endmacro %}
 
 
 
16
 
17
  const WG: u32 = {{ workgroupSize }}u;
18
  const SPLIT: u32 = {{ split }}u;
 
 
 
19
  {% if op == "logsumexp" %}
20
  const F32_MIN: f32 = -3.4028234663852886e38;
21
  const F32_MAX: f32 = 3.4028234663852886e38;
 
31
  @compute @workgroup_size(WG, 1, 1)
32
  fn main(@builtin(global_invocation_id) gid: vec3<u32>,
33
  @builtin(num_workgroups) nwg: vec3<u32>) {
34
+ // The start already folds gid.y in, so the stride must span every y row too;
35
+ // an x-only stride would send y = 0 lanes over columns the y >= 1 rows own.
36
+ let stride = nwg.x * nwg.y * WG;
37
  let start = (gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG) + gid.x;
38
  for (var col = start; col < params.cols; col = col + stride) {
39
  {% if op == "logsumexp" %}
 
86
  total = total + p;
87
  {% endif %}
88
  }
 
 
 
89
  {% if op == "l2" %}
90
  y[col] = {{ yv }}sqrt(total){{ vy }};
91
  {% elif op == "logsum" %}
build/webgpu/reduce-axis0-splitk-reduce.wgsl.jinja CHANGED
@@ -2,29 +2,16 @@
2
  // segments increases residency for tall matrices. Each (column, segment)
3
  // invocation reduces one row slice and writes partials[segment * columns +
4
  // column]. Adjacent column threads keep row reads coalesced.
5
- {% set castF32 = castF32 is defined and castF32 %}
6
  {% set xa = "f32(" if castF32 else "" %}
7
  {% set ax = ")" if castF32 else "" %}
8
- {% if usesF16Spec is defined and usesF16Spec %}
9
- enable f16;
10
- {% endif %}
11
  {{ env.wgsl.resourceDeclarations }}
12
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
13
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
14
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
15
- {% set logicalBoolIdentity = logicalBool is defined and logicalBool %}
16
  fn {{ name }}() -> {{ scalar }} {
17
- {% if scalar == "i32" %}
18
- return {{ "-2147483647i - 1i" if op == "max" else "2147483647i" }};
19
- {% elif scalar == "u32" %}
20
- return {{ "1u" if logicalBoolIdentity and op == "min" else "0u" if op == "max" else "4294967295u" }};
21
- {% else %}
22
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
23
  return bitcast<f32>(bits);
24
- {% endif %}
25
- }
26
- {%- endmacro %}
27
-
28
 
29
  const WG: u32 = {{ workgroupSize }}u;
30
  const SPLIT: u32 = {{ split }}u;
 
2
  // segments increases residency for tall matrices. Each (column, segment)
3
  // invocation reduces one row slice and writes partials[segment * columns +
4
  // column]. Adjacent column threads keep row reads coalesced.
 
5
  {% set xa = "f32(" if castF32 else "" %}
6
  {% set ax = ")" if castF32 else "" %}
 
 
 
7
  {{ env.wgsl.resourceDeclarations }}
8
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
9
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
10
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
 
11
  fn {{ name }}() -> {{ scalar }} {
 
 
 
 
 
12
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
13
  return bitcast<f32>(bits);
14
+ }{% endmacro %}
 
 
 
15
 
16
  const WG: u32 = {{ workgroupSize }}u;
17
  const SPLIT: u32 = {{ split }}u;
build/webgpu/reduce-axis0-tilecols.wgsl.jinja CHANGED
@@ -13,7 +13,6 @@
13
  {% set rowEnd = "params.rows" %}
14
  {% set elem = "x[inputBase + row * params.cols + col]" %}
15
  {% endif %}
16
- {% set castF32 = castF32 is defined and castF32 %}
17
  {% set intMode = intMode is defined and intMode %}
18
  {% set scalar = "f32" if castF32 else scalar %}
19
  {% if castF32 %}
@@ -21,9 +20,6 @@
21
  {% endif %}
22
  {% set yv = "f16(" if castF32 else "" %}
23
  {% set vy = ")" if castF32 else "" %}
24
- {% if usesF16Spec is defined and usesF16Spec %}
25
- enable f16;
26
- {% endif %}
27
  {{ env.wgsl.resourceDeclarations }}
28
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
29
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
@@ -38,15 +34,12 @@ fn {{ name }}() -> {{ scalar }} {
38
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
39
  return bitcast<f32>(bits);
40
  {% endif %}
41
- }
42
- {%- endmacro %}
43
-
44
  {% if not splitMode and not intMode and (op == "logsum" or op == "logsumexp") %}
45
  fn negative_infinity() -> f32 {
46
  var bits = 0xff800000u;
47
  return bitcast<f32>(bits);
48
  }
49
-
50
  {% endif %}
51
 
52
  const WG: u32 = {{ workgroupSize }}u;
@@ -69,7 +62,6 @@ fn is_nan_f32(value: f32) -> bool {
69
  let bits = bitcast<u32>(value);
70
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
71
  }
72
-
73
  {% endif %}
74
  @compute @workgroup_size(WG, 1, 1)
75
  fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
@@ -77,8 +69,9 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid:
77
  let col_lane = tid % TILE_COLS;
78
  let row_lane = tid / TILE_COLS;
79
  {% if splitMode %}
80
- // Narrow outputs: wg.x covers every column tile, wg.y is the axis segment.
81
- let col = wg.x * TILE_COLS + col_lane;
 
82
  let outputIndex = col;
83
  let in_bounds = col < params.outputs;
84
  let seg = wg.y;
 
13
  {% set rowEnd = "params.rows" %}
14
  {% set elem = "x[inputBase + row * params.cols + col]" %}
15
  {% endif %}
 
16
  {% set intMode = intMode is defined and intMode %}
17
  {% set scalar = "f32" if castF32 else scalar %}
18
  {% if castF32 %}
 
20
  {% endif %}
21
  {% set yv = "f16(" if castF32 else "" %}
22
  {% set vy = ")" if castF32 else "" %}
 
 
 
23
  {{ env.wgsl.resourceDeclarations }}
24
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
25
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
 
34
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
35
  return bitcast<f32>(bits);
36
  {% endif %}
37
+ }{% endmacro %}
 
 
38
  {% if not splitMode and not intMode and (op == "logsum" or op == "logsumexp") %}
39
  fn negative_infinity() -> f32 {
40
  var bits = 0xff800000u;
41
  return bitcast<f32>(bits);
42
  }
 
43
  {% endif %}
44
 
45
  const WG: u32 = {{ workgroupSize }}u;
 
62
  let bits = bitcast<u32>(value);
63
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
64
  }
 
65
  {% endif %}
66
  @compute @workgroup_size(WG, 1, 1)
67
  fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
 
69
  let col_lane = tid % TILE_COLS;
70
  let row_lane = tid / TILE_COLS;
71
  {% if splitMode %}
72
+ // Narrow outputs: wg.x (with wg.z carrying its fold overflow) covers every
73
+ // column tile, wg.y is the axis segment.
74
+ let col = (wg.x + wg.z * {{ DISPATCH_FOLD_WIDTH }}u) * TILE_COLS + col_lane;
75
  let outputIndex = col;
76
  let in_bounds = col < params.outputs;
77
  let seg = wg.y;
build/webgpu/reduce-flat-partial.wgsl.jinja CHANGED
@@ -3,33 +3,20 @@
3
  // in registers, reduce within each workgroup, and write one partial each. A
4
  // following combine pass folds the partials and applies the operation finalizer.
5
  //
6
- // Scalar f32 bindings keep arbitrary element counts legal. The grid-stride loop
7
- // manually assembles full vec4 groups from contiguous scalars, and one global
8
- // thread folds the final zero-to-three scalar elements exactly once.
9
  {% set intMode = intMode is defined and intMode %}
10
- {% set castF32 = castF32 is defined and castF32 %}
11
  {% set xa = "f32(" if castF32 else "" %}
12
  {% set ax = ")" if castF32 else "" %}
13
- {% if usesF16Spec is defined and usesF16Spec %}
14
- enable f16;
15
- {% endif %}
16
  {{ env.wgsl.resourceDeclarations }}
17
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
18
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
19
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
20
- {% set logicalBoolIdentity = logicalBool is defined and logicalBool %}
21
  fn {{ name }}() -> {{ scalar }} {
22
- {% if scalar == "i32" %}
23
- return {{ "-2147483647i - 1i" if op == "max" else "2147483647i" }};
24
- {% elif scalar == "u32" %}
25
- return {{ "1u" if logicalBoolIdentity and op == "min" else "0u" if op == "max" else "4294967295u" }};
26
- {% else %}
27
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
28
  return bitcast<f32>(bits);
29
- {% endif %}
30
- }
31
- {%- endmacro %}
32
-
33
 
34
  const WG: u32 = {{ workgroupSize }}u;
35
  var<workgroup> red: array<{{ "i32" if intMode else "f32" }}, WG>;
@@ -100,23 +87,23 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
100
  }
101
  red[tid] = acc;
102
  workgroupBarrier();
103
- var stride: u32 = WG / 2u;
 
 
 
 
 
104
  loop {
105
- if (stride == 0u) { break; }
106
- if (tid < stride) {
107
- {% if op == "max" %}
108
- red[tid] = max(red[tid], red[tid + stride]);
109
- {% elif op == "min" %}
110
- red[tid] = min(red[tid], red[tid + stride]);
111
- {% elif op == "prod" %}
112
- red[tid] = red[tid] * red[tid + stride];
113
- {% else %}
114
- red[tid] = red[tid] + red[tid + stride];
115
- {% endif %}
116
  }
117
- stride = stride / 2u;
118
  workgroupBarrier();
119
- }
 
120
  if (tid == 0u) {
121
  partials[wg.x] = red[0];
122
  }
 
3
  // in registers, reduce within each workgroup, and write one partial each. A
4
  // following combine pass folds the partials and applies the operation finalizer.
5
  //
6
+ // Scalar bindings support arbitrary element counts and a zero-to-three element
7
+ // tail. An aligned packed binding loads each group directly, preserving the
8
+ // component accumulation order and widening half storage before arithmetic.
9
  {% set intMode = intMode is defined and intMode %}
 
10
  {% set xa = "f32(" if castF32 else "" %}
11
  {% set ax = ")" if castF32 else "" %}
 
 
 
12
  {{ env.wgsl.resourceDeclarations }}
13
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
14
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
15
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
 
16
  fn {{ name }}() -> {{ scalar }} {
 
 
 
 
 
17
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
18
  return bitcast<f32>(bits);
19
+ }{% endmacro %}
 
 
 
20
 
21
  const WG: u32 = {{ workgroupSize }}u;
22
  var<workgroup> red: array<{{ "i32" if intMode else "f32" }}, WG>;
 
87
  }
88
  red[tid] = acc;
89
  workgroupBarrier();
90
+ {% macro wgsl_tree_fold_stmt(a, op, idx, svar) %}
91
+ {% if op == "max" or op == "min" %}
92
+ {{ a }}[{{ idx }}] = {{ op }}({{ a }}[{{ idx }}], {{ a }}[{{ idx }} + {{ svar }}]);{% else %}
93
+ {{ a }}[{{ idx }}] = {{ a }}[{{ idx }}] {{ "*" if op == "prod" else "+" }} {{ a }}[{{ idx }} + {{ svar }}];{% endif %}{% endmacro %}
94
+ {% macro wgsl_tree_fold(arrays, op="add", idx="lid", wg="WORKGROUP_SIZE", svar="stride", typed=false, form="tail", breakInline=false, bodyInline=false, barrierFirst=false, reuse=false) %}
95
+ var {{ svar }}{{ ": u32 " if typed else " " }}= {{ wg }} / 2u;
96
  loop {
97
+ if ({{ svar }} == 0u) { break; }
98
+ if ({{ idx }} < {{ svar }}) {
99
+ {% for a in arrays %}
100
+ {{ wgsl_tree_fold_stmt(a, op, idx, svar) }}
101
+ {% endfor %}
 
 
 
 
 
 
102
  }
103
+ {{ svar }} = {{ svar }} / 2u;
104
  workgroupBarrier();
105
+ }{% endmacro %}
106
+ {{ wgsl_tree_fold(["red"], op=op, idx="tid", wg="WG", typed=true, form="head", breakInline=true) }}
107
  if (tid == 0u) {
108
  partials[wg.x] = red[0];
109
  }
build/webgpu/reduce-multi-axis-coop.wgsl.jinja CHANGED
@@ -1,5 +1,4 @@
1
  {% macro reduce_multi_axis_offset(hasReduced) %}
2
-
3
  // One thread per output element walks the Cartesian product of the reduced axes,
4
  // linearized as reduce_linear. Specialized shapes make every input offset a sum
5
  // of coordinate-times-constant terms.
@@ -44,8 +43,7 @@ fn input_offset(out_index: u32{% if hasReduced %}, reduce_linear: u32{% endif %}
44
  {% set src.value = "(" ~ src.value ~ " * " ~ dataShape[a] ~ "u + coord" ~ a ~ ")" %}
45
  {% endfor %}
46
  return {{ src.value }};
47
- }
48
- {%- endmacro %}
49
  // Cooperative multi-axis reduction with one workgroup per output element.
50
  // Threads take strided shares of the reduced index range, then combine their
51
  // partials through a workgroup tree. The shared offset helper maps each reduced
@@ -53,17 +51,13 @@ fn input_offset(out_index: u32{% if hasReduced %}, reduce_linear: u32{% endif %}
53
  //
54
  // The tree changes f32 association relative to a left-to-right fold, so the two
55
  // accumulation orders need not be bit-identical.
56
- {% set castF32 = castF32 is defined and castF32 %}
57
- {% set intMode = intMode is defined and intMode %}
58
  {% set yv = "f16(" if castF32 else "" %}
59
  {% set vy = ")" if castF32 else "" %}
60
- {% if usesF16Spec is defined and usesF16Spec %}
61
- enable f16;
62
- {% endif %}
63
  {{ env.wgsl.resourceDeclarations }}
64
  {% set hasReducedAxis = namespace(value=false) %}
65
  {% for a in range(rank) %}{% if reduce[a] %}{% set hasReducedAxis.value = true %}{% endif %}{% endfor %}
66
- {{- reduce_multi_axis_offset(hasReducedAxis.value) }}
 
67
 
68
  {% set ACC = scalar if intMode else "f32" %}
69
  const WG: u32 = {{ workgroupSize }}u;
@@ -98,15 +92,33 @@ fn main(
98
  return;
99
  }
100
  let lid = lid3.x;
 
 
 
 
 
 
 
 
 
 
 
101
  var acc = {{ ACC }}(0);
 
102
 
103
  for (var r = lid; r < REDUCED; r = r + WG) {
104
  {% set at = "x[input_offset(i, r)]" %}
105
  {% if castF32 %}
106
  {% set at = "f32(" ~ at ~ ")" %}
107
  {% endif %}
 
 
 
108
  let value = {{ at }};
109
  acc = combine(acc, value * value);
 
 
 
110
  }
111
 
112
  // Fold the per-thread accumulators. WORKGROUP_SIZE is a power of two, and every
@@ -127,5 +139,19 @@ fn main(
127
  return;
128
  }
129
  let total = partials[0];
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  y[i] = {{ yv }}total{{ vy }};
 
131
  }
 
1
  {% macro reduce_multi_axis_offset(hasReduced) %}
 
2
  // One thread per output element walks the Cartesian product of the reduced axes,
3
  // linearized as reduce_linear. Specialized shapes make every input offset a sum
4
  // of coordinate-times-constant terms.
 
43
  {% set src.value = "(" ~ src.value ~ " * " ~ dataShape[a] ~ "u + coord" ~ a ~ ")" %}
44
  {% endfor %}
45
  return {{ src.value }};
46
+ }{% endmacro %}
 
47
  // Cooperative multi-axis reduction with one workgroup per output element.
48
  // Threads take strided shares of the reduced index range, then combine their
49
  // partials through a workgroup tree. The shared offset helper maps each reduced
 
51
  //
52
  // The tree changes f32 association relative to a left-to-right fold, so the two
53
  // accumulation orders need not be bit-identical.
 
 
54
  {% set yv = "f16(" if castF32 else "" %}
55
  {% set vy = ")" if castF32 else "" %}
 
 
 
56
  {{ env.wgsl.resourceDeclarations }}
57
  {% set hasReducedAxis = namespace(value=false) %}
58
  {% for a in range(rank) %}{% if reduce[a] %}{% set hasReducedAxis.value = true %}{% endif %}{% endfor %}
59
+
60
+ {{ reduce_multi_axis_offset(hasReducedAxis.value) }}
61
 
62
  {% set ACC = scalar if intMode else "f32" %}
63
  const WG: u32 = {{ workgroupSize }}u;
 
92
  return;
93
  }
94
  let lid = lid3.x;
95
+ {% if op == "mean" %}
96
+ if (REDUCED == 0u) {
97
+ {% if intMode %}
98
+ if (lid == 0u) { y[i] = {{ scalar }}(0); }
99
+ {% else %}
100
+ if (lid == 0u) { y[i] = {{ yv }}0.0{{ vy }}; }
101
+ {% endif %}
102
+ return;
103
+ }
104
+ var acc = {{ ACC }}(0);
105
+ {% else %}
106
  var acc = {{ ACC }}(0);
107
+ {% endif %}
108
 
109
  for (var r = lid; r < REDUCED; r = r + WG) {
110
  {% set at = "x[input_offset(i, r)]" %}
111
  {% if castF32 %}
112
  {% set at = "f32(" ~ at ~ ")" %}
113
  {% endif %}
114
+ {% if op == "l1" %}
115
+ acc = combine(acc, abs({{ at }}));
116
+ {% elif op == "l2" or op == "sumsquare" %}
117
  let value = {{ at }};
118
  acc = combine(acc, value * value);
119
+ {% else %}
120
+ acc = combine(acc, {{ at }});
121
+ {% endif %}
122
  }
123
 
124
  // Fold the per-thread accumulators. WORKGROUP_SIZE is a power of two, and every
 
139
  return;
140
  }
141
  let total = partials[0];
142
+ {% if op == "l2" %}
143
+ {% if intMode %}
144
+ y[i] = {{ scalar }}(sqrt(f32(total)));
145
+ {% else %}
146
+ y[i] = {{ yv }}sqrt(total){{ vy }};
147
+ {% endif %}
148
+ {% elif op == "mean" %}
149
+ {% if intMode %}
150
+ y[i] = total / {{ scalar }}(REDUCED);
151
+ {% else %}
152
+ y[i] = {{ yv }}total / f32(REDUCED){{ vy }};
153
+ {% endif %}
154
+ {% else %}
155
  y[i] = {{ yv }}total{{ vy }};
156
+ {% endif %}
157
  }
build/webgpu/reduce-noop-empty-axes.wgsl.jinja CHANGED
@@ -1,13 +1,16 @@
 
 
 
 
 
 
 
 
1
  {{ env.wgsl.resourceDeclarations }}
2
 
3
  @compute @workgroup_size({{ reduceWorkgroupSize }})
4
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
5
- // 2D-folded flat index: gid.y carries the high bits past the
6
- // per-axis dispatch fold width (outputs > 16.7M elements).
7
- let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ reduceWorkgroupSize }}u;
8
- if (i >= params.count) {
9
- return;
10
- }
11
  let v = x[i];
12
  y[i] = v * v;
13
  }
 
1
+ {% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
2
+ {% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width.
5
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};
6
+ if ({{ name }} >= {{ bound }}) {
7
+ return;
8
+ }{% endmacro %}
9
  {{ env.wgsl.resourceDeclarations }}
10
 
11
  @compute @workgroup_size({{ reduceWorkgroupSize }})
12
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
13
+ {{ flat_index_2d(reduceWorkgroupSize) }}
 
 
 
 
 
14
  let v = x[i];
15
  y[i] = v * v;
16
  }
build/webgpu/reduce-row-subgroup-rows.wgsl.jinja CHANGED
@@ -15,18 +15,12 @@
15
  // The finalizer takes the logarithm of the sum.
16
  {% endif %}
17
  // f16 storage widens before accumulation and narrows only at the final store.
18
- {% set castF32 = castF32 is defined and castF32 %}
19
  {% set isInt = scalar == "i32" or scalar == "u32" %}
20
  {% set accScalar = scalar if isInt else "f32" %}
21
  {% set xv = "vec4<f32>(" if castF32 else "" %}
22
  {% set vx = ")" if castF32 else "" %}
23
  {% set yv = "f16(" if castF32 else "" %}
24
  {% set vy = ")" if castF32 else "" %}
25
- enable subgroups;
26
- {% if usesF16Spec is defined and usesF16Spec %}
27
- enable f16;
28
- {% endif %}
29
- {{ env.wgsl.resourceDeclarations }}
30
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
31
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
32
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
@@ -40,15 +34,15 @@ fn {{ name }}() -> {{ scalar }} {
40
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
41
  return bitcast<f32>(bits);
42
  {% endif %}
43
- }
44
- {%- endmacro %}
45
-
46
 
47
  const WG: u32 = {{ workgroupSize }}u;
48
- {%- if op == "max" or op == "min" %}
 
49
  {{ wgsl_minmax_identity("reduction_identity", op, accScalar) }}
50
- {%- endif %}
51
- {%- if op == "logsumexp" %}
52
 
53
  const F32_MIN: f32 = -3.4028234663852886e38;
54
  const F32_MAX: f32 = 3.4028234663852886e38;
@@ -57,7 +51,7 @@ fn is_nan_f32(value: f32) -> bool {
57
  let bits = bitcast<u32>(value);
58
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
59
  }
60
- {%- endif %}
61
 
62
  @compute @workgroup_size(WG, 1, 1)
63
  fn main(@builtin(workgroup_id) wg: vec3<u32>,
@@ -110,58 +104,58 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>,
110
  let finiteOrInf = select(rowMax + log(sum), rowMax, hasPositiveInf);
111
  y[row] = {{ yv }}select(finiteOrInf, nanValue, hasNan){{ vy }};
112
  }
113
- {%- else %}
114
  {% if op == "max" or op == "min" %}
115
  let INIT: {{ accScalar }} = reduction_identity();
116
- {%- elif op == "prod" %}
117
  let INIT: {{ accScalar }} = {{ "1.0" if not isInt else accScalar ~ "(1)" }};
118
- {%- else %}
119
  let INIT: {{ accScalar }} = {{ "0.0" if not isInt else accScalar ~ "(0)" }};
120
- {%- endif %}
121
  var acc4 = vec4<{{ accScalar }}>(INIT);
122
  for (var c = sgLane; c < chunkLimit; c = c + sgSize) {
123
  let v = {{ xv }}x[base + c]{{ vx }};
124
- {%- if op == "max" %}
125
  acc4 = max(acc4, v);
126
- {%- elif op == "min" %}
127
  acc4 = min(acc4, v);
128
- {%- elif op == "prod" %}
129
  acc4 = acc4 * v;
130
- {%- elif op == "l1" %}
131
  acc4 = acc4 + abs(v);
132
- {%- elif op == "l2" or op == "sumsquare" %}
133
  acc4 = acc4 + v * v;
134
- {%- else %}
135
  acc4 = acc4 + v;
136
- {%- endif %}
137
  }
138
- {%- if op == "max" %}
139
  let acc = max(max(acc4.x, acc4.y), max(acc4.z, acc4.w));
140
  let total = subgroupMax(acc);
141
- {%- elif op == "min" %}
142
  let acc = min(min(acc4.x, acc4.y), min(acc4.z, acc4.w));
143
  let total = subgroupMin(acc);
144
- {%- elif op == "prod" %}
145
  let acc = (acc4.x * acc4.y) * (acc4.z * acc4.w);
146
  let total = subgroupMul(acc);
147
- {%- else %}
148
  let acc = (acc4.x + acc4.y) + (acc4.z + acc4.w);
149
  let total = subgroupAdd(acc);
150
- {%- endif %}
151
  if (rowValid && sgLane == 0u) {
152
- {%- if op == "mean" and isInt %}
153
  y[row] = total / {{ accScalar }}(params.chunkCount * 4u);
154
- {%- elif op == "mean" %}
155
  y[row] = {{ yv }}total / f32(params.chunkCount * 4u){{ vy }};
156
- {%- elif op == "l2" and isInt %}
157
  y[row] = {{ accScalar }}(sqrt(f32(total)));
158
- {%- elif op == "l2" %}
159
  y[row] = {{ yv }}sqrt(total){{ vy }};
160
- {%- elif op == "logsum" %}
161
  y[row] = {{ yv }}log(total){{ vy }};
162
- {%- else %}
163
  y[row] = {{ yv }}total{{ vy }};
164
- {%- endif %}
165
  }
166
- {%- endif %}
167
  }
 
15
  // The finalizer takes the logarithm of the sum.
16
  {% endif %}
17
  // f16 storage widens before accumulation and narrows only at the final store.
 
18
  {% set isInt = scalar == "i32" or scalar == "u32" %}
19
  {% set accScalar = scalar if isInt else "f32" %}
20
  {% set xv = "vec4<f32>(" if castF32 else "" %}
21
  {% set vx = ")" if castF32 else "" %}
22
  {% set yv = "f16(" if castF32 else "" %}
23
  {% set vy = ")" if castF32 else "" %}
 
 
 
 
 
24
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
25
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
26
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
 
34
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
35
  return bitcast<f32>(bits);
36
  {% endif %}
37
+ }{% endmacro %}
38
+ enable subgroups;
39
+ {{ env.wgsl.resourceDeclarations }}
40
 
41
  const WG: u32 = {{ workgroupSize }}u;
42
+ {% if op == "max" or op == "min" %}
43
+
44
  {{ wgsl_minmax_identity("reduction_identity", op, accScalar) }}
45
+ {% elif op == "logsumexp" %}
 
46
 
47
  const F32_MIN: f32 = -3.4028234663852886e38;
48
  const F32_MAX: f32 = 3.4028234663852886e38;
 
51
  let bits = bitcast<u32>(value);
52
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
53
  }
54
+ {% endif %}
55
 
56
  @compute @workgroup_size(WG, 1, 1)
57
  fn main(@builtin(workgroup_id) wg: vec3<u32>,
 
104
  let finiteOrInf = select(rowMax + log(sum), rowMax, hasPositiveInf);
105
  y[row] = {{ yv }}select(finiteOrInf, nanValue, hasNan){{ vy }};
106
  }
107
+ {% else %}
108
  {% if op == "max" or op == "min" %}
109
  let INIT: {{ accScalar }} = reduction_identity();
110
+ {% elif op == "prod" %}
111
  let INIT: {{ accScalar }} = {{ "1.0" if not isInt else accScalar ~ "(1)" }};
112
+ {% else %}
113
  let INIT: {{ accScalar }} = {{ "0.0" if not isInt else accScalar ~ "(0)" }};
114
+ {% endif %}
115
  var acc4 = vec4<{{ accScalar }}>(INIT);
116
  for (var c = sgLane; c < chunkLimit; c = c + sgSize) {
117
  let v = {{ xv }}x[base + c]{{ vx }};
118
+ {% if op == "max" %}
119
  acc4 = max(acc4, v);
120
+ {% elif op == "min" %}
121
  acc4 = min(acc4, v);
122
+ {% elif op == "prod" %}
123
  acc4 = acc4 * v;
124
+ {% elif op == "l1" %}
125
  acc4 = acc4 + abs(v);
126
+ {% elif op == "l2" or op == "sumsquare" %}
127
  acc4 = acc4 + v * v;
128
+ {% else %}
129
  acc4 = acc4 + v;
130
+ {% endif %}
131
  }
132
+ {% if op == "max" %}
133
  let acc = max(max(acc4.x, acc4.y), max(acc4.z, acc4.w));
134
  let total = subgroupMax(acc);
135
+ {% elif op == "min" %}
136
  let acc = min(min(acc4.x, acc4.y), min(acc4.z, acc4.w));
137
  let total = subgroupMin(acc);
138
+ {% elif op == "prod" %}
139
  let acc = (acc4.x * acc4.y) * (acc4.z * acc4.w);
140
  let total = subgroupMul(acc);
141
+ {% else %}
142
  let acc = (acc4.x + acc4.y) + (acc4.z + acc4.w);
143
  let total = subgroupAdd(acc);
144
+ {% endif %}
145
  if (rowValid && sgLane == 0u) {
146
+ {% if op == "mean" and isInt %}
147
  y[row] = total / {{ accScalar }}(params.chunkCount * 4u);
148
+ {% elif op == "mean" %}
149
  y[row] = {{ yv }}total / f32(params.chunkCount * 4u){{ vy }};
150
+ {% elif op == "l2" and isInt %}
151
  y[row] = {{ accScalar }}(sqrt(f32(total)));
152
+ {% elif op == "l2" %}
153
  y[row] = {{ yv }}sqrt(total){{ vy }};
154
+ {% elif op == "logsum" %}
155
  y[row] = {{ yv }}log(total){{ vy }};
156
+ {% else %}
157
  y[row] = {{ yv }}total{{ vy }};
158
+ {% endif %}
159
  }
160
+ {% endif %}
161
  }
build/webgpu/reduce-row-subgroup.wgsl.jinja CHANGED
@@ -15,17 +15,11 @@
15
  // IEEE-754 bit patterns because WGSL rejects infinite constants.
16
  {% endif %}
17
  // f16 storage widens before accumulation and narrows only at the final store.
18
- {% set castF32 = castF32 is defined and castF32 %}
19
  {% set scalar = "f32" if castF32 else scalar %}
20
  {% set xv = ("vec4<f32>(" if vec4 else "f32(") if castF32 else "" %}
21
  {% set vx = ")" if castF32 else "" %}
22
  {% set yv = "f16(" if castF32 else "" %}
23
  {% set vy = ")" if castF32 else "" %}
24
- enable subgroups;
25
- {% if usesF16Spec is defined and usesF16Spec %}
26
- enable f16;
27
- {% endif %}
28
- {{ env.wgsl.resourceDeclarations }}
29
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
30
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
31
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
@@ -39,15 +33,15 @@ fn {{ name }}() -> {{ scalar }} {
39
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
40
  return bitcast<f32>(bits);
41
  {% endif %}
42
- }
43
- {%- endmacro %}
44
-
45
 
46
  const WG: u32 = {{ workgroupSize }}u;
47
- {%- if op == "max" or op == "min" %}
 
48
  {{ wgsl_minmax_identity("reduction_identity", op, scalar) }}
49
- {%- endif %}
50
- {%- if op == "logsumexp" %}
51
 
52
  const F32_MIN: f32 = -3.4028234663852886e38;
53
  const F32_MAX: f32 = 3.4028234663852886e38;
@@ -56,7 +50,7 @@ fn is_nan_f32(value: f32) -> bool {
56
  let bits = bitcast<u32>(value);
57
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
58
  }
59
- {%- endif %}
60
 
61
  var<workgroup> wgPartial: array<{{ scalar }}, WG>;
62
 
@@ -77,20 +71,19 @@ fn {{ name }}(value: {{ scalar }}, sgLid: u32, sgId: u32, numSg: u32) -> {{ scal
77
  workgroupBarrier();
78
  return total;
79
  }
80
- {%- endmacro %}
81
- {%- if op == "max" %}
82
  {{ emit_reduce("reduce_row", "subgroupMax", "total = max(total, wgPartial[i]);") }}
83
- {%- elif op == "min" %}
84
  {{ emit_reduce("reduce_row", "subgroupMin", "total = min(total, wgPartial[i]);") }}
85
- {%- elif op == "prod" %}
86
  {{ emit_reduce("reduce_row", "subgroupMul", "total = total * wgPartial[i];") }}
87
- {%- elif op == "logsumexp" %}
88
  {{ emit_reduce("reduce_row_add", "subgroupAdd", "total = total + wgPartial[i];") }}
89
  {{ emit_reduce("reduce_row_max", "subgroupMax", "total = max(total, wgPartial[i]);") }}
90
- {%- else %}
91
  {{ emit_reduce("reduce_row", "subgroupAdd", "total = total + wgPartial[i];") }}
92
- {%- endif %}
93
-
94
  @compute @workgroup_size(WG, 1, 1)
95
  fn main(@builtin(workgroup_id) wg: vec3<u32>,
96
  @builtin(local_invocation_id) lid: vec3<u32>,
@@ -103,13 +96,13 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>,
103
  }
104
  let tid = lid.x;
105
  let base = row * params.chunkCount;
106
- {%- if op == "logsumexp" %}
107
  var localMax = F32_MIN;
108
  var localNan = 0.0;
109
  var localNanValue = 0.0;
110
  for (var c = tid; c < params.chunkCount; c = c + WG) {
111
  let v = {{ xv }}x[base + c]{{ vx }};
112
- {%- if vec4 %}
113
  {% for comp in ["x", "y", "z", "w"] %}
114
  if (is_nan_f32(v.{{ comp }})) {
115
  localNan = 1.0;
@@ -117,7 +110,7 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>,
117
  } else {
118
  localMax = max(localMax, v.{{ comp }});
119
  }
120
- {%- endfor %}
121
  {% else %}
122
  if (is_nan_f32(v)) {
123
  localNan = 1.0;
@@ -125,7 +118,7 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>,
125
  } else {
126
  localMax = max(localMax, v);
127
  }
128
- {%- endif %}
129
  }
130
  let rowMax = reduce_row_max(localMax, sgLid, sgId, numSg);
131
  let nanCount = reduce_row_add(localNan, sgLid, sgId, numSg);
@@ -135,83 +128,83 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>,
135
  var acc = 0.0;
136
  for (var c = tid; c < params.chunkCount; c = c + WG) {
137
  let v = {{ xv }}x[base + c]{{ vx }};
138
- {%- if vec4 %}
139
  let e = select(exp(v - vec4<f32>(rowMax)), vec4<f32>(0.0), hasPositiveInf || hasNan);
140
  acc = acc + ((e.x + e.y) + (e.z + e.w));
141
- {%- else %}
142
  acc = acc + select(exp(v - rowMax), 0.0, hasPositiveInf || hasNan);
143
- {%- endif %}
144
  }
145
  let sum = reduce_row_add(acc, sgLid, sgId, numSg);
146
  if (tid == 0u) {
147
  let finiteOrInf = select(rowMax + log(sum), rowMax, hasPositiveInf);
148
  y[row] = {{ yv }}select(finiteOrInf, nanValue, hasNan){{ vy }};
149
  }
150
- {%- else %}
151
  {% if op == "max" or op == "min" %}
152
  let INIT: {{ scalar }} = reduction_identity();
153
- {%- elif op == "prod" %}
154
  let INIT: f32 = 1.0;
155
- {%- else %}
156
  let INIT: f32 = 0.0;
157
- {%- endif %}
158
  {% if vec4 %}
159
  var acc4 = vec4<{{ scalar }}>(INIT);
160
  for (var c = tid; c < params.chunkCount; c = c + WG) {
161
  let v = {{ xv }}x[base + c]{{ vx }};
162
- {%- if op == "max" %}
163
  acc4 = max(acc4, v);
164
- {%- elif op == "min" %}
165
  acc4 = min(acc4, v);
166
- {%- elif op == "prod" %}
167
  acc4 = acc4 * v;
168
- {%- elif op == "l1" %}
169
  acc4 = acc4 + abs(v);
170
- {%- elif op == "l2" or op == "sumsquare" %}
171
  acc4 = acc4 + v * v;
172
- {%- else %}
173
  acc4 = acc4 + v;
174
- {%- endif %}
175
  }
176
- {%- if op == "max" %}
177
  let acc = max(max(acc4.x, acc4.y), max(acc4.z, acc4.w));
178
- {%- elif op == "min" %}
179
  let acc = min(min(acc4.x, acc4.y), min(acc4.z, acc4.w));
180
- {%- elif op == "prod" %}
181
  let acc = (acc4.x * acc4.y) * (acc4.z * acc4.w);
182
- {%- else %}
183
  let acc = (acc4.x + acc4.y) + (acc4.z + acc4.w);
184
- {%- endif %}
185
  {% else %}
186
  var acc = INIT;
187
  for (var c = tid; c < params.chunkCount; c = c + WG) {
188
  let v = {{ xv }}x[base + c]{{ vx }};
189
- {%- if op == "max" %}
190
  acc = max(acc, v);
191
- {%- elif op == "min" %}
192
  acc = min(acc, v);
193
- {%- elif op == "prod" %}
194
  acc = acc * v;
195
- {%- elif op == "l1" %}
196
  acc = acc + abs(v);
197
- {%- elif op == "l2" or op == "sumsquare" %}
198
  acc = acc + v * v;
199
- {%- else %}
200
  acc = acc + v;
201
- {%- endif %}
202
  }
203
- {%- endif %}
204
  let total = reduce_row(acc, sgLid, sgId, numSg);
205
  if (tid == 0u) {
206
- {%- if op == "mean" %}
207
  y[row] = {{ yv }}total / f32(params.cols){{ vy }};
208
- {%- elif op == "l2" %}
209
  y[row] = {{ yv }}sqrt(total){{ vy }};
210
- {%- elif op == "logsum" %}
211
  y[row] = {{ yv }}log(total){{ vy }};
212
- {%- else %}
213
  y[row] = {{ yv }}total{{ vy }};
214
- {%- endif %}
215
  }
216
- {%- endif %}
217
  }
 
15
  // IEEE-754 bit patterns because WGSL rejects infinite constants.
16
  {% endif %}
17
  // f16 storage widens before accumulation and narrows only at the final store.
 
18
  {% set scalar = "f32" if castF32 else scalar %}
19
  {% set xv = ("vec4<f32>(" if vec4 else "f32(") if castF32 else "" %}
20
  {% set vx = ")" if castF32 else "" %}
21
  {% set yv = "f16(" if castF32 else "" %}
22
  {% set vy = ")" if castF32 else "" %}
 
 
 
 
 
23
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
24
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
25
  * evaluation, so an f32 identity is constructed from its IEEE-754 bits. */
 
33
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
34
  return bitcast<f32>(bits);
35
  {% endif %}
36
+ }{% endmacro %}
37
+ enable subgroups;
38
+ {{ env.wgsl.resourceDeclarations }}
39
 
40
  const WG: u32 = {{ workgroupSize }}u;
41
+ {% if op == "max" or op == "min" %}
42
+
43
  {{ wgsl_minmax_identity("reduction_identity", op, scalar) }}
44
+ {% elif op == "logsumexp" %}
 
45
 
46
  const F32_MIN: f32 = -3.4028234663852886e38;
47
  const F32_MAX: f32 = 3.4028234663852886e38;
 
50
  let bits = bitcast<u32>(value);
51
  return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
52
  }
53
+ {% endif %}
54
 
55
  var<workgroup> wgPartial: array<{{ scalar }}, WG>;
56
 
 
71
  workgroupBarrier();
72
  return total;
73
  }
74
+ {% endmacro %}
75
+ {% if op == "max" %}
76
  {{ emit_reduce("reduce_row", "subgroupMax", "total = max(total, wgPartial[i]);") }}
77
+ {% elif op == "min" %}
78
  {{ emit_reduce("reduce_row", "subgroupMin", "total = min(total, wgPartial[i]);") }}
79
+ {% elif op == "prod" %}
80
  {{ emit_reduce("reduce_row", "subgroupMul", "total = total * wgPartial[i];") }}
81
+ {% elif op == "logsumexp" %}
82
  {{ emit_reduce("reduce_row_add", "subgroupAdd", "total = total + wgPartial[i];") }}
83
  {{ emit_reduce("reduce_row_max", "subgroupMax", "total = max(total, wgPartial[i]);") }}
84
+ {% else %}
85
  {{ emit_reduce("reduce_row", "subgroupAdd", "total = total + wgPartial[i];") }}
86
+ {% endif %}
 
87
  @compute @workgroup_size(WG, 1, 1)
88
  fn main(@builtin(workgroup_id) wg: vec3<u32>,
89
  @builtin(local_invocation_id) lid: vec3<u32>,
 
96
  }
97
  let tid = lid.x;
98
  let base = row * params.chunkCount;
99
+ {% if op == "logsumexp" %}
100
  var localMax = F32_MIN;
101
  var localNan = 0.0;
102
  var localNanValue = 0.0;
103
  for (var c = tid; c < params.chunkCount; c = c + WG) {
104
  let v = {{ xv }}x[base + c]{{ vx }};
105
+ {% if vec4 %}
106
  {% for comp in ["x", "y", "z", "w"] %}
107
  if (is_nan_f32(v.{{ comp }})) {
108
  localNan = 1.0;
 
110
  } else {
111
  localMax = max(localMax, v.{{ comp }});
112
  }
113
+ {% endfor %}
114
  {% else %}
115
  if (is_nan_f32(v)) {
116
  localNan = 1.0;
 
118
  } else {
119
  localMax = max(localMax, v);
120
  }
121
+ {% endif %}
122
  }
123
  let rowMax = reduce_row_max(localMax, sgLid, sgId, numSg);
124
  let nanCount = reduce_row_add(localNan, sgLid, sgId, numSg);
 
128
  var acc = 0.0;
129
  for (var c = tid; c < params.chunkCount; c = c + WG) {
130
  let v = {{ xv }}x[base + c]{{ vx }};
131
+ {% if vec4 %}
132
  let e = select(exp(v - vec4<f32>(rowMax)), vec4<f32>(0.0), hasPositiveInf || hasNan);
133
  acc = acc + ((e.x + e.y) + (e.z + e.w));
134
+ {% else %}
135
  acc = acc + select(exp(v - rowMax), 0.0, hasPositiveInf || hasNan);
136
+ {% endif %}
137
  }
138
  let sum = reduce_row_add(acc, sgLid, sgId, numSg);
139
  if (tid == 0u) {
140
  let finiteOrInf = select(rowMax + log(sum), rowMax, hasPositiveInf);
141
  y[row] = {{ yv }}select(finiteOrInf, nanValue, hasNan){{ vy }};
142
  }
143
+ {% else %}
144
  {% if op == "max" or op == "min" %}
145
  let INIT: {{ scalar }} = reduction_identity();
146
+ {% elif op == "prod" %}
147
  let INIT: f32 = 1.0;
148
+ {% else %}
149
  let INIT: f32 = 0.0;
150
+ {% endif %}
151
  {% if vec4 %}
152
  var acc4 = vec4<{{ scalar }}>(INIT);
153
  for (var c = tid; c < params.chunkCount; c = c + WG) {
154
  let v = {{ xv }}x[base + c]{{ vx }};
155
+ {% if op == "max" %}
156
  acc4 = max(acc4, v);
157
+ {% elif op == "min" %}
158
  acc4 = min(acc4, v);
159
+ {% elif op == "prod" %}
160
  acc4 = acc4 * v;
161
+ {% elif op == "l1" %}
162
  acc4 = acc4 + abs(v);
163
+ {% elif op == "l2" or op == "sumsquare" %}
164
  acc4 = acc4 + v * v;
165
+ {% else %}
166
  acc4 = acc4 + v;
167
+ {% endif %}
168
  }
169
+ {% if op == "max" %}
170
  let acc = max(max(acc4.x, acc4.y), max(acc4.z, acc4.w));
171
+ {% elif op == "min" %}
172
  let acc = min(min(acc4.x, acc4.y), min(acc4.z, acc4.w));
173
+ {% elif op == "prod" %}
174
  let acc = (acc4.x * acc4.y) * (acc4.z * acc4.w);
175
+ {% else %}
176
  let acc = (acc4.x + acc4.y) + (acc4.z + acc4.w);
177
+ {% endif %}
178
  {% else %}
179
  var acc = INIT;
180
  for (var c = tid; c < params.chunkCount; c = c + WG) {
181
  let v = {{ xv }}x[base + c]{{ vx }};
182
+ {% if op == "max" %}
183
  acc = max(acc, v);
184
+ {% elif op == "min" %}
185
  acc = min(acc, v);
186
+ {% elif op == "prod" %}
187
  acc = acc * v;
188
+ {% elif op == "l1" %}
189
  acc = acc + abs(v);
190
+ {% elif op == "l2" or op == "sumsquare" %}
191
  acc = acc + v * v;
192
+ {% else %}
193
  acc = acc + v;
194
+ {% endif %}
195
  }
196
+ {% endif %}
197
  let total = reduce_row(acc, sgLid, sgId, numSg);
198
  if (tid == 0u) {
199
+ {% if op == "mean" %}
200
  y[row] = {{ yv }}total / f32(params.cols){{ vy }};
201
+ {% elif op == "l2" %}
202
  y[row] = {{ yv }}sqrt(total){{ vy }};
203
+ {% elif op == "logsum" %}
204
  y[row] = {{ yv }}log(total){{ vy }};
205
+ {% else %}
206
  y[row] = {{ yv }}total{{ vy }};
207
+ {% endif %}
208
  }
209
+ {% endif %}
210
  }
build/webgpu/reduce-row-tree.wgsl.jinja CHANGED
@@ -16,15 +16,11 @@
16
  // then narrows only at the final store.
17
  {% set isVec4 = vec4 is defined and vec4 %}
18
  {% set rowIsEmpty = "params.chunkCount == 0u" if isVec4 else "params.cols == 0u" %}
19
- {% set castF32 = castF32 is defined and castF32 %}
20
  {% set scalar = "f32" if castF32 else scalar %}
21
  {% set xv = ("vec4<f32>(" if vec4 else "f32(") if castF32 else "" %}
22
  {% set vx = ")" if castF32 else "" %}
23
  {% set yv = "f16(" if castF32 else "" %}
24
  {% set vy = ")" if castF32 else "" %}
25
- {% if usesF16Spec is defined and usesF16Spec %}
26
- enable f16;
27
- {% endif %}
28
  {{ env.wgsl.resourceDeclarations }}
29
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
30
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
@@ -39,15 +35,12 @@ fn {{ name }}() -> {{ scalar }} {
39
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
40
  return bitcast<f32>(bits);
41
  {% endif %}
42
- }
43
- {%- endmacro %}
44
-
45
  {% if scalar != "i32" and scalar != "u32" and (op == "logsum" or op == "logsumexp") %}
46
  fn negative_infinity() -> f32 {
47
  var bits = 0xff800000u;
48
  return bitcast<f32>(bits);
49
  }
50
-
51
  {% endif %}
52
 
53
  const WG: u32 = {{ workgroupSize }}u;
@@ -57,8 +50,8 @@ const F32_MIN: f32 = -3.4028234663852886e38;
57
  const F32_MAX: f32 = 3.4028234663852886e38;
58
 
59
  var<workgroup> partial: array<f32, WG>;
60
- {% macro wgsl_tree_reduce_f32(name, mode, buffer="partial", wg="WG", trailingBarrier=true) %}
61
- fn {{ name }}(value: f32, tid: u32) -> f32 {
62
  {{ buffer }}[tid] = value;
63
  workgroupBarrier();
64
  // Ceil-halving keeps every lane when the workgroup size is not a power of
@@ -84,20 +77,15 @@ fn {{ name }}(value: f32, tid: u32) -> f32 {
84
  // slot 0 here, so the next call's first store must not run until all lanes have read it.
85
  // `trailingBarrier=false` is safe only when the buffer is never written again before kernel exit.
86
  let reduced = {{ buffer }}[0];
87
- {% if trailingBarrier %}
88
  workgroupBarrier();
89
- {% endif %}
90
  return reduced;
91
  }
92
  {% endmacro %}
93
-
94
  {{ wgsl_tree_reduce_f32("reduce_sum", "add", "partial", "WG") }}
95
  {{ wgsl_tree_reduce_f32("reduce_max", "max", "partial", "WG") }}
96
-
97
  fn is_nan_f32(value: f32) -> bool {
98
  let bits = bitcast<u32>(value);
99
- return (bits & 0x7f800000u) == 0x7f800000u
100
- && (bits & 0x007fffffu) != 0u;
101
  }
102
  {% else %}
103
  {% set is_int = scalar == "i32" or scalar == "u32" %}
@@ -216,13 +204,14 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>,
216
  {% endif %}
217
  return;
218
  }
 
219
  {% elif op == "logsum" %}
220
  if ({{ rowIsEmpty }}) {
221
  if (tid == 0u) { y[row] = {{ yv }}negative_infinity(){{ vy }}; }
222
  return;
223
  }
224
- {% endif %}
225
 
 
226
  {% if vec4 %}
227
  var acc4 = vec4<{{ accType }}>(identity());
228
  for (var col = tid; col < params.chunkCount; col = col + WG) {
 
16
  // then narrows only at the final store.
17
  {% set isVec4 = vec4 is defined and vec4 %}
18
  {% set rowIsEmpty = "params.chunkCount == 0u" if isVec4 else "params.cols == 0u" %}
 
19
  {% set scalar = "f32" if castF32 else scalar %}
20
  {% set xv = ("vec4<f32>(" if vec4 else "f32(") if castF32 else "" %}
21
  {% set vx = ")" if castF32 else "" %}
22
  {% set yv = "f16(" if castF32 else "" %}
23
  {% set vy = ")" if castF32 else "" %}
 
 
 
24
  {{ env.wgsl.resourceDeclarations }}
25
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
26
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
 
35
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
36
  return bitcast<f32>(bits);
37
  {% endif %}
38
+ }{% endmacro %}
 
 
39
  {% if scalar != "i32" and scalar != "u32" and (op == "logsum" or op == "logsumexp") %}
40
  fn negative_infinity() -> f32 {
41
  var bits = 0xff800000u;
42
  return bitcast<f32>(bits);
43
  }
 
44
  {% endif %}
45
 
46
  const WG: u32 = {{ workgroupSize }}u;
 
50
  const F32_MAX: f32 = 3.4028234663852886e38;
51
 
52
  var<workgroup> partial: array<f32, WG>;
53
+ {% macro wgsl_tree_reduce_f32(name, mode, buffer="partial", wg="WG", trailingBarrier=true, valueType="f32") %}
54
+ fn {{ name }}(value: {{ valueType }}, tid: u32) -> {{ valueType }} {
55
  {{ buffer }}[tid] = value;
56
  workgroupBarrier();
57
  // Ceil-halving keeps every lane when the workgroup size is not a power of
 
77
  // slot 0 here, so the next call's first store must not run until all lanes have read it.
78
  // `trailingBarrier=false` is safe only when the buffer is never written again before kernel exit.
79
  let reduced = {{ buffer }}[0];
 
80
  workgroupBarrier();
 
81
  return reduced;
82
  }
83
  {% endmacro %}
 
84
  {{ wgsl_tree_reduce_f32("reduce_sum", "add", "partial", "WG") }}
85
  {{ wgsl_tree_reduce_f32("reduce_max", "max", "partial", "WG") }}
 
86
  fn is_nan_f32(value: f32) -> bool {
87
  let bits = bitcast<u32>(value);
88
+ return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
 
89
  }
90
  {% else %}
91
  {% set is_int = scalar == "i32" or scalar == "u32" %}
 
204
  {% endif %}
205
  return;
206
  }
207
+
208
  {% elif op == "logsum" %}
209
  if ({{ rowIsEmpty }}) {
210
  if (tid == 0u) { y[row] = {{ yv }}negative_infinity(){{ vy }}; }
211
  return;
212
  }
 
213
 
214
+ {% endif %}
215
  {% if vec4 %}
216
  var acc4 = vec4<{{ accType }}>(identity());
217
  for (var col = tid; col < params.chunkCount; col = col + WG) {
build/webgpu/reduce-serial-axis.wgsl.jinja CHANGED
@@ -1,5 +1,4 @@
1
  {% macro reduce_multi_axis_offset(hasReduced) %}
2
-
3
  // One thread per output element walks the Cartesian product of the reduced axes,
4
  // linearized as reduce_linear. Specialized shapes make every input offset a sum
5
  // of coordinate-times-constant terms.
@@ -44,18 +43,20 @@ fn input_offset(out_index: u32{% if hasReduced %}, reduce_linear: u32{% endif %}
44
  {% set src.value = "(" ~ src.value ~ " * " ~ dataShape[a] ~ "u + coord" ~ a ~ ")" %}
45
  {% endfor %}
46
  return {{ src.value }};
47
- }
48
- {%- endmacro %}
49
  // Serial one-thread-per-output reduction for the no-feature tier. f16 storage
50
  // is widened before every accumulation and narrowed only for the final store.
51
- {% set castF32 = castF32 is defined and castF32 %}
52
  {% set logicalBool = logicalBool is defined and logicalBool %}
53
- {% set intMode = intMode is defined and intMode %}
54
  {% set yv = "f16(" if castF32 else "" %}
55
  {% set vy = ")" if castF32 else "" %}
56
- {% if usesF16Spec is defined and usesF16Spec %}
57
- enable f16;
58
- {% endif %}
 
 
 
 
 
59
  {{ env.wgsl.resourceDeclarations }}
60
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
61
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
@@ -70,15 +71,12 @@ fn {{ name }}() -> {{ scalar }} {
70
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
71
  return bitcast<f32>(bits);
72
  {% endif %}
73
- }
74
- {%- endmacro %}
75
-
76
  {% if not intMode and (op == "logsum" or op == "logsumexp") %}
77
  fn negative_infinity() -> f32 {
78
  var bits = 0xff800000u;
79
  return bitcast<f32>(bits);
80
  }
81
-
82
  {% endif %}
83
  {% if op == "max" or op == "min" %}
84
 
@@ -131,7 +129,8 @@ fn input_offset(out_index: u32, reduce_index: u32) -> u32 {
131
  {% if indexing == "multiaxis" %}
132
  {% set hasReducedAxis = namespace(value=false) %}
133
  {% for a in range(rank) %}{% if reduce[a] %}{% set hasReducedAxis.value = true %}{% endif %}{% endfor %}
134
- {{- reduce_multi_axis_offset(hasReducedAxis.value) }}
 
135
  {% endif %}
136
  {% if indexing == "multiaxis" %}
137
  {% set mcount = namespace(value=1) %}
@@ -166,12 +165,7 @@ fn input_offset(out_index: u32, reduce_index: u32) -> u32 {
166
 
167
  @compute @workgroup_size({{ reduceWorkgroupSize }})
168
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
169
- // 2D-folded flat index: gid.y carries the high bits past the
170
- // per-axis dispatch fold width (outputs > 16.7M elements).
171
- let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ reduceWorkgroupSize }}u;
172
- if (i >= params.outCount) {
173
- return;
174
- }
175
  {% if op == "logsumexp" %}
176
  {% if intMode %}
177
  // Integer logsumexp widens each element for exp/log, then truncates the result
 
1
  {% macro reduce_multi_axis_offset(hasReduced) %}
 
2
  // One thread per output element walks the Cartesian product of the reduced axes,
3
  // linearized as reduce_linear. Specialized shapes make every input offset a sum
4
  // of coordinate-times-constant terms.
 
43
  {% set src.value = "(" ~ src.value ~ " * " ~ dataShape[a] ~ "u + coord" ~ a ~ ")" %}
44
  {% endfor %}
45
  return {{ src.value }};
46
+ }{% endmacro %}
 
47
  // Serial one-thread-per-output reduction for the no-feature tier. f16 storage
48
  // is widened before every accumulation and narrowed only for the final store.
 
49
  {% set logicalBool = logicalBool is defined and logicalBool %}
 
50
  {% set yv = "f16(" if castF32 else "" %}
51
  {% set vy = ")" if castF32 else "" %}
52
+ {% macro flat_index_2d(workgroupSize, name="i", bound="params.count", guardInline=false) %}
53
+ {% set wgTerm = workgroupSize ~ "u" if workgroupSize is number else workgroupSize %}
54
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
55
+ // per-axis workgroup fold width.
56
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ wgTerm }};
57
+ if ({{ name }} >= {{ bound }}) {
58
+ return;
59
+ }{% endmacro %}
60
  {{ env.wgsl.resourceDeclarations }}
61
  {% macro wgsl_minmax_identity(name, op, scalar="f32") %}
62
  /* Exact {{ op }} reduction identity. WGSL rejects infinity during constant
 
71
  var bits = {{ "0xff800000u" if op == "max" else "0x7f800000u" }};
72
  return bitcast<f32>(bits);
73
  {% endif %}
74
+ }{% endmacro %}
 
 
75
  {% if not intMode and (op == "logsum" or op == "logsumexp") %}
76
  fn negative_infinity() -> f32 {
77
  var bits = 0xff800000u;
78
  return bitcast<f32>(bits);
79
  }
 
80
  {% endif %}
81
  {% if op == "max" or op == "min" %}
82
 
 
129
  {% if indexing == "multiaxis" %}
130
  {% set hasReducedAxis = namespace(value=false) %}
131
  {% for a in range(rank) %}{% if reduce[a] %}{% set hasReducedAxis.value = true %}{% endif %}{% endfor %}
132
+
133
+ {{ reduce_multi_axis_offset(hasReducedAxis.value) }}
134
  {% endif %}
135
  {% if indexing == "multiaxis" %}
136
  {% set mcount = namespace(value=1) %}
 
165
 
166
  @compute @workgroup_size({{ reduceWorkgroupSize }})
167
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
168
+ {{ flat_index_2d(reduceWorkgroupSize, bound="params.outCount") }}
 
 
 
 
 
169
  {% if op == "logsumexp" %}
170
  {% if intMode %}
171
  // Integer logsumexp widens each element for exp/log, then truncates the result
build/webgpu/reduce-strided-axis.wgsl.jinja CHANGED
@@ -1,6 +1,3 @@
1
- {% if usesF16 %}
2
- enable f16;
3
- {% endif %}
4
  {{ env.wgsl.resourceDeclarations }}
5
 
6
  @compute @workgroup_size({{ workgroupSize }})
@@ -15,7 +12,13 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
15
  var acc = 0.0;
16
  for (var r = 0u; r < {{ axisDim }}u; r++) {
17
  let value = {% if usesF16 %}f32({% endif %}x[base + r * {{ innerSize }}u]{% if usesF16 %}){% endif %};
 
18
  acc = acc + value * value;
 
 
 
 
 
19
  }
20
- y[i] = {% if usesF16 %}f16({% endif %}acc{% if usesF16 %}){% endif %};
21
  }
 
 
 
 
1
  {{ env.wgsl.resourceDeclarations }}
2
 
3
  @compute @workgroup_size({{ workgroupSize }})
 
12
  var acc = 0.0;
13
  for (var r = 0u; r < {{ axisDim }}u; r++) {
14
  let value = {% if usesF16 %}f32({% endif %}x[base + r * {{ innerSize }}u]{% if usesF16 %}){% endif %};
15
+ {% if op == "l2" or op == "sumsquare" %}
16
  acc = acc + value * value;
17
+ {% elif op == "l1" %}
18
+ acc = acc + abs(value);
19
+ {% else %}
20
+ acc = acc + value;
21
+ {% endif %}
22
  }
23
+ y[i] = {% if usesF16 %}f16({% endif %}{% if op == "l2" %}sqrt(acc){% elif op == "mean" %}acc / {{ axisDim }}.0{% else %}acc{% endif %}{% if usesF16 %}){% endif %};
24
  }
build/webgpu/test.json CHANGED
@@ -859,7 +859,7 @@
859
  }
860
  },
861
  {
862
- "name": "axis0_narrow_f32_8192x3_splitk_guard_lock",
863
  "provenance": {
864
  "notes": "An 8,192-by-3 axis-0 reduction exercises split-K with a narrow output. Constant ones make square accumulation exactly 8,192."
865
  },
 
859
  }
860
  },
861
  {
862
+ "name": "axis0_narrow_f32_8192x3_splitk",
863
  "provenance": {
864
  "notes": "An 8,192-by-3 axis-0 reduction exercises split-K with a narrow output. Constant ones make square accumulation exactly 8,192."
865
  },