Xenova HF Staff commited on
Commit
4b8d4dd
·
verified ·
1 Parent(s): d70fea3

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,18 +18,18 @@ See the [ONNX `Sum` spec](https://onnx.ai/onnx/operators/onnx__Sum.html) for the
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `A` | `a` | `T` | — | — | First input tensor. | required |
24
- | `B` | `b` | `T` | — | — | Second input tensor (optional). | optional |
25
- | `C` | `c` | `T` | — | — | Third input tensor (optional). | optional |
26
- | `D` | `d` | `T` | — | — | Fourth input tensor (optional). | optional |
27
 
28
  ## Outputs
29
 
30
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
31
  | --- | --- | --- | --- | --- | --- | --- |
32
- | `sum` | `y` | `T` | derived | derived; see description | Elementwise sum of all provided input tensors. | required |
33
 
34
  ## Type constraints
35
 
@@ -39,7 +39,7 @@ See the [ONNX `Sum` spec](https://onnx.ai/onnx/operators/onnx__Sum.html) for the
39
 
40
  ## Files
41
 
42
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
43
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
44
  - [`test.json`](build/webgpu/test.json) — correctness cases
45
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -49,10 +49,14 @@ See the [ONNX `Sum` spec](https://onnx.ai/onnx/operators/onnx__Sum.html) for the
49
 
50
  ## Use with `@huggingface/kernels`
51
 
52
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
53
- It then allocates the result tensors automatically.
 
 
 
54
 
55
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
56
 
57
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
58
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `a` | `A` | `T` | — | — | First input tensor. | required |
24
+ | `b` | `B` | `T` | — | — | Second input tensor (optional). | optional |
25
+ | `c` | `C` | `T` | — | — | Third input tensor (optional). | optional |
26
+ | `d` | `D` | `T` | — | — | Fourth input tensor (optional). | optional |
27
 
28
  ## Outputs
29
 
30
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
31
  | --- | --- | --- | --- | --- | --- | --- |
32
+ | `y` | `sum` | `T` | derived | derived | Elementwise sum of all provided input tensors. | required |
33
 
34
  ## Type constraints
35
 
 
39
 
40
  ## Files
41
 
42
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
43
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
44
  - [`test.json`](build/webgpu/test.json) — correctness cases
45
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
49
 
50
  ## Use with `@huggingface/kernels`
51
 
52
+ ```sh
53
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
54
+ ```
55
+
56
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
57
 
58
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
59
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
60
 
61
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
62
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Sum",
3
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
4
  "cases": [
5
  {
 
1
  {
 
2
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
3
  "cases": [
4
  {
build/webgpu/datamove-elementwise-copy.wgsl.jinja CHANGED
@@ -1,6 +1,3 @@
1
- {% if usesF16 %}
2
- enable f16;
3
- {% endif %}
4
  {{ env.wgsl.resourceDeclarations }}
5
 
6
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
 
 
 
 
1
  {{ env.wgsl.resourceDeclarations }}
2
 
3
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
build/webgpu/manifest.json CHANGED
@@ -2,200 +2,71 @@
2
  "domain": "ai.onnx",
3
  "name": "Sum",
4
  "sinceVersion": 13,
5
- "description": "Computes the elementwise sum of one or more input tensors with multidirectional (NumPy-style) broadcasting. All inputs and the output must share the same data type.",
6
- "inputs": [
7
- { "role": "A", "dtype": "T", "description": "First input tensor." },
8
- { "role": "B", "dtype": "T", "optional": true, "description": "Second input tensor (optional)." },
9
- { "role": "C", "dtype": "T", "optional": true, "description": "Third input tensor (optional)." },
10
- { "role": "D", "dtype": "T", "optional": true, "description": "Fourth input tensor (optional)." }
11
- ],
12
- "outputs": [
13
- {
14
- "role": "sum",
15
  "dtype": "T",
16
- "rank": "max(ranks.A, ranks.B if present.b else 0, ranks.C if present.b and present.c else 0, ranks.D if present.b and present.c and present.d else 0)",
17
- "shape": "variadicShape",
18
- "description": "Elementwise sum of all provided input tensors."
19
  }
20
- ],
21
- "typeConstraints": { "T": ["float32", "float16"] },
22
- "args": {
23
- "a": { "kind": "tensor", "semantic": "A", "role": "input" },
24
- "b": { "kind": "tensor", "semantic": "B", "role": "input", "required": false },
25
- "c": { "kind": "tensor", "semantic": "C", "role": "input2", "required": false },
26
- "y": { "kind": "tensor", "semantic": "sum", "role": "output" },
27
- "d": { "kind": "tensor", "semantic": "D", "role": "input3", "required": false }
28
  },
29
- "tunables": { "WORKGROUP_SIZE": 256 },
 
30
  "derive": {
31
- "variadicShape": "broadcastShape(broadcastShape(broadcastShape(shapes.A, shapes.B), shapes.C), shapes.D) if present.b and present.c and present.d else (broadcastShape(broadcastShape(shapes.A, shapes.B), shapes.C) if present.b and present.c else (broadcastShape(shapes.A, shapes.B) if present.b else shapes.A))",
32
  "flatVec4OutputOk": "numel(shapes.y) > 0 and numel(shapes.y) % 4 == 0 and f16Ok(dtypes.T)",
33
  "broadcastOutputOk": "f16Ok(dtypes.T)",
34
  "broadcastVec4OutputOk": "ranks.y >= 1 and dim(shapes.y, ranks.y - 1) > 0 and dim(shapes.y, ranks.y - 1) % 4 == 0 and f16Ok(dtypes.T)",
35
- "aBroadcastVec4Ok": "ranks.A <= ranks.y and (ranks.A == 0 or dim(shapes.A, ranks.A - 1) == 1 or dim(shapes.A, ranks.A - 1) == dim(shapes.y, ranks.y - 1))",
36
- "bBroadcastVec4Ok": "not present.b or (ranks.B <= ranks.y and (ranks.B == 0 or dim(shapes.B, ranks.B - 1) == 1 or dim(shapes.B, ranks.B - 1) == dim(shapes.y, ranks.y - 1)))",
37
- "cBroadcastVec4Ok": "not present.c or (ranks.C <= ranks.y and (ranks.C == 0 or dim(shapes.C, ranks.C - 1) == 1 or dim(shapes.C, ranks.C - 1) == dim(shapes.y, ranks.y - 1)))",
38
- "dBroadcastVec4Ok": "not present.d or (ranks.D <= ranks.y and (ranks.D == 0 or dim(shapes.D, ranks.D - 1) == 1 or dim(shapes.D, ranks.D - 1) == dim(shapes.y, ranks.y - 1)))"
 
 
39
  },
40
- "constants": { "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"" },
41
- "bindingSets": {
42
- "identity": [
43
- { "name": "x", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
44
- { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
45
- {
46
- "name": "params",
47
- "semantic": "kernel.params",
48
- "buffer": { "type": "uniform" },
49
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
50
- }
51
- ],
52
- "vec4Two": [
53
- {
54
- "name": "a",
55
- "arg": "a",
56
- "semantic": "A",
57
- "buffer": { "type": "read-only-storage" },
58
- "elementType": "$vectorScalar"
59
- },
60
- {
61
- "name": "b",
62
- "arg": "b",
63
- "semantic": "B",
64
- "buffer": { "type": "read-only-storage" },
65
- "elementType": "$vectorScalar"
66
- },
67
- { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
68
- {
69
- "name": "params",
70
- "semantic": "kernel.params",
71
- "buffer": { "type": "uniform" },
72
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
73
- }
74
- ],
75
- "vec4Three": [
76
- {
77
- "name": "a",
78
- "arg": "a",
79
- "semantic": "A",
80
- "buffer": { "type": "read-only-storage" },
81
- "elementType": "$aElement"
82
- },
83
- {
84
- "name": "b",
85
- "arg": "b",
86
- "semantic": "B",
87
- "buffer": { "type": "read-only-storage" },
88
- "elementType": "$bElement"
89
- },
90
- {
91
- "name": "c",
92
- "arg": "c",
93
- "semantic": "C",
94
- "buffer": { "type": "read-only-storage" },
95
- "elementType": "$cElement"
96
- },
97
- { "name": "y", "arg": "y", "semantic": "sum", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
98
- {
99
- "name": "params",
100
- "semantic": "kernel.params",
101
- "buffer": { "type": "uniform" },
102
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
103
- }
104
- ],
105
- "vec4Four": [
106
- {
107
- "name": "a",
108
- "arg": "a",
109
- "semantic": "A",
110
- "buffer": { "type": "read-only-storage" },
111
- "elementType": "$aElement"
112
- },
113
- {
114
- "name": "b",
115
- "arg": "b",
116
- "semantic": "B",
117
- "buffer": { "type": "read-only-storage" },
118
- "elementType": "$bElement"
119
- },
120
- {
121
- "name": "c",
122
- "arg": "c",
123
- "semantic": "C",
124
- "buffer": { "type": "read-only-storage" },
125
- "elementType": "$cElement"
126
- },
127
- {
128
- "name": "d",
129
- "arg": "d",
130
- "semantic": "D",
131
- "buffer": { "type": "read-only-storage" },
132
- "elementType": "$dElement"
133
- },
134
- { "name": "y", "arg": "y", "semantic": "sum", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
135
- {
136
- "name": "params",
137
- "semantic": "kernel.params",
138
- "buffer": { "type": "uniform" },
139
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }] }
140
- }
141
- ],
142
- "scalarTwo": [
143
- { "name": "a", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
144
- { "name": "b", "arg": "b", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
145
- { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
146
- {
147
- "name": "params",
148
- "semantic": "kernel.params",
149
- "buffer": { "type": "uniform" },
150
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
151
- }
152
- ],
153
- "scalarThree": [
154
- { "name": "a", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
155
- { "name": "b", "arg": "b", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
156
- { "name": "c", "arg": "c", "semantic": "C", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
157
- { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
158
- {
159
- "name": "params",
160
- "semantic": "kernel.params",
161
- "buffer": { "type": "uniform" },
162
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
163
- }
164
- ],
165
- "scalarFour": [
166
- { "name": "a", "arg": "a", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
167
- { "name": "b", "arg": "b", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
168
- { "name": "c", "arg": "c", "semantic": "C", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
169
- { "name": "d", "arg": "d", "semantic": "D", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
170
- { "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$scalar" },
171
- {
172
- "name": "params",
173
- "semantic": "kernel.params",
174
- "buffer": { "type": "uniform" },
175
- "struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] }
176
- }
177
- ]
178
  },
179
  "variants": [
180
  {
181
  "id": "single_input_identity",
182
  "priority": 30,
183
- "when": ["not present.b", "not present.c", "ranks.A == ranks.y", "numel(shapes.A) == numel(shapes.y)", "f16Ok(dtypes.T)"],
184
  "passes": [
185
  {
186
  "id": "main",
187
  "name": "Sum.Identity",
188
  "shader": "datamove-elementwise-copy.wgsl.jinja",
189
- "bindings": "identity",
190
- "dispatch": { "gridStride": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
191
  }
192
  ]
193
  },
194
  {
195
  "id": "same_shape_vec4_two_input",
196
  "priority": 20,
197
- "when": ["present.b", "not present.c", "sameShape(shapes.A, shapes.y)", "sameShape(shapes.B, shapes.y)", "flatVec4OutputOk"],
198
- "constants": {
199
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
200
  "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
201
  "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
@@ -205,17 +76,27 @@
205
  {
206
  "id": "main",
207
  "name": "Sum.vec4",
208
- "source": { "shader": "summean-vec4.wgsl.jinja", "inputs": { "op": "\"sum\"", "hasC": "false" } },
209
- "bindings": "vec4Two",
210
- "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
 
 
 
 
 
211
  }
212
  ]
213
  },
214
  {
215
  "id": "same_shape_vec4_three_input",
216
  "priority": 25,
217
- "when": ["present.b", "present.c", "sameShape(shapes.A, shapes.y)", "sameShape(shapes.B, shapes.y)", "sameShape(shapes.C, shapes.y)", "flatVec4OutputOk", "not present.d"],
218
- "constants": {
219
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
220
  "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
221
  "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
@@ -225,17 +106,22 @@
225
  {
226
  "id": "main",
227
  "name": "Sum.vec4_3",
228
- "source": { "shader": "summean-vec4.wgsl.jinja", "inputs": { "op": "\"sum\"", "hasC": "true" } },
229
- "bindings": "vec4Three",
230
- "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
 
231
  }
232
  ]
233
  },
234
  {
235
  "id": "same_shape_vec4_four_input",
236
  "priority": 27,
237
- "when": ["present.b", "present.c", "present.d", "sameShape(shapes.A, shapes.y)", "sameShape(shapes.B, shapes.y)", "sameShape(shapes.C, shapes.y)", "sameShape(shapes.D, shapes.y)", "flatVec4OutputOk"],
238
- "constants": {
239
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
240
  "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
241
  "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
@@ -246,37 +132,41 @@
246
  {
247
  "id": "main",
248
  "name": "Sum.vec4_3",
249
- "source": {
250
- "shader": "summean-vec4.wgsl.jinja",
251
- "inputs": { "op": "\"sum\"", "hasC": "true", "hasD": "true" }
252
- },
253
- "bindings": "vec4Four",
254
- "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
255
  }
256
  ]
257
  },
258
  {
259
  "id": "broadcast_two_input",
260
- "when": ["present.b", "not present.c", "ranks.A <= ranks.y", "ranks.B <= ranks.y", "broadcastOutputOk"],
261
  "passes": [
262
  {
263
  "id": "main",
264
  "name": "Sum",
265
- "source": {
266
- "shader": "summean-broadcast.wgsl.jinja",
267
- "inputs": {
268
- "aShape": "shapes.A",
269
- "bShape": "shapes.B",
270
- "yShape": "shapes.y",
271
- "aRank": "ranks.A",
272
- "bRank": "ranks.B",
273
- "yRank": "ranks.y",
274
- "hasC": "false",
275
- "op": "\"sum\""
276
- }
277
  },
278
- "bindings": "scalarTwo",
279
- "dispatch": { "threads": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
280
  }
281
  ]
282
  },
@@ -284,96 +174,102 @@
284
  "id": "broadcast_three_input_vec4",
285
  "priority": 24,
286
  "when": ["present.b", "present.c", "not present.d", "broadcastVec4OutputOk", "aBroadcastVec4Ok", "bBroadcastVec4Ok", "cBroadcastVec4Ok"],
287
- "constants": {
288
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
289
- "aElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1 else dtypes.T",
290
- "bElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1 else dtypes.T",
291
- "cElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1 else dtypes.T"
292
  },
293
  "passes": [
294
  {
295
  "id": "main",
296
  "name": "Sum.Broadcast3Vec4",
297
- "source": {
298
- "shader": "summean-broadcast.wgsl.jinja",
299
- "inputs": {
300
- "aShape": "shapes.A",
301
- "bShape": "shapes.B",
302
- "cShape": "shapes.C",
303
- "yShape": "shapes.y",
304
- "aRank": "ranks.A",
305
- "bRank": "ranks.B",
306
- "cRank": "ranks.C",
307
- "yRank": "ranks.y",
308
- "aVector": "ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1",
309
- "bVector": "ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1",
310
- "cVector": "ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1",
311
- "hasC": true,
312
- "vectorized": true,
313
- "op": "\"sum\""
314
- }
315
  },
316
- "bindings": "vec4Three",
317
- "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
318
  }
319
  ]
320
  },
321
  {
322
  "id": "broadcast_three_input",
323
  "priority": 20,
324
- "when": ["present.b", "present.c", "ranks.A <= ranks.y", "ranks.B <= ranks.y", "ranks.C <= ranks.y", "broadcastOutputOk", "not present.d"],
325
  "passes": [
326
  {
327
  "id": "main",
328
  "name": "Sum",
329
- "source": {
330
- "shader": "summean-broadcast.wgsl.jinja",
331
- "inputs": {
332
- "aShape": "shapes.A",
333
- "bShape": "shapes.B",
334
- "cShape": "shapes.C",
335
- "yShape": "shapes.y",
336
- "aRank": "ranks.A",
337
- "bRank": "ranks.B",
338
- "cRank": "ranks.C",
339
- "yRank": "ranks.y",
340
- "hasC": "true",
341
- "op": "\"sum\""
342
- }
343
  },
344
- "bindings": "scalarThree",
345
- "dispatch": { "threads": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
346
  }
347
  ]
348
  },
349
  {
350
  "id": "broadcast_four_input",
351
  "priority": 22,
352
- "when": ["present.b", "present.c", "present.d", "ranks.A <= ranks.y", "ranks.B <= ranks.y", "ranks.C <= ranks.y", "ranks.D <= ranks.y", "broadcastOutputOk"],
353
  "passes": [
354
  {
355
  "id": "main",
356
  "name": "Sum",
357
- "source": {
358
- "shader": "summean-broadcast.wgsl.jinja",
359
- "inputs": {
360
- "aShape": "shapes.A",
361
- "bShape": "shapes.B",
362
- "cShape": "shapes.C",
363
- "yShape": "shapes.y",
364
- "aRank": "ranks.A",
365
- "bRank": "ranks.B",
366
- "cRank": "ranks.C",
367
- "yRank": "ranks.y",
368
- "hasC": "true",
369
- "op": "\"sum\"",
370
- "dShape": "shapes.D",
371
- "dRank": "ranks.D",
372
- "hasD": "true"
373
- }
374
  },
375
- "bindings": "scalarFour",
376
- "dispatch": { "threads": "numel(shapes.y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
377
  }
378
  ]
379
  },
@@ -381,42 +277,44 @@
381
  "id": "broadcast_four_input_vec4",
382
  "priority": 26,
383
  "when": ["present.b", "present.c", "present.d", "broadcastVec4OutputOk", "aBroadcastVec4Ok", "bBroadcastVec4Ok", "cBroadcastVec4Ok", "dBroadcastVec4Ok"],
384
- "constants": {
385
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
386
- "aElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1 else dtypes.T",
387
- "bElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1 else dtypes.T",
388
- "cElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1 else dtypes.T",
389
- "dElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.D > 0 and dim(shapes.D, ranks.D - 1) != 1 else dtypes.T"
390
  },
391
  "passes": [
392
  {
393
  "id": "main",
394
  "name": "Sum.Broadcast4Vec4",
395
- "source": {
396
- "shader": "summean-broadcast.wgsl.jinja",
397
- "inputs": {
398
- "aShape": "shapes.A",
399
- "bShape": "shapes.B",
400
- "cShape": "shapes.C",
401
- "dShape": "shapes.D",
402
- "yShape": "shapes.y",
403
- "aRank": "ranks.A",
404
- "bRank": "ranks.B",
405
- "cRank": "ranks.C",
406
- "dRank": "ranks.D",
407
- "yRank": "ranks.y",
408
- "aVector": "ranks.A > 0 and dim(shapes.A, ranks.A - 1) != 1",
409
- "bVector": "ranks.B > 0 and dim(shapes.B, ranks.B - 1) != 1",
410
- "cVector": "ranks.C > 0 and dim(shapes.C, ranks.C - 1) != 1",
411
- "dVector": "ranks.D > 0 and dim(shapes.D, ranks.D - 1) != 1",
412
- "hasC": true,
413
- "hasD": true,
414
- "vectorized": true,
415
- "op": "\"sum\""
416
- }
417
  },
418
- "bindings": "vec4Four",
419
- "dispatch": { "threads": "numel(shapes.y) / 4", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
420
  }
421
  ]
422
  }
 
2
  "domain": "ai.onnx",
3
  "name": "Sum",
4
  "sinceVersion": 13,
5
+ "inputs": {
6
+ "a": { "onnx": "A", "dtype": "T" },
7
+ "b": { "onnx": "B", "dtype": "T", "optional": true },
8
+ "c": { "onnx": "C", "dtype": "T", "optional": true },
9
+ "d": { "onnx": "D", "dtype": "T", "optional": true }
10
+ },
11
+ "outputs": {
12
+ "y": {
13
+ "onnx": "sum",
 
14
  "dtype": "T",
15
+ "rank": "max(ranks.a, ranks.b if present.b else 0, ranks.c if present.b and present.c else 0, ranks.d if present.b and present.c and present.d else 0)",
16
+ "shape": "variadicShape"
 
17
  }
 
 
 
 
 
 
 
 
18
  },
19
+ "typeConstraints": { "T": ["float32", "float16"] },
20
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 } },
21
  "derive": {
22
+ "variadicShape": "broadcastShape(broadcastShape(broadcastShape(shapes.a, shapes.b), shapes.c), shapes.d) if present.b and present.c and present.d else (broadcastShape(broadcastShape(shapes.a, shapes.b), shapes.c) if present.b and present.c else (broadcastShape(shapes.a, shapes.b) if present.b else shapes.a))",
23
  "flatVec4OutputOk": "numel(shapes.y) > 0 and numel(shapes.y) % 4 == 0 and f16Ok(dtypes.T)",
24
  "broadcastOutputOk": "f16Ok(dtypes.T)",
25
  "broadcastVec4OutputOk": "ranks.y >= 1 and dim(shapes.y, ranks.y - 1) > 0 and dim(shapes.y, ranks.y - 1) % 4 == 0 and f16Ok(dtypes.T)",
26
+ "aBroadcastVec4Ok": "ranks.a <= ranks.y and (ranks.a == 0 or dim(shapes.a, ranks.a - 1) == 1 or dim(shapes.a, ranks.a - 1) == dim(shapes.y, ranks.y - 1))",
27
+ "bBroadcastVec4Ok": "not present.b or (ranks.b <= ranks.y and (ranks.b == 0 or dim(shapes.b, ranks.b - 1) == 1 or dim(shapes.b, ranks.b - 1) == dim(shapes.y, ranks.y - 1)))",
28
+ "cBroadcastVec4Ok": "not present.c or (ranks.c <= ranks.y and (ranks.c == 0 or dim(shapes.c, ranks.c - 1) == 1 or dim(shapes.c, ranks.c - 1) == dim(shapes.y, ranks.y - 1)))",
29
+ "dBroadcastVec4Ok": "not present.d or (ranks.d <= ranks.y and (ranks.d == 0 or dim(shapes.d, ranks.d - 1) == 1 or dim(shapes.d, ranks.d - 1) == dim(shapes.y, ranks.y - 1)))",
30
+ "scalar": "dtypes.T",
31
+ "usesF16": "dtypes.T == \"f16\""
32
  },
33
+ "bindings": {
34
+ "params": { "buffer": "uniform", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] },
35
+ "y_2": { "name": "y", "buffer": "storage", "elementType": "$vectorScalar" },
36
+ "params_2": {
37
+ "name": "params",
38
+ "buffer": "uniform",
39
+ "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y) / 4" }]
40
+ },
41
+ "a_2": { "name": "a", "buffer": "read-only-storage", "elementType": "$aElement" },
42
+ "b_2": { "name": "b", "buffer": "read-only-storage", "elementType": "$bElement" },
43
+ "c": { "buffer": "read-only-storage", "elementType": "$cElement" },
44
+ "d": { "buffer": "read-only-storage", "elementType": "$dElement" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  },
46
  "variants": [
47
  {
48
  "id": "single_input_identity",
49
  "priority": 30,
50
+ "when": ["not present.b", "not present.c", "ranks.a == ranks.y", "numel(shapes.a) == numel(shapes.y)", "f16Ok(dtypes.T)"],
51
  "passes": [
52
  {
53
  "id": "main",
54
  "name": "Sum.Identity",
55
  "shader": "datamove-elementwise-copy.wgsl.jinja",
56
+ "bindings": [{ "arg": "a", "name": "x", "elementType": "$scalar" }, "y", "params"],
57
+ "dispatch": {
58
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
59
+ "y": 1,
60
+ "z": 1
61
+ }
62
  }
63
  ]
64
  },
65
  {
66
  "id": "same_shape_vec4_two_input",
67
  "priority": 20,
68
+ "when": ["present.b", "not present.c", "sameShape(shapes.a, shapes.y)", "sameShape(shapes.b, shapes.y)", "flatVec4OutputOk"],
69
+ "derive": {
70
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
71
  "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
72
  "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
 
76
  {
77
  "id": "main",
78
  "name": "Sum.vec4",
79
+ "shader": "summean-vec4.wgsl.jinja",
80
+ "derive": { "op": "\"sum\"", "hasC": "false" },
81
+ "bindings": [
82
+ { "arg": "a", "elementType": "$vectorScalar" },
83
+ { "arg": "b", "elementType": "$vectorScalar" },
84
+ "y_2",
85
+ "params_2"
86
+ ],
87
+ "dispatch": {
88
+ "x": "min(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
89
+ "y": "ceilDiv(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
90
+ "z": 1
91
+ }
92
  }
93
  ]
94
  },
95
  {
96
  "id": "same_shape_vec4_three_input",
97
  "priority": 25,
98
+ "when": ["present.b", "present.c", "sameShape(shapes.a, shapes.y)", "sameShape(shapes.b, shapes.y)", "sameShape(shapes.c, shapes.y)", "flatVec4OutputOk", "not present.d"],
99
+ "derive": {
100
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
101
  "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
102
  "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
 
106
  {
107
  "id": "main",
108
  "name": "Sum.vec4_3",
109
+ "shader": "summean-vec4.wgsl.jinja",
110
+ "derive": { "op": "\"sum\"", "hasC": "true" },
111
+ "bindings": ["a_2", "b_2", "c", "y_2", "params_2"],
112
+ "dispatch": {
113
+ "x": "min(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
114
+ "y": "ceilDiv(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
115
+ "z": 1
116
+ }
117
  }
118
  ]
119
  },
120
  {
121
  "id": "same_shape_vec4_four_input",
122
  "priority": 27,
123
+ "when": ["present.b", "present.c", "present.d", "sameShape(shapes.a, shapes.y)", "sameShape(shapes.b, shapes.y)", "sameShape(shapes.c, shapes.y)", "sameShape(shapes.d, shapes.y)", "flatVec4OutputOk"],
124
+ "derive": {
125
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
126
  "aElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
127
  "bElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
 
132
  {
133
  "id": "main",
134
  "name": "Sum.vec4_3",
135
+ "shader": "summean-vec4.wgsl.jinja",
136
+ "derive": { "op": "\"sum\"", "hasC": "true", "hasD": "true" },
137
+ "bindings": ["a_2", "b_2", "c", "d", "y_2", "params_2"],
138
+ "dispatch": {
139
+ "x": "min(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
140
+ "y": "ceilDiv(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
141
+ "z": 1
142
+ }
143
  }
144
  ]
145
  },
146
  {
147
  "id": "broadcast_two_input",
148
+ "when": ["present.b", "not present.c", "ranks.a <= ranks.y", "ranks.b <= ranks.y", "broadcastOutputOk"],
149
  "passes": [
150
  {
151
  "id": "main",
152
  "name": "Sum",
153
+ "shader": "summean-broadcast.wgsl.jinja",
154
+ "derive": {
155
+ "aShape": "shapes.a",
156
+ "bShape": "shapes.b",
157
+ "yShape": "shapes.y",
158
+ "aRank": "ranks.a",
159
+ "bRank": "ranks.b",
160
+ "yRank": "ranks.y",
161
+ "hasC": "false",
162
+ "op": "\"sum\""
 
 
163
  },
164
+ "bindings": ["a", "b", "y", "params"],
165
+ "dispatch": {
166
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
167
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
168
+ "z": 1
169
+ }
170
  }
171
  ]
172
  },
 
174
  "id": "broadcast_three_input_vec4",
175
  "priority": 24,
176
  "when": ["present.b", "present.c", "not present.d", "broadcastVec4OutputOk", "aBroadcastVec4Ok", "bBroadcastVec4Ok", "cBroadcastVec4Ok"],
177
+ "derive": {
178
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
179
+ "aElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.a > 0 and dim(shapes.a, ranks.a - 1) != 1 else dtypes.T",
180
+ "bElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.b > 0 and dim(shapes.b, ranks.b - 1) != 1 else dtypes.T",
181
+ "cElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.c > 0 and dim(shapes.c, ranks.c - 1) != 1 else dtypes.T"
182
  },
183
  "passes": [
184
  {
185
  "id": "main",
186
  "name": "Sum.Broadcast3Vec4",
187
+ "shader": "summean-broadcast.wgsl.jinja",
188
+ "derive": {
189
+ "aShape": "shapes.a",
190
+ "bShape": "shapes.b",
191
+ "cShape": "shapes.c",
192
+ "yShape": "shapes.y",
193
+ "aRank": "ranks.a",
194
+ "bRank": "ranks.b",
195
+ "cRank": "ranks.c",
196
+ "yRank": "ranks.y",
197
+ "aVector": "ranks.a > 0 and dim(shapes.a, ranks.a - 1) != 1",
198
+ "bVector": "ranks.b > 0 and dim(shapes.b, ranks.b - 1) != 1",
199
+ "cVector": "ranks.c > 0 and dim(shapes.c, ranks.c - 1) != 1",
200
+ "hasC": true,
201
+ "vectorizedSpec": true,
202
+ "op": "\"sum\""
 
 
203
  },
204
+ "bindings": ["a_2", "b_2", "c", "y_2", "params_2"],
205
+ "dispatch": {
206
+ "x": "min(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
207
+ "y": "ceilDiv(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
208
+ "z": 1
209
+ }
210
  }
211
  ]
212
  },
213
  {
214
  "id": "broadcast_three_input",
215
  "priority": 20,
216
+ "when": ["present.b", "present.c", "ranks.a <= ranks.y", "ranks.b <= ranks.y", "ranks.c <= ranks.y", "broadcastOutputOk", "not present.d"],
217
  "passes": [
218
  {
219
  "id": "main",
220
  "name": "Sum",
221
+ "shader": "summean-broadcast.wgsl.jinja",
222
+ "derive": {
223
+ "aShape": "shapes.a",
224
+ "bShape": "shapes.b",
225
+ "cShape": "shapes.c",
226
+ "yShape": "shapes.y",
227
+ "aRank": "ranks.a",
228
+ "bRank": "ranks.b",
229
+ "cRank": "ranks.c",
230
+ "yRank": "ranks.y",
231
+ "hasC": "true",
232
+ "op": "\"sum\""
 
 
233
  },
234
+ "bindings": ["a", "b", { "arg": "c" }, "y", "params"],
235
+ "dispatch": {
236
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
237
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
238
+ "z": 1
239
+ }
240
  }
241
  ]
242
  },
243
  {
244
  "id": "broadcast_four_input",
245
  "priority": 22,
246
+ "when": ["present.b", "present.c", "present.d", "ranks.a <= ranks.y", "ranks.b <= ranks.y", "ranks.c <= ranks.y", "ranks.d <= ranks.y", "broadcastOutputOk"],
247
  "passes": [
248
  {
249
  "id": "main",
250
  "name": "Sum",
251
+ "shader": "summean-broadcast.wgsl.jinja",
252
+ "derive": {
253
+ "aShape": "shapes.a",
254
+ "bShape": "shapes.b",
255
+ "cShape": "shapes.c",
256
+ "yShape": "shapes.y",
257
+ "aRank": "ranks.a",
258
+ "bRank": "ranks.b",
259
+ "cRank": "ranks.c",
260
+ "yRank": "ranks.y",
261
+ "hasC": "true",
262
+ "op": "\"sum\"",
263
+ "dShape": "shapes.d",
264
+ "dRank": "ranks.d",
265
+ "hasD": "true"
 
 
266
  },
267
+ "bindings": ["a", "b", { "arg": "c" }, { "arg": "d" }, "y", "params"],
268
+ "dispatch": {
269
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
270
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
271
+ "z": 1
272
+ }
273
  }
274
  ]
275
  },
 
277
  "id": "broadcast_four_input_vec4",
278
  "priority": 26,
279
  "when": ["present.b", "present.c", "present.d", "broadcastVec4OutputOk", "aBroadcastVec4Ok", "bBroadcastVec4Ok", "cBroadcastVec4Ok", "dBroadcastVec4Ok"],
280
+ "derive": {
281
  "vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
282
+ "aElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.a > 0 and dim(shapes.a, ranks.a - 1) != 1 else dtypes.T",
283
+ "bElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.b > 0 and dim(shapes.b, ranks.b - 1) != 1 else dtypes.T",
284
+ "cElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.c > 0 and dim(shapes.c, ranks.c - 1) != 1 else dtypes.T",
285
+ "dElement": "(\"vec4<\" ~ dtypes.T ~ \">\") if ranks.d > 0 and dim(shapes.d, ranks.d - 1) != 1 else dtypes.T"
286
  },
287
  "passes": [
288
  {
289
  "id": "main",
290
  "name": "Sum.Broadcast4Vec4",
291
+ "shader": "summean-broadcast.wgsl.jinja",
292
+ "derive": {
293
+ "aShape": "shapes.a",
294
+ "bShape": "shapes.b",
295
+ "cShape": "shapes.c",
296
+ "dShape": "shapes.d",
297
+ "yShape": "shapes.y",
298
+ "aRank": "ranks.a",
299
+ "bRank": "ranks.b",
300
+ "cRank": "ranks.c",
301
+ "dRank": "ranks.d",
302
+ "yRank": "ranks.y",
303
+ "aVector": "ranks.a > 0 and dim(shapes.a, ranks.a - 1) != 1",
304
+ "bVector": "ranks.b > 0 and dim(shapes.b, ranks.b - 1) != 1",
305
+ "cVector": "ranks.c > 0 and dim(shapes.c, ranks.c - 1) != 1",
306
+ "dVector": "ranks.d > 0 and dim(shapes.d, ranks.d - 1) != 1",
307
+ "hasC": true,
308
+ "hasD": true,
309
+ "vectorizedSpec": true,
310
+ "op": "\"sum\""
 
 
311
  },
312
+ "bindings": ["a_2", "b_2", "c", "d", "y_2", "params_2"],
313
+ "dispatch": {
314
+ "x": "min(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
315
+ "y": "ceilDiv(ceilDiv((numel(shapes.y) / 4), (tunables.WORKGROUP_SIZE)), 65535)",
316
+ "z": 1
317
+ }
318
  }
319
  ]
320
  }
build/webgpu/metadata.json CHANGED
@@ -1,20 +1,33 @@
1
  {
2
  "name": "ai.onnx.Sum",
3
- "id": "_ai_onnx_sum_webgpu_2c2501b",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "BZH67s4WnBU60TILXvJnrpu86w673p/81oS0ITOTOjw=",
11
- "datamove-elementwise-copy.wgsl.jinja": "J5yC2bAddPiP+odXLgVGS3TJ9jeNsfRTvedKrj/fhZg=",
12
- "manifest.json": "n3EnmVEajNhmmHf1ueSFLaPzePrvHY41jyuk9AfSxWM=",
13
- "summean-broadcast.wgsl.jinja": "6BaP0d/1jnidmzD5A+BolzRw9fhm4PaQa4nAjjGCVmI=",
14
- "summean-vec4.wgsl.jinja": "kS8/lN2swjqu28ALxVcF4rkhpEfMfqUGRxyMf770veU=",
15
- "test.json": "DUAhzo/YUbzJAmNXu5b92KKxvQCxcDDuuNeen2eTyfI="
16
  }
17
  },
18
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
19
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Sum" }
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
 
1
  {
2
  "name": "ai.onnx.Sum",
3
+ "id": "_ai_onnx_sum_webgpu_897002a",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "6IfhKTJQtyCXiNDSgCS1fq/K6MnfDkTN4OOlHyk+tAA=",
11
+ "datamove-elementwise-copy.wgsl.jinja": "Q1WuZCqcDf9b6cbT4rLilpeWj6327t/bSFmKr6IVBoQ=",
12
+ "manifest.json": "U1BSd6o0oGGE29P1Qs81OguhhV+/J3ahevzT4CTe1+Q=",
13
+ "summean-broadcast.wgsl.jinja": "PoxdcI7l73NJlE9atrddvWDgeaeBi6F92AFEcqSA+sA=",
14
+ "summean-vec4.wgsl.jinja": "3v0ep58YgrM8lkb5b3jqOiDR/FxnKdH8ORIFrWEcLqM=",
15
+ "test.json": "rjLE2xy25wmywX1N+nSDlWvfBH5AfD1J2/R5WWSx83M="
16
  }
17
  },
18
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
19
+ "webgpu": {
20
+ "manifestSpec": "2.0",
21
+ "variants": {
22
+ "single_input_identity": ["datamove-elementwise-copy.wgsl.jinja"],
23
+ "same_shape_vec4_two_input": ["summean-vec4.wgsl.jinja"],
24
+ "same_shape_vec4_three_input": ["summean-vec4.wgsl.jinja"],
25
+ "same_shape_vec4_four_input": ["summean-vec4.wgsl.jinja"],
26
+ "broadcast_two_input": ["summean-broadcast.wgsl.jinja"],
27
+ "broadcast_three_input_vec4": ["summean-broadcast.wgsl.jinja"],
28
+ "broadcast_three_input": ["summean-broadcast.wgsl.jinja"],
29
+ "broadcast_four_input": ["summean-broadcast.wgsl.jinja"],
30
+ "broadcast_four_input_vec4": ["summean-broadcast.wgsl.jinja"]
31
+ }
32
+ }
33
  }
build/webgpu/summean-broadcast.wgsl.jinja CHANGED
@@ -1,8 +1,5 @@
1
- {% set allInputs = ["a", "b"] + (source.extraInputs if source.extraInputs is defined else (["c"] if source.hasC else []) + (["d"] if source.hasD else [])) %}
2
- {% set extraInputs = source.extraInputs if source.extraInputs is defined else (["c"] if source.hasC else []) + (["d"] if source.hasD else []) %}
3
- {% if usesF16 %}
4
- enable f16;
5
- {% endif %}
6
  {{ env.wgsl.resourceDeclarations }}
7
  {% macro offset_fn(fn_name, opShape, opRank, op_same, op_numel, outShape, outRank, out_numel) %}
8
  fn {{ fn_name }}({% if out_numel != 0 and op_numel != 1 %}out_index: u32{% endif %}) -> u32 {
@@ -68,40 +65,40 @@ fn {{ fn_name }}({% if out_numel != 0 and op_numel != 1 %}out_index: u32{% endif
68
 
69
 
70
 
71
- {{ broadcast_offset_fn("a_offset", source.aShape, source.aRank, source.yShape, source.yRank) }}
72
 
73
- {{ broadcast_offset_fn("b_offset", source.bShape, source.bRank, source.yShape, source.yRank) }}
74
 
75
  {% for n in extraInputs %}
76
- {{ broadcast_offset_fn(n ~ "_offset", source[n ~ "Shape"], source[n ~ "Rank"], source.yShape, source.yRank) }}
77
 
78
  {% endfor %}
79
 
80
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
81
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
82
  // 2D-folded flat index: gid.y carries the high bits past the
83
- // maxComputeWorkgroupsPerDimension limit.
84
- let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
85
  if (i >= params.count) {
86
  return;
87
  }
88
- {% if source.vectorized %}
89
  // The output's innermost dimension is four-aligned. Each input either keeps
90
  // that dimension (one aligned vec4 load) or broadcasts it (one scalar splat).
91
  // Offset folding is therefore paid once per four outputs without changing
92
  // multidirectional broadcast semantics on any outer dimension.
93
  let base = i * 4u;
94
  {% for n in allInputs %}
95
- {% if source[n ~ "Vector"] %}
96
- let {{ n }}v = vec4<f32>({{ n }}[{{ broadcast_offset_call(n ~ "_offset", source[n ~ "Shape"], source.yShape, "base") }} / 4u]);
97
  {% else %}
98
- let {{ n }}v = vec4<f32>(f32({{ n }}[{{ broadcast_offset_call(n ~ "_offset", source[n ~ "Shape"], source.yShape, "base") }}]));
99
  {% endif %}
100
  {% endfor %}
101
  let total = {% for n in allInputs %}{{ n }}v{% if not loop.last %} + {% endif %}{% endfor %};
102
  y[i] = {{ vectorScalar }}(total);
103
  {% else %}
104
- let total = {% for n in allInputs %}f32({{ n }}[{{ broadcast_offset_call(n ~ "_offset", source[n ~ "Shape"], source.yShape, "i") }}]){% if not loop.last %} + {% endif %}{% endfor %};
105
  y[i] = {{ scalar }}(total);
106
  {% endif %}
107
  }
 
1
+ {% set allInputs = ["a", "b"] + (extraInputs if extraInputs is defined else (["c"] if hasC else []) + (["d"] if hasD else [])) %}
2
+ {% set extraInputs = extraInputs if extraInputs is defined else (["c"] if hasC else []) + (["d"] if hasD else []) %}
 
 
 
3
  {{ env.wgsl.resourceDeclarations }}
4
  {% macro offset_fn(fn_name, opShape, opRank, op_same, op_numel, outShape, outRank, out_numel) %}
5
  fn {{ fn_name }}({% if out_numel != 0 and op_numel != 1 %}out_index: u32{% endif %}) -> u32 {
 
65
 
66
 
67
 
68
+ {{ broadcast_offset_fn("a_offset", aShape, aRank, yShape, yRank) }}
69
 
70
+ {{ broadcast_offset_fn("b_offset", bShape, bRank, yShape, yRank) }}
71
 
72
  {% for n in extraInputs %}
73
+ {{ broadcast_offset_fn(n ~ "_offset", derive[n ~ "Shape"], derive[n ~ "Rank"], yShape, yRank) }}
74
 
75
  {% endfor %}
76
 
77
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
78
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
79
  // 2D-folded flat index: gid.y carries the high bits past the
80
+ // per-axis dispatch fold width.
81
+ let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
82
  if (i >= params.count) {
83
  return;
84
  }
85
+ {% if vectorizedSpec %}
86
  // The output's innermost dimension is four-aligned. Each input either keeps
87
  // that dimension (one aligned vec4 load) or broadcasts it (one scalar splat).
88
  // Offset folding is therefore paid once per four outputs without changing
89
  // multidirectional broadcast semantics on any outer dimension.
90
  let base = i * 4u;
91
  {% for n in allInputs %}
92
+ {% if derive[n ~ "Vector"] %}
93
+ let {{ n }}v = vec4<f32>({{ n }}[{{ broadcast_offset_call(n ~ "_offset", derive[n ~ "Shape"], yShape, "base") }} / 4u]);
94
  {% else %}
95
+ let {{ n }}v = vec4<f32>(f32({{ n }}[{{ broadcast_offset_call(n ~ "_offset", derive[n ~ "Shape"], yShape, "base") }}]));
96
  {% endif %}
97
  {% endfor %}
98
  let total = {% for n in allInputs %}{{ n }}v{% if not loop.last %} + {% endif %}{% endfor %};
99
  y[i] = {{ vectorScalar }}(total);
100
  {% else %}
101
+ let total = {% for n in allInputs %}f32({{ n }}[{{ broadcast_offset_call(n ~ "_offset", derive[n ~ "Shape"], yShape, "i") }}]){% if not loop.last %} + {% endif %}{% endfor %};
102
  y[i] = {{ scalar }}(total);
103
  {% endif %}
104
  }
build/webgpu/summean-vec4.wgsl.jinja CHANGED
@@ -1,14 +1,11 @@
1
- {% set extraInputs = source.extraInputs if source.extraInputs is defined else (["c"] if source.hasC else []) + (["d"] if source.hasD else []) %}
2
- {% if usesF16 %}
3
- enable f16;
4
- {% endif %}
5
  {{ env.wgsl.resourceDeclarations }}
6
 
7
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
8
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
9
  // 2D-folded flat index: gid.y carries the high bits past the
10
- // maxComputeWorkgroupsPerDimension limit.
11
- let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
12
  if (i >= params.count) {
13
  return;
14
  }
 
1
+ {% set extraInputs = extraInputs if extraInputs is defined else (["c"] if hasC else []) + (["d"] if hasD else []) %}
 
 
 
2
  {{ env.wgsl.resourceDeclarations }}
3
 
4
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
5
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
6
  // 2D-folded flat index: gid.y carries the high bits past the
7
+ // per-axis dispatch fold width.
8
+ let i = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
9
  if (i >= params.count) {
10
  return;
11
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Sum",
3
  "cases": [
4
  {
5
  "name": "max_arity_float16_positions",
@@ -71,7 +70,7 @@
71
  "provenance": {
72
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
73
  "test": "MathOpTest.Sum_8_Test1",
74
- "notes": "Vec4 companion: variadic Sum must preserve finite subnormal totals in the vectorized path."
75
  },
76
  "inputs": {
77
  "a": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [1e-40, -1e-40, 0.0, 1e-39] } },
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "max_arity_float16_positions",
 
70
  "provenance": {
71
  "source": "onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc",
72
  "test": "MathOpTest.Sum_8_Test1",
73
+ "notes": "Vectorized variadic Sum must preserve finite subnormal totals."
74
  },
75
  "inputs": {
76
  "a": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [1e-40, -1e-40, 0.0, 1e-39] } },