sync 6fdf6301e2bb
Browse files- README.md +11 -4
- build/webgpu/bench.json +2 -2
- build/webgpu/manifest.json +108 -232
- build/webgpu/metadata.json +16 -16
- build/webgpu/reduce-axis-split-reduce.wgsl.jinja +3 -15
- build/webgpu/reduce-axis0-splitk-combine.wgsl.jinja +4 -31
- build/webgpu/reduce-axis0-splitk-reduce.wgsl.jinja +1 -14
- build/webgpu/reduce-axis0-tilecols.wgsl.jinja +8 -20
- build/webgpu/reduce-flat-partial.wgsl.jinja +20 -39
- build/webgpu/reduce-noop-empty-axes.wgsl.jinja +9 -6
- build/webgpu/reduce-row-subgroup-rows.wgsl.jinja +31 -37
- build/webgpu/reduce-row-subgroup.wgsl.jinja +51 -58
- build/webgpu/reduce-row-tree.wgsl.jinja +6 -17
- build/webgpu/reduce-serial-axis.wgsl.jinja +14 -59
- build/webgpu/test.json +4 -4
README.md
CHANGED
|
@@ -44,6 +44,13 @@ Default values (overridable per request):
|
|
| 44 |
| --- | --- |
|
| 45 |
| `T` | `float32`, `float16`, `int32`, `uint32`, `int8`, `uint8`, `bool` |
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
## Device requirements
|
| 48 |
|
| 49 |
Some implementation variants require `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
|
|
@@ -53,7 +60,7 @@ Some implementation variants require `subgroups`. These are route-specific capab
|
|
| 53 |
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
|
| 54 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 55 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 56 |
-
- [`bench.json`](build/webgpu/bench.json) — benchmark
|
| 57 |
- [`datamove-flat-copy-x4-tail.wgsl.jinja`](build/webgpu/datamove-flat-copy-x4-tail.wgsl.jinja)
|
| 58 |
- [`reduce-axis-split-reduce.wgsl.jinja`](build/webgpu/reduce-axis-split-reduce.wgsl.jinja)
|
| 59 |
- [`reduce-axis0-splitk-combine.wgsl.jinja`](build/webgpu/reduce-axis0-splitk-combine.wgsl.jinja)
|
|
@@ -70,7 +77,7 @@ Some implementation variants require `subgroups`. These are route-specific capab
|
|
| 70 |
## Use with `@huggingface/kernels`
|
| 71 |
|
| 72 |
```sh
|
| 73 |
-
npm install --save-exact @huggingface/kernels@0.0.1-preview.
|
| 74 |
```
|
| 75 |
|
| 76 |
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.
|
|
@@ -89,7 +96,7 @@ import { getKernel } from "@huggingface/kernels";
|
|
| 89 |
|
| 90 |
const kernel = await getKernel("webgpu-kernels/ai.onnx.ReduceMin", { version: 1 });
|
| 91 |
// Explicit destinations request optional results or supply metadata that cannot be inferred.
|
| 92 |
-
const { y } = await kernel({ x: { data: xData, shape: [] } }, {
|
| 93 |
-
outputs: { y: { shape: [], dtype: "float32" } },
|
| 94 |
});
|
| 95 |
```
|
|
|
|
| 44 |
| --- | --- |
|
| 45 |
| `T` | `float32`, `float16`, `int32`, `uint32`, `int8`, `uint8`, `bool` |
|
| 46 |
|
| 47 |
+
## Implementation variants
|
| 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 |
+
- `subgroup_last_axis_vec4` — Reduces each contiguous last-axis row with subgroup collectives and vec4-packed reads.
|
| 52 |
+
- `subgroup_last_axis` — Reduces each contiguous last-axis row with subgroup collectives and scalar reads for an unaligned row width.
|
| 53 |
+
|
| 54 |
## Device requirements
|
| 55 |
|
| 56 |
Some implementation variants require `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
|
|
|
|
| 60 |
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
|
| 61 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 62 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 63 |
+
- [`bench.json`](build/webgpu/bench.json) — benchmark cases
|
| 64 |
- [`datamove-flat-copy-x4-tail.wgsl.jinja`](build/webgpu/datamove-flat-copy-x4-tail.wgsl.jinja)
|
| 65 |
- [`reduce-axis-split-reduce.wgsl.jinja`](build/webgpu/reduce-axis-split-reduce.wgsl.jinja)
|
| 66 |
- [`reduce-axis0-splitk-combine.wgsl.jinja`](build/webgpu/reduce-axis0-splitk-combine.wgsl.jinja)
|
|
|
|
| 77 |
## Use with `@huggingface/kernels`
|
| 78 |
|
| 79 |
```sh
|
| 80 |
+
npm install --save-exact @huggingface/kernels@0.0.1-preview.3
|
| 81 |
```
|
| 82 |
|
| 83 |
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.
|
|
|
|
| 96 |
|
| 97 |
const kernel = await getKernel("webgpu-kernels/ai.onnx.ReduceMin", { version: 1 });
|
| 98 |
// Explicit destinations request optional results or supply metadata that cannot be inferred.
|
| 99 |
+
const { y } = await kernel({ x: { data: xData, shape: [3, 2, 2] } }, {
|
| 100 |
+
outputs: { y: { shape: [1, 1, 1], dtype: "float32" } },
|
| 101 |
});
|
| 102 |
```
|
build/webgpu/bench.json
CHANGED
|
@@ -73,7 +73,7 @@
|
|
| 73 |
"name": "reducemin-rank3-spatial-axes12-f32-128x256x256-pathology",
|
| 74 |
"preset": "stress",
|
| 75 |
"provenance": {
|
| 76 |
-
"source": "
|
| 77 |
"notes": "Rank-3 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
|
| 78 |
},
|
| 79 |
"attrs": { "axes": [1, 2], "keepdims": 1 },
|
|
@@ -85,7 +85,7 @@
|
|
| 85 |
"name": "reducemin-rank4-spatial-axes23-f32-2x64x256x256-pathology",
|
| 86 |
"preset": "stress",
|
| 87 |
"provenance": {
|
| 88 |
-
"source": "
|
| 89 |
"notes": "Rank-4 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
|
| 90 |
},
|
| 91 |
"attrs": { "axes": [2, 3], "keepdims": 1 },
|
|
|
|
| 73 |
"name": "reducemin-rank3-spatial-axes12-f32-128x256x256-pathology",
|
| 74 |
"preset": "stress",
|
| 75 |
"provenance": {
|
| 76 |
+
"source": "synthetic",
|
| 77 |
"notes": "Rank-3 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
|
| 78 |
},
|
| 79 |
"attrs": { "axes": [1, 2], "keepdims": 1 },
|
|
|
|
| 85 |
"name": "reducemin-rank4-spatial-axes23-f32-2x64x256x256-pathology",
|
| 86 |
"preset": "stress",
|
| 87 |
"provenance": {
|
| 88 |
+
"source": "synthetic",
|
| 89 |
"notes": "Rank-4 multi-axis reduction with 128 output lanes, each serially scanning a 256x256 plane."
|
| 90 |
},
|
| 91 |
"attrs": { "axes": [2, 3], "keepdims": 1 },
|
build/webgpu/manifest.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
| 33 |
"ROW_SERIAL_MAX_COLS": { "default": 1024 }
|
| 34 |
},
|
| 35 |
"derive": {
|
|
|
|
| 36 |
"emptyNarrowInputOk": "numel(shapes.x) > 0 or (tensorDtypes.x != \"int8\" and tensorDtypes.x != \"uint8\")",
|
| 37 |
"deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
|
| 38 |
"reduceWorkgroupSize": "min(tunables.WORKGROUP_SIZE, deviceWorkgroupCap)",
|
|
@@ -59,62 +60,75 @@
|
|
| 59 |
"flatScratchBytes": "flatSplitCount * dtypeBytes(\"float32\")",
|
| 60 |
"flatPathFits": "treeWorkgroupOk and flatSplitCount <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and flatScratchBytes <= device.limits.maxStorageBufferBindingSize and flatScratchBytes <= device.limits.maxBufferSize",
|
| 61 |
"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",
|
| 62 |
-
"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)))"
|
|
|
|
|
|
|
| 63 |
},
|
| 64 |
"bindings": {
|
| 65 |
-
"
|
| 66 |
-
"x": { "buffer": "read-only-storage", "elementType": "$vectorScalar" },
|
| 67 |
-
"y_2": { "name": "y", "buffer": "storage", "elementType": "$T" },
|
| 68 |
-
"params_2": {
|
| 69 |
"name": "params",
|
| 70 |
-
"buffer": "uniform",
|
| 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 |
-
"
|
| 77 |
-
"params_3": {
|
| 78 |
"name": "params",
|
| 79 |
-
"buffer": "uniform",
|
| 80 |
"struct": [
|
| 81 |
-
{ "name": "rows", "type": "u32", "value": "
|
| 82 |
-
{ "name": "
|
| 83 |
]
|
| 84 |
},
|
| 85 |
-
"
|
| 86 |
"name": "params",
|
| 87 |
-
"
|
| 88 |
-
"struct": [{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }]
|
| 89 |
},
|
| 90 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
"name": "params",
|
| 92 |
-
"buffer": "uniform",
|
| 93 |
"struct": [
|
| 94 |
{ "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
|
| 95 |
-
{ "name": "
|
| 96 |
]
|
| 97 |
},
|
| 98 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
"name": "params",
|
| 100 |
-
"buffer": "uniform",
|
| 101 |
"struct": [
|
| 102 |
{ "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
|
| 103 |
-
{ "name": "
|
| 104 |
]
|
| 105 |
},
|
| 106 |
-
"
|
| 107 |
"name": "params",
|
| 108 |
-
"buffer": "uniform",
|
| 109 |
"struct": [
|
| 110 |
{ "name": "rows", "type": "u32", "value": "1" },
|
| 111 |
{ "name": "cols", "type": "u32", "value": "1" },
|
| 112 |
{ "name": "outCount", "type": "u32", "value": "1" }
|
| 113 |
]
|
| 114 |
},
|
| 115 |
-
"
|
| 116 |
"name": "params",
|
| 117 |
-
"buffer": "uniform",
|
| 118 |
"struct": [
|
| 119 |
{ "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
|
| 120 |
{ "name": "cols", "type": "u32", "value": "1" },
|
|
@@ -122,75 +136,56 @@
|
|
| 122 |
]
|
| 123 |
},
|
| 124 |
"partials": { "buffer": "storage", "elementType": "$partialElement" },
|
| 125 |
-
"
|
| 126 |
"name": "params",
|
| 127 |
-
"buffer": "uniform",
|
| 128 |
"struct": [
|
| 129 |
{ "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
|
| 130 |
{ "name": "inner", "type": "u32", "value": "axisSplitInner" },
|
| 131 |
{ "name": "outputs", "type": "u32", "value": "axisSplitOutputs" }
|
| 132 |
]
|
| 133 |
},
|
| 134 |
-
"
|
| 135 |
-
"
|
| 136 |
"name": "params",
|
| 137 |
-
"buffer": "uniform",
|
| 138 |
-
"struct": [{ "name": "cols", "type": "u32", "value": "axisSplitOutputs" }]
|
| 139 |
-
},
|
| 140 |
-
"params_11": {
|
| 141 |
-
"name": "params",
|
| 142 |
-
"buffer": "uniform",
|
| 143 |
"struct": [
|
| 144 |
{ "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
|
| 145 |
{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }
|
| 146 |
]
|
| 147 |
},
|
| 148 |
-
"
|
| 149 |
-
"name": "params",
|
| 150 |
-
"buffer": "uniform",
|
| 151 |
-
"struct": [{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }]
|
| 152 |
-
},
|
| 153 |
-
"partials_3": { "name": "partials", "buffer": "storage", "elementType": "f32" },
|
| 154 |
-
"params_13": {
|
| 155 |
"name": "params",
|
| 156 |
-
"buffer": "uniform",
|
| 157 |
"struct": [
|
| 158 |
{ "name": "count4", "type": "u32", "value": "floor(numel(shapes.x) / tunables.VECTOR_WIDTH)" },
|
| 159 |
{ "name": "numel", "type": "u32", "value": "numel(shapes.x)" }
|
| 160 |
]
|
| 161 |
},
|
| 162 |
-
"
|
| 163 |
-
"params_14": { "name": "params", "buffer": "uniform", "struct": [{ "name": "cols", "type": "u32", "value": "1" }] },
|
| 164 |
-
"params_15": {
|
| 165 |
"name": "params",
|
| 166 |
-
"buffer": "uniform",
|
| 167 |
"struct": [
|
| 168 |
{ "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
|
| 169 |
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 170 |
]
|
| 171 |
},
|
| 172 |
-
"
|
| 173 |
"name": "params",
|
| 174 |
-
"buffer": "uniform",
|
| 175 |
"struct": [
|
| 176 |
-
{ "name": "rows", "type": "u32", "value": "
|
| 177 |
-
{ "name": "
|
|
|
|
| 178 |
]
|
| 179 |
},
|
| 180 |
-
"
|
| 181 |
"name": "params",
|
| 182 |
-
"buffer": "uniform",
|
| 183 |
"struct": [
|
| 184 |
-
{ "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
|
| 185 |
{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 186 |
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 187 |
]
|
| 188 |
},
|
| 189 |
-
"
|
| 190 |
"name": "params",
|
| 191 |
-
"buffer": "uniform",
|
| 192 |
"struct": [
|
| 193 |
-
{ "name": "
|
|
|
|
| 194 |
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 195 |
]
|
| 196 |
}
|
|
@@ -207,7 +202,7 @@
|
|
| 207 |
"name": "ReduceMin.NarrowEmptyIdentity",
|
| 208 |
"shader": "reduce-narrow-empty-identity.wgsl.jinja",
|
| 209 |
"derive": { "identity": "1 if tensorDtypes.x == \"bool\" else 127 if tensorDtypes.x == \"int8\" else 255" },
|
| 210 |
-
"bindings": [{ "arg": "y", "elementType": "$scalar" }, "
|
| 211 |
"dispatch": {
|
| 212 |
"x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
|
| 213 |
"y": 1,
|
|
@@ -231,15 +226,9 @@
|
|
| 231 |
"id": "main",
|
| 232 |
"name": "ReduceMin.ContiguousSuffixSubgroupVec4",
|
| 233 |
"shader": "reduce-row-subgroup.wgsl.jinja",
|
| 234 |
-
"derive": {
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 238 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 239 |
-
},
|
| 240 |
-
"bindings": ["x", "y_2", "params_2"],
|
| 241 |
-
"dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 },
|
| 242 |
-
"subgroupCollectivesWidth": "portable"
|
| 243 |
}
|
| 244 |
]
|
| 245 |
},
|
|
@@ -257,13 +246,8 @@
|
|
| 257 |
"id": "main",
|
| 258 |
"name": "ReduceMin.ContiguousSuffixTreeVec4",
|
| 259 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 260 |
-
"derive": {
|
| 261 |
-
|
| 262 |
-
"vec4": true,
|
| 263 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 264 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 265 |
-
},
|
| 266 |
-
"bindings": ["x", "y_2", "params_2"],
|
| 267 |
"dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
|
| 268 |
}
|
| 269 |
]
|
|
@@ -281,8 +265,7 @@
|
|
| 281 |
"id": "main",
|
| 282 |
"name": "ReduceMin.ContiguousSuffixTree",
|
| 283 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 284 |
-
"
|
| 285 |
-
"bindings": ["x_2", "y_2", "params_3"],
|
| 286 |
"dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
|
| 287 |
}
|
| 288 |
]
|
|
@@ -321,7 +304,7 @@
|
|
| 321 |
"id": "main",
|
| 322 |
"name": "ReduceMin.NoopEmptyAxes",
|
| 323 |
"shader": "reduce-noop-empty-axes.wgsl.jinja",
|
| 324 |
-
"bindings": ["
|
| 325 |
"dispatch": {
|
| 326 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 327 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -342,10 +325,7 @@
|
|
| 342 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 343 |
"derive": {
|
| 344 |
"reduceWorkgroupSize": "reduceWorkgroupSize",
|
| 345 |
-
"op": "\"min\"",
|
| 346 |
"indexing": "\"multiaxis\"",
|
| 347 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 348 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 349 |
"rank": 3,
|
| 350 |
"reduce": ["hasAxis(attrs.axes, 0, 3)", "hasAxis(attrs.axes, 1, 3)", "hasAxis(attrs.axes, 2, 3)"],
|
| 351 |
"dataShape": "shapes.x",
|
|
@@ -354,7 +334,7 @@
|
|
| 354 |
"keepDims": "attrs.keepdims != 0",
|
| 355 |
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 356 |
},
|
| 357 |
-
"bindings": ["
|
| 358 |
"dispatch": {
|
| 359 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 360 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -375,10 +355,7 @@
|
|
| 375 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 376 |
"derive": {
|
| 377 |
"reduceWorkgroupSize": "reduceWorkgroupSize",
|
| 378 |
-
"op": "\"min\"",
|
| 379 |
"indexing": "\"multiaxis\"",
|
| 380 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 381 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 382 |
"rank": 4,
|
| 383 |
"reduce": ["hasAxis(attrs.axes, 0, 4)", "hasAxis(attrs.axes, 1, 4)", "hasAxis(attrs.axes, 2, 4)", "hasAxis(attrs.axes, 3, 4)"],
|
| 384 |
"dataShape": "shapes.x",
|
|
@@ -387,7 +364,7 @@
|
|
| 387 |
"keepDims": "attrs.keepdims != 0",
|
| 388 |
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 389 |
},
|
| 390 |
-
"bindings": ["
|
| 391 |
"dispatch": {
|
| 392 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 393 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -410,8 +387,7 @@
|
|
| 410 |
"id": "main",
|
| 411 |
"name": "ReduceMin.TreeLastAxis",
|
| 412 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 413 |
-
"
|
| 414 |
-
"bindings": ["x_2", "y_2", "params_5"],
|
| 415 |
"dispatch": {
|
| 416 |
"x": "min(rows(shapes.x, ranks.x - 1), 65535)",
|
| 417 |
"y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
|
|
@@ -436,14 +412,12 @@
|
|
| 436 |
"id": "main",
|
| 437 |
"name": "ReduceMin.SubgroupRowsVec4",
|
| 438 |
"shader": "reduce-row-subgroup-rows.wgsl.jinja",
|
| 439 |
-
"
|
| 440 |
-
"bindings": ["x", "y_2", "params_6"],
|
| 441 |
"dispatch": {
|
| 442 |
"x": "min(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
|
| 443 |
"y": "ceilDiv(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
|
| 444 |
"z": 1
|
| 445 |
-
}
|
| 446 |
-
"subgroupCollectivesWidth": "portable"
|
| 447 |
}
|
| 448 |
]
|
| 449 |
},
|
|
@@ -462,13 +436,8 @@
|
|
| 462 |
"id": "main",
|
| 463 |
"name": "ReduceMin.TreeRowVec4",
|
| 464 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 465 |
-
"derive": {
|
| 466 |
-
|
| 467 |
-
"vec4": true,
|
| 468 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 469 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 470 |
-
},
|
| 471 |
-
"bindings": ["x", "y_2", "params_6"],
|
| 472 |
"dispatch": { "x": "min(lastAxisRows, 65535)", "y": "ceilDiv(lastAxisRows, 65535)", "z": 1 }
|
| 473 |
}
|
| 474 |
]
|
|
@@ -483,14 +452,8 @@
|
|
| 483 |
"id": "main",
|
| 484 |
"name": "ReduceMin.Rank0Scalar",
|
| 485 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 486 |
-
"derive": {
|
| 487 |
-
|
| 488 |
-
"indexing": "\"axis2d\"",
|
| 489 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 490 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 491 |
-
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 492 |
-
},
|
| 493 |
-
"bindings": ["x_2", "y_2", "params_7"],
|
| 494 |
"dispatch": { "x": 1 }
|
| 495 |
}
|
| 496 |
]
|
|
@@ -504,14 +467,8 @@
|
|
| 504 |
"id": "main",
|
| 505 |
"name": "ReduceMin.Rank1Axis0",
|
| 506 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 507 |
-
"derive": {
|
| 508 |
-
|
| 509 |
-
"indexing": "\"axis2d\"",
|
| 510 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 511 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 512 |
-
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 513 |
-
},
|
| 514 |
-
"bindings": ["x_2", "y_2", "params_8"],
|
| 515 |
"dispatch": {
|
| 516 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 517 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -536,13 +493,8 @@
|
|
| 536 |
"id": "split_reduce",
|
| 537 |
"name": "ReduceMin.AxisSplitReduce",
|
| 538 |
"shader": "reduce-axis-split-reduce.wgsl.jinja",
|
| 539 |
-
"derive": {
|
| 540 |
-
|
| 541 |
-
"splitSpec": "splitCount",
|
| 542 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 543 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 544 |
-
},
|
| 545 |
-
"bindings": ["x_2", "partials", "params_9"],
|
| 546 |
"dispatch": {
|
| 547 |
"x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
|
| 548 |
"y": "splitCount",
|
|
@@ -553,8 +505,8 @@
|
|
| 553 |
"id": "combine",
|
| 554 |
"name": "ReduceMin.AxisSplitCombine",
|
| 555 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 556 |
-
"derive": { "
|
| 557 |
-
"bindings": ["
|
| 558 |
"dispatch": {
|
| 559 |
"x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
| 560 |
"y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
|
@@ -581,14 +533,8 @@
|
|
| 581 |
"id": "split_reduce",
|
| 582 |
"name": "ReduceMin.AxisSplitTiledReduce",
|
| 583 |
"shader": "reduce-axis0-tilecols.wgsl.jinja",
|
| 584 |
-
"derive": {
|
| 585 |
-
|
| 586 |
-
"splitSpec": "splitCount",
|
| 587 |
-
"tileCols": "tunables.AXIS_SPLIT_TILE_COLS",
|
| 588 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 589 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 590 |
-
},
|
| 591 |
-
"bindings": ["x_2", "partials", "params_9"],
|
| 592 |
"dispatch": {
|
| 593 |
"x": "min(ceilDiv((axisSplitOutputs), (tileCols)), DISPATCH_FOLD_WIDTH)",
|
| 594 |
"y": "splitCount",
|
|
@@ -599,8 +545,8 @@
|
|
| 599 |
"id": "combine",
|
| 600 |
"name": "ReduceMin.AxisSplitCombine",
|
| 601 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 602 |
-
"derive": { "
|
| 603 |
-
"bindings": ["
|
| 604 |
"dispatch": {
|
| 605 |
"x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
| 606 |
"y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
|
@@ -613,6 +559,7 @@
|
|
| 613 |
"id": "axis0_splitk",
|
| 614 |
"priority": 22,
|
| 615 |
"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"],
|
|
|
|
| 616 |
"derive": {
|
| 617 |
"splitCount": "axis0SplitCount",
|
| 618 |
"partialElement": "\"f32\"",
|
|
@@ -625,13 +572,8 @@
|
|
| 625 |
"id": "split_reduce",
|
| 626 |
"name": "ReduceMin.Axis0SplitKReduce",
|
| 627 |
"shader": "reduce-axis0-splitk-reduce.wgsl.jinja",
|
| 628 |
-
"derive": {
|
| 629 |
-
|
| 630 |
-
"splitSpec": "splitCount",
|
| 631 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 632 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 633 |
-
},
|
| 634 |
-
"bindings": ["x_2", "partials", "params_11"],
|
| 635 |
"dispatch": {
|
| 636 |
"x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
|
| 637 |
"y": "splitCount",
|
|
@@ -642,8 +584,8 @@
|
|
| 642 |
"id": "combine",
|
| 643 |
"name": "ReduceMin.Axis0SplitKCombine",
|
| 644 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 645 |
-
"derive": { "
|
| 646 |
-
"bindings": ["
|
| 647 |
"dispatch": {
|
| 648 |
"x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
|
| 649 |
"y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -662,8 +604,8 @@
|
|
| 662 |
"id": "main",
|
| 663 |
"name": "ReduceMin.Axis0TileCols",
|
| 664 |
"shader": "reduce-axis0-tilecols.wgsl.jinja",
|
| 665 |
-
"derive": {
|
| 666 |
-
"bindings": ["
|
| 667 |
"dispatch": {
|
| 668 |
"x": "min(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
|
| 669 |
"y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
|
|
@@ -676,23 +618,22 @@
|
|
| 676 |
"id": "all_axes_flat",
|
| 677 |
"priority": 31,
|
| 678 |
"when": ["flatParallelCovered"],
|
| 679 |
-
"derive": { "
|
| 680 |
"intermediates": [{ "id": "partials", "dtype": "float32", "shape": "[flatSplitCount]" }],
|
| 681 |
"passes": [
|
| 682 |
{
|
| 683 |
"id": "flat_partial",
|
| 684 |
"name": "ReduceMin.AllAxesFlatPartial",
|
| 685 |
"shader": "reduce-flat-partial.wgsl.jinja",
|
| 686 |
-
"
|
| 687 |
-
"bindings": ["x_2", "partials_3", "params_13"],
|
| 688 |
"dispatch": { "x": "flatSplitCount" }
|
| 689 |
},
|
| 690 |
{
|
| 691 |
"id": "combine",
|
| 692 |
"name": "ReduceMin.AllAxesFlatCombine",
|
| 693 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 694 |
-
"derive": { "
|
| 695 |
-
"bindings": ["
|
| 696 |
"dispatch": { "x": 1 }
|
| 697 |
}
|
| 698 |
]
|
|
@@ -710,10 +651,7 @@
|
|
| 710 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 711 |
"derive": {
|
| 712 |
"reduceWorkgroupSize": "reduceWorkgroupSize",
|
| 713 |
-
"op": "\"min\"",
|
| 714 |
"indexing": "\"rankn\"",
|
| 715 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 716 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 717 |
"rank": "ranks.x",
|
| 718 |
"axisSpec": "reduceAxis",
|
| 719 |
"dataShape": "shapes.x",
|
|
@@ -722,7 +660,7 @@
|
|
| 722 |
"keepDims": "attrs.keepdims != 0",
|
| 723 |
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 724 |
},
|
| 725 |
-
"bindings": ["
|
| 726 |
"dispatch": {
|
| 727 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 728 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -746,19 +684,13 @@
|
|
| 746 |
"id": "main",
|
| 747 |
"name": "ReduceMin.SubgroupRowVec4",
|
| 748 |
"shader": "reduce-row-subgroup.wgsl.jinja",
|
| 749 |
-
"derive": {
|
| 750 |
-
|
| 751 |
-
"vec4": true,
|
| 752 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 753 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 754 |
-
},
|
| 755 |
-
"bindings": ["x", "y_2", "params_6"],
|
| 756 |
"dispatch": {
|
| 757 |
"x": "min(rows(shapes.x, ranks.x - 1), 65535)",
|
| 758 |
"y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
|
| 759 |
"z": 1
|
| 760 |
-
}
|
| 761 |
-
"subgroupCollectivesWidth": "portable"
|
| 762 |
}
|
| 763 |
]
|
| 764 |
},
|
|
@@ -776,19 +708,13 @@
|
|
| 776 |
"id": "main",
|
| 777 |
"name": "ReduceMin.SubgroupRow",
|
| 778 |
"shader": "reduce-row-subgroup.wgsl.jinja",
|
| 779 |
-
"derive": {
|
| 780 |
-
|
| 781 |
-
"vec4": false,
|
| 782 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 783 |
-
"usesF16Spec": "dtypes.T == \"f16\""
|
| 784 |
-
},
|
| 785 |
-
"bindings": ["x_2", "y_2", "params_16"],
|
| 786 |
"dispatch": {
|
| 787 |
"x": "min(rows(shapes.x, ranks.x - 1), 65535)",
|
| 788 |
"y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
|
| 789 |
"z": 1
|
| 790 |
-
}
|
| 791 |
-
"subgroupCollectivesWidth": "portable"
|
| 792 |
}
|
| 793 |
]
|
| 794 |
},
|
|
@@ -801,18 +727,10 @@
|
|
| 801 |
"passes": [
|
| 802 |
{
|
| 803 |
"id": "main",
|
| 804 |
-
"name": "
|
| 805 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 806 |
-
"derive": {
|
| 807 |
-
|
| 808 |
-
"scalar": "dtypes.T",
|
| 809 |
-
"op": "\"min\"",
|
| 810 |
-
"indexing": "\"axis2d\"",
|
| 811 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 812 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 813 |
-
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 814 |
-
},
|
| 815 |
-
"bindings": ["x_2", "y_2", "params_17"],
|
| 816 |
"dispatch": {
|
| 817 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 818 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -829,18 +747,10 @@
|
|
| 829 |
"passes": [
|
| 830 |
{
|
| 831 |
"id": "main",
|
| 832 |
-
"name": "
|
| 833 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 834 |
-
"derive": {
|
| 835 |
-
|
| 836 |
-
"scalar": "dtypes.T",
|
| 837 |
-
"op": "\"min\"",
|
| 838 |
-
"indexing": "\"axis2d\"",
|
| 839 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 840 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 841 |
-
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 842 |
-
},
|
| 843 |
-
"bindings": ["x_2", "y_2", "params_18"],
|
| 844 |
"dispatch": {
|
| 845 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 846 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -852,32 +762,15 @@
|
|
| 852 |
{
|
| 853 |
"id": "all_axes_keepdims",
|
| 854 |
"priority": 30,
|
| 855 |
-
"when": ["not flatParallelCovered", "emptyNarrowInputOk", "f16Ok(dtypes.T)", "ranks.x >= 3", "attrs.keepdims == 1
|
| 856 |
"derive": { "axis": 0, "scalar": "dtypes.T" },
|
| 857 |
"passes": [
|
| 858 |
{
|
| 859 |
"id": "main",
|
| 860 |
"name": "ReduceMin.Rank3AllAxesKeepdims",
|
| 861 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 862 |
-
"derive": {
|
| 863 |
-
|
| 864 |
-
"indexing": "\"axis2d\"",
|
| 865 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 866 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 867 |
-
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 868 |
-
},
|
| 869 |
-
"bindings": [
|
| 870 |
-
"x_2",
|
| 871 |
-
"y_2",
|
| 872 |
-
{
|
| 873 |
-
"name": "params",
|
| 874 |
-
"struct": [
|
| 875 |
-
{ "name": "rows", "type": "u32", "value": "numel(shapes.x)" },
|
| 876 |
-
{ "name": "cols", "type": "u32", "value": "1" },
|
| 877 |
-
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 878 |
-
]
|
| 879 |
-
}
|
| 880 |
-
],
|
| 881 |
"dispatch": {
|
| 882 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 883 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
@@ -889,32 +782,15 @@
|
|
| 889 |
{
|
| 890 |
"id": "all_axes_no_keepdims",
|
| 891 |
"priority": 30,
|
| 892 |
-
"when": ["not flatParallelCovered", "emptyNarrowInputOk", "f16Ok(dtypes.T)", "ranks.x >= 3", "attrs.keepdims == 0
|
| 893 |
"derive": { "axis": 0, "scalar": "dtypes.T" },
|
| 894 |
"passes": [
|
| 895 |
{
|
| 896 |
"id": "main",
|
| 897 |
"name": "ReduceMin.Rank3AllAxesNoKeepdims",
|
| 898 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 899 |
-
"derive": {
|
| 900 |
-
|
| 901 |
-
"indexing": "\"axis2d\"",
|
| 902 |
-
"castF32": "dtypes.T == \"f16\"",
|
| 903 |
-
"usesF16Spec": "dtypes.T == \"f16\"",
|
| 904 |
-
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 905 |
-
},
|
| 906 |
-
"bindings": [
|
| 907 |
-
"x_2",
|
| 908 |
-
"y_2",
|
| 909 |
-
{
|
| 910 |
-
"name": "params",
|
| 911 |
-
"struct": [
|
| 912 |
-
{ "name": "rows", "type": "u32", "value": "numel(shapes.x)" },
|
| 913 |
-
{ "name": "cols", "type": "u32", "value": "1" },
|
| 914 |
-
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 915 |
-
]
|
| 916 |
-
}
|
| 917 |
-
],
|
| 918 |
"dispatch": {
|
| 919 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 920 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 33 |
"ROW_SERIAL_MAX_COLS": { "default": 1024 }
|
| 34 |
},
|
| 35 |
"derive": {
|
| 36 |
+
"reduceOp": "\"min\"",
|
| 37 |
"emptyNarrowInputOk": "numel(shapes.x) > 0 or (tensorDtypes.x != \"int8\" and tensorDtypes.x != \"uint8\")",
|
| 38 |
"deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
|
| 39 |
"reduceWorkgroupSize": "min(tunables.WORKGROUP_SIZE, deviceWorkgroupCap)",
|
|
|
|
| 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 |
+
"op": "reduceOp",
|
| 65 |
+
"castF32": "dtypes.T == \"f16\""
|
| 66 |
},
|
| 67 |
"bindings": {
|
| 68 |
+
"params_suffix_vec4": {
|
|
|
|
|
|
|
|
|
|
| 69 |
"name": "params",
|
|
|
|
| 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 |
+
"params_reduce_row_tree": {
|
|
|
|
| 76 |
"name": "params",
|
|
|
|
| 77 |
"struct": [
|
| 78 |
+
{ "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
|
| 79 |
+
{ "name": "chunkCount", "type": "u32", "value": "dim(shapes.x, ranks.x - 1) / tunables.VECTOR_WIDTH" }
|
| 80 |
]
|
| 81 |
},
|
| 82 |
+
"params_axis_split_combine": {
|
| 83 |
"name": "params",
|
| 84 |
+
"struct": [{ "name": "cols", "type": "u32", "value": "axisSplitOutputs" }]
|
|
|
|
| 85 |
},
|
| 86 |
+
"params_axis0_combine": {
|
| 87 |
+
"name": "params",
|
| 88 |
+
"struct": [{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }]
|
| 89 |
+
},
|
| 90 |
+
"partials_flat": { "name": "partials", "buffer": "storage", "elementType": "f32" },
|
| 91 |
+
"partials_flat_read": { "name": "partials", "buffer": "read-only-storage", "elementType": "f32" },
|
| 92 |
+
"params_all_axes_flat": { "name": "params", "struct": [{ "name": "cols", "type": "u32", "value": "1" }] },
|
| 93 |
+
"params_rows_chunk_count": {
|
| 94 |
"name": "params",
|
|
|
|
| 95 |
"struct": [
|
| 96 |
{ "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
|
| 97 |
+
{ "name": "chunkCount", "type": "u32", "value": "dim(shapes.x, ranks.x - 1)" }
|
| 98 |
]
|
| 99 |
},
|
| 100 |
+
"x": { "elementType": "$vectorScalar" },
|
| 101 |
+
"y": { "elementType": "$T" },
|
| 102 |
+
"x_t": { "name": "x", "elementType": "$T" },
|
| 103 |
+
"params_main": {
|
| 104 |
+
"name": "params",
|
| 105 |
+
"struct": [
|
| 106 |
+
{ "name": "rows", "type": "u32", "value": "numel(shapes.y)" },
|
| 107 |
+
{ "name": "cols", "type": "u32", "value": "numel(shapes.x) / numel(shapes.y)" }
|
| 108 |
+
]
|
| 109 |
+
},
|
| 110 |
+
"params_count": { "name": "params", "struct": [{ "name": "count", "type": "u32", "value": "numel(shapes.y)" }] },
|
| 111 |
+
"params_out_count": {
|
| 112 |
+
"name": "params",
|
| 113 |
+
"struct": [{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }]
|
| 114 |
+
},
|
| 115 |
+
"params_rows_cols": {
|
| 116 |
"name": "params",
|
|
|
|
| 117 |
"struct": [
|
| 118 |
{ "name": "rows", "type": "u32", "value": "rows(shapes.x, ranks.x - 1)" },
|
| 119 |
+
{ "name": "cols", "type": "u32", "value": "dim(shapes.x, ranks.x - 1)" }
|
| 120 |
]
|
| 121 |
},
|
| 122 |
+
"params_rank0_scalar": {
|
| 123 |
"name": "params",
|
|
|
|
| 124 |
"struct": [
|
| 125 |
{ "name": "rows", "type": "u32", "value": "1" },
|
| 126 |
{ "name": "cols", "type": "u32", "value": "1" },
|
| 127 |
{ "name": "outCount", "type": "u32", "value": "1" }
|
| 128 |
]
|
| 129 |
},
|
| 130 |
+
"params_rank1_axis0": {
|
| 131 |
"name": "params",
|
|
|
|
| 132 |
"struct": [
|
| 133 |
{ "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
|
| 134 |
{ "name": "cols", "type": "u32", "value": "1" },
|
|
|
|
| 136 |
]
|
| 137 |
},
|
| 138 |
"partials": { "buffer": "storage", "elementType": "$partialElement" },
|
| 139 |
+
"params_axis_split": {
|
| 140 |
"name": "params",
|
|
|
|
| 141 |
"struct": [
|
| 142 |
{ "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
|
| 143 |
{ "name": "inner", "type": "u32", "value": "axisSplitInner" },
|
| 144 |
{ "name": "outputs", "type": "u32", "value": "axisSplitOutputs" }
|
| 145 |
]
|
| 146 |
},
|
| 147 |
+
"partials_combine": { "name": "partials", "buffer": "read-only-storage", "elementType": "$partialElement" },
|
| 148 |
+
"params_split_reduce": {
|
| 149 |
"name": "params",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
"struct": [
|
| 151 |
{ "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
|
| 152 |
{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" }
|
| 153 |
]
|
| 154 |
},
|
| 155 |
+
"params_count4_numel": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
"name": "params",
|
|
|
|
| 157 |
"struct": [
|
| 158 |
{ "name": "count4", "type": "u32", "value": "floor(numel(shapes.x) / tunables.VECTOR_WIDTH)" },
|
| 159 |
{ "name": "numel", "type": "u32", "value": "numel(shapes.x)" }
|
| 160 |
]
|
| 161 |
},
|
| 162 |
+
"params_axis_dim_out_count": {
|
|
|
|
|
|
|
| 163 |
"name": "params",
|
|
|
|
| 164 |
"struct": [
|
| 165 |
{ "name": "axisDim", "type": "u32", "value": "axisSplitDim" },
|
| 166 |
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 167 |
]
|
| 168 |
},
|
| 169 |
+
"params_axis0": {
|
| 170 |
"name": "params",
|
|
|
|
| 171 |
"struct": [
|
| 172 |
+
{ "name": "rows", "type": "u32", "value": "dim(shapes.x, 0)" },
|
| 173 |
+
{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 174 |
+
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 175 |
]
|
| 176 |
},
|
| 177 |
+
"params_axis1": {
|
| 178 |
"name": "params",
|
|
|
|
| 179 |
"struct": [
|
|
|
|
| 180 |
{ "name": "cols", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 181 |
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 182 |
]
|
| 183 |
},
|
| 184 |
+
"params_all_axes": {
|
| 185 |
"name": "params",
|
|
|
|
| 186 |
"struct": [
|
| 187 |
+
{ "name": "rows", "type": "u32", "value": "numel(shapes.x)" },
|
| 188 |
+
{ "name": "cols", "type": "u32", "value": "1" },
|
| 189 |
{ "name": "outCount", "type": "u32", "value": "numel(shapes.y)" }
|
| 190 |
]
|
| 191 |
}
|
|
|
|
| 202 |
"name": "ReduceMin.NarrowEmptyIdentity",
|
| 203 |
"shader": "reduce-narrow-empty-identity.wgsl.jinja",
|
| 204 |
"derive": { "identity": "1 if tensorDtypes.x == \"bool\" else 127 if tensorDtypes.x == \"int8\" else 255" },
|
| 205 |
+
"bindings": [{ "arg": "y", "elementType": "$scalar" }, "params_count"],
|
| 206 |
"dispatch": {
|
| 207 |
"x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
|
| 208 |
"y": 1,
|
|
|
|
| 226 |
"id": "main",
|
| 227 |
"name": "ReduceMin.ContiguousSuffixSubgroupVec4",
|
| 228 |
"shader": "reduce-row-subgroup.wgsl.jinja",
|
| 229 |
+
"derive": { "vec4": true },
|
| 230 |
+
"bindings": ["x", "y", "params_suffix_vec4"],
|
| 231 |
+
"dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
}
|
| 233 |
]
|
| 234 |
},
|
|
|
|
| 246 |
"id": "main",
|
| 247 |
"name": "ReduceMin.ContiguousSuffixTreeVec4",
|
| 248 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 249 |
+
"derive": { "vec4": true },
|
| 250 |
+
"bindings": ["x", "y", "params_suffix_vec4"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
"dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
|
| 252 |
}
|
| 253 |
]
|
|
|
|
| 265 |
"id": "main",
|
| 266 |
"name": "ReduceMin.ContiguousSuffixTree",
|
| 267 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 268 |
+
"bindings": ["x_t", "y", "params_main"],
|
|
|
|
| 269 |
"dispatch": { "x": "min(numel(shapes.y), 65535)", "y": "ceilDiv(numel(shapes.y), 65535)", "z": 1 }
|
| 270 |
}
|
| 271 |
]
|
|
|
|
| 304 |
"id": "main",
|
| 305 |
"name": "ReduceMin.NoopEmptyAxes",
|
| 306 |
"shader": "reduce-noop-empty-axes.wgsl.jinja",
|
| 307 |
+
"bindings": ["x_t", "y", "params_count"],
|
| 308 |
"dispatch": {
|
| 309 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 310 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 325 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 326 |
"derive": {
|
| 327 |
"reduceWorkgroupSize": "reduceWorkgroupSize",
|
|
|
|
| 328 |
"indexing": "\"multiaxis\"",
|
|
|
|
|
|
|
| 329 |
"rank": 3,
|
| 330 |
"reduce": ["hasAxis(attrs.axes, 0, 3)", "hasAxis(attrs.axes, 1, 3)", "hasAxis(attrs.axes, 2, 3)"],
|
| 331 |
"dataShape": "shapes.x",
|
|
|
|
| 334 |
"keepDims": "attrs.keepdims != 0",
|
| 335 |
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 336 |
},
|
| 337 |
+
"bindings": ["x_t", "y", "params_out_count"],
|
| 338 |
"dispatch": {
|
| 339 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 340 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 355 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 356 |
"derive": {
|
| 357 |
"reduceWorkgroupSize": "reduceWorkgroupSize",
|
|
|
|
| 358 |
"indexing": "\"multiaxis\"",
|
|
|
|
|
|
|
| 359 |
"rank": 4,
|
| 360 |
"reduce": ["hasAxis(attrs.axes, 0, 4)", "hasAxis(attrs.axes, 1, 4)", "hasAxis(attrs.axes, 2, 4)", "hasAxis(attrs.axes, 3, 4)"],
|
| 361 |
"dataShape": "shapes.x",
|
|
|
|
| 364 |
"keepDims": "attrs.keepdims != 0",
|
| 365 |
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 366 |
},
|
| 367 |
+
"bindings": ["x_t", "y", "params_out_count"],
|
| 368 |
"dispatch": {
|
| 369 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 370 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 387 |
"id": "main",
|
| 388 |
"name": "ReduceMin.TreeLastAxis",
|
| 389 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 390 |
+
"bindings": ["x_t", "y", "params_rows_cols"],
|
|
|
|
| 391 |
"dispatch": {
|
| 392 |
"x": "min(rows(shapes.x, ranks.x - 1), 65535)",
|
| 393 |
"y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
|
|
|
|
| 412 |
"id": "main",
|
| 413 |
"name": "ReduceMin.SubgroupRowsVec4",
|
| 414 |
"shader": "reduce-row-subgroup-rows.wgsl.jinja",
|
| 415 |
+
"bindings": ["x", "y", "params_reduce_row_tree"],
|
|
|
|
| 416 |
"dispatch": {
|
| 417 |
"x": "min(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
|
| 418 |
"y": "ceilDiv(ceilDiv(lastAxisRows, reduceWorkgroupSize / device.adapterInfo.subgroupMaxSize), 65535)",
|
| 419 |
"z": 1
|
| 420 |
+
}
|
|
|
|
| 421 |
}
|
| 422 |
]
|
| 423 |
},
|
|
|
|
| 436 |
"id": "main",
|
| 437 |
"name": "ReduceMin.TreeRowVec4",
|
| 438 |
"shader": "reduce-row-tree.wgsl.jinja",
|
| 439 |
+
"derive": { "vec4": true },
|
| 440 |
+
"bindings": ["x", "y", "params_reduce_row_tree"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
"dispatch": { "x": "min(lastAxisRows, 65535)", "y": "ceilDiv(lastAxisRows, 65535)", "z": 1 }
|
| 442 |
}
|
| 443 |
]
|
|
|
|
| 452 |
"id": "main",
|
| 453 |
"name": "ReduceMin.Rank0Scalar",
|
| 454 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 455 |
+
"derive": { "indexing": "\"axis2d\"", "logicalBool": "tensorDtypes.x == \"bool\"" },
|
| 456 |
+
"bindings": ["x_t", "y", "params_rank0_scalar"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
"dispatch": { "x": 1 }
|
| 458 |
}
|
| 459 |
]
|
|
|
|
| 467 |
"id": "main",
|
| 468 |
"name": "ReduceMin.Rank1Axis0",
|
| 469 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 470 |
+
"derive": { "indexing": "\"axis2d\"", "logicalBool": "tensorDtypes.x == \"bool\"" },
|
| 471 |
+
"bindings": ["x_t", "y", "params_rank1_axis0"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
"dispatch": {
|
| 473 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 474 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 493 |
"id": "split_reduce",
|
| 494 |
"name": "ReduceMin.AxisSplitReduce",
|
| 495 |
"shader": "reduce-axis-split-reduce.wgsl.jinja",
|
| 496 |
+
"derive": { "splitSpec": "splitCount" },
|
| 497 |
+
"bindings": ["x_t", "partials", "params_axis_split"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
"dispatch": {
|
| 499 |
"x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
|
| 500 |
"y": "splitCount",
|
|
|
|
| 505 |
"id": "combine",
|
| 506 |
"name": "ReduceMin.AxisSplitCombine",
|
| 507 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 508 |
+
"derive": { "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
|
| 509 |
+
"bindings": ["partials_combine", "y", "params_axis_split_combine"],
|
| 510 |
"dispatch": {
|
| 511 |
"x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
| 512 |
"y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 533 |
"id": "split_reduce",
|
| 534 |
"name": "ReduceMin.AxisSplitTiledReduce",
|
| 535 |
"shader": "reduce-axis0-tilecols.wgsl.jinja",
|
| 536 |
+
"derive": { "splitSpec": "splitCount" },
|
| 537 |
+
"bindings": ["x_t", "partials", "params_axis_split"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
"dispatch": {
|
| 539 |
"x": "min(ceilDiv((axisSplitOutputs), (tileCols)), DISPATCH_FOLD_WIDTH)",
|
| 540 |
"y": "splitCount",
|
|
|
|
| 545 |
"id": "combine",
|
| 546 |
"name": "ReduceMin.AxisSplitCombine",
|
| 547 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 548 |
+
"derive": { "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
|
| 549 |
+
"bindings": ["partials_combine", "y", "params_axis_split_combine"],
|
| 550 |
"dispatch": {
|
| 551 |
"x": "min(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
| 552 |
"y": "ceilDiv(ceilDiv((axisSplitOutputs), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 559 |
"id": "axis0_splitk",
|
| 560 |
"priority": 22,
|
| 561 |
"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"],
|
| 562 |
+
"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"],
|
| 563 |
"derive": {
|
| 564 |
"splitCount": "axis0SplitCount",
|
| 565 |
"partialElement": "\"f32\"",
|
|
|
|
| 572 |
"id": "split_reduce",
|
| 573 |
"name": "ReduceMin.Axis0SplitKReduce",
|
| 574 |
"shader": "reduce-axis0-splitk-reduce.wgsl.jinja",
|
| 575 |
+
"derive": { "splitSpec": "splitCount" },
|
| 576 |
+
"bindings": ["x_t", "partials", "params_split_reduce"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 577 |
"dispatch": {
|
| 578 |
"x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), DISPATCH_FOLD_WIDTH)",
|
| 579 |
"y": "splitCount",
|
|
|
|
| 584 |
"id": "combine",
|
| 585 |
"name": "ReduceMin.Axis0SplitKCombine",
|
| 586 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 587 |
+
"derive": { "splitSpec": "splitCount", "outputF16": "dtypes.T == \"f16\"" },
|
| 588 |
+
"bindings": ["partials_combine", "y", "params_axis0_combine"],
|
| 589 |
"dispatch": {
|
| 590 |
"x": "min(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
|
| 591 |
"y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 604 |
"id": "main",
|
| 605 |
"name": "ReduceMin.Axis0TileCols",
|
| 606 |
"shader": "reduce-axis0-tilecols.wgsl.jinja",
|
| 607 |
+
"derive": {},
|
| 608 |
+
"bindings": ["x_t", "y", "params_split_reduce"],
|
| 609 |
"dispatch": {
|
| 610 |
"x": "min(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
|
| 611 |
"y": "ceilDiv(ceilDiv((dim(shapes.x, 1)), (tileCols)), 65535)",
|
|
|
|
| 618 |
"id": "all_axes_flat",
|
| 619 |
"priority": 31,
|
| 620 |
"when": ["flatParallelCovered"],
|
| 621 |
+
"derive": { "workgroupSize": "reduceWorkgroupSize", "split": "flatSplitCount" },
|
| 622 |
"intermediates": [{ "id": "partials", "dtype": "float32", "shape": "[flatSplitCount]" }],
|
| 623 |
"passes": [
|
| 624 |
{
|
| 625 |
"id": "flat_partial",
|
| 626 |
"name": "ReduceMin.AllAxesFlatPartial",
|
| 627 |
"shader": "reduce-flat-partial.wgsl.jinja",
|
| 628 |
+
"bindings": ["x_t", "partials_flat", "params_count4_numel"],
|
|
|
|
| 629 |
"dispatch": { "x": "flatSplitCount" }
|
| 630 |
},
|
| 631 |
{
|
| 632 |
"id": "combine",
|
| 633 |
"name": "ReduceMin.AllAxesFlatCombine",
|
| 634 |
"shader": "reduce-axis0-splitk-combine.wgsl.jinja",
|
| 635 |
+
"derive": { "outputF16": "dtypes.T == \"f16\"" },
|
| 636 |
+
"bindings": ["partials_flat_read", "y", "params_all_axes_flat"],
|
| 637 |
"dispatch": { "x": 1 }
|
| 638 |
}
|
| 639 |
]
|
|
|
|
| 651 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 652 |
"derive": {
|
| 653 |
"reduceWorkgroupSize": "reduceWorkgroupSize",
|
|
|
|
| 654 |
"indexing": "\"rankn\"",
|
|
|
|
|
|
|
| 655 |
"rank": "ranks.x",
|
| 656 |
"axisSpec": "reduceAxis",
|
| 657 |
"dataShape": "shapes.x",
|
|
|
|
| 660 |
"keepDims": "attrs.keepdims != 0",
|
| 661 |
"logicalBool": "tensorDtypes.x == \"bool\""
|
| 662 |
},
|
| 663 |
+
"bindings": ["x_t", "y", "params_axis_dim_out_count"],
|
| 664 |
"dispatch": {
|
| 665 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 666 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 684 |
"id": "main",
|
| 685 |
"name": "ReduceMin.SubgroupRowVec4",
|
| 686 |
"shader": "reduce-row-subgroup.wgsl.jinja",
|
| 687 |
+
"derive": { "vec4": true },
|
| 688 |
+
"bindings": ["x", "y", "params_reduce_row_tree"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 689 |
"dispatch": {
|
| 690 |
"x": "min(rows(shapes.x, ranks.x - 1), 65535)",
|
| 691 |
"y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
|
| 692 |
"z": 1
|
| 693 |
+
}
|
|
|
|
| 694 |
}
|
| 695 |
]
|
| 696 |
},
|
|
|
|
| 708 |
"id": "main",
|
| 709 |
"name": "ReduceMin.SubgroupRow",
|
| 710 |
"shader": "reduce-row-subgroup.wgsl.jinja",
|
| 711 |
+
"derive": { "vec4": false },
|
| 712 |
+
"bindings": ["x_t", "y", "params_rows_chunk_count"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 713 |
"dispatch": {
|
| 714 |
"x": "min(rows(shapes.x, ranks.x - 1), 65535)",
|
| 715 |
"y": "ceilDiv(rows(shapes.x, ranks.x - 1), 65535)",
|
| 716 |
"z": 1
|
| 717 |
+
}
|
|
|
|
| 718 |
}
|
| 719 |
]
|
| 720 |
},
|
|
|
|
| 727 |
"passes": [
|
| 728 |
{
|
| 729 |
"id": "main",
|
| 730 |
+
"name": "ReduceMin.Axis0",
|
| 731 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 732 |
+
"derive": { "indexing": "\"axis2d\"", "logicalBool": "tensorDtypes.x == \"bool\"" },
|
| 733 |
+
"bindings": ["x_t", "y", "params_axis0"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 734 |
"dispatch": {
|
| 735 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 736 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 747 |
"passes": [
|
| 748 |
{
|
| 749 |
"id": "main",
|
| 750 |
+
"name": "ReduceMin.Axis1",
|
| 751 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 752 |
+
"derive": { "indexing": "\"axis2d\"", "logicalBool": "tensorDtypes.x == \"bool\"" },
|
| 753 |
+
"bindings": ["x_t", "y", "params_axis1"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 754 |
"dispatch": {
|
| 755 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 756 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 762 |
{
|
| 763 |
"id": "all_axes_keepdims",
|
| 764 |
"priority": 30,
|
| 765 |
+
"when": ["not flatParallelCovered", "emptyNarrowInputOk", "f16Ok(dtypes.T)", "ranks.x >= 3", "attrs.keepdims == 1", "ranks.y == ranks.x", "numel(shapes.y) == 1"],
|
| 766 |
"derive": { "axis": 0, "scalar": "dtypes.T" },
|
| 767 |
"passes": [
|
| 768 |
{
|
| 769 |
"id": "main",
|
| 770 |
"name": "ReduceMin.Rank3AllAxesKeepdims",
|
| 771 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 772 |
+
"derive": { "indexing": "\"axis2d\"", "logicalBool": "tensorDtypes.x == \"bool\"" },
|
| 773 |
+
"bindings": ["x_t", "y", "params_all_axes"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 774 |
"dispatch": {
|
| 775 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 776 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
|
|
|
| 782 |
{
|
| 783 |
"id": "all_axes_no_keepdims",
|
| 784 |
"priority": 30,
|
| 785 |
+
"when": ["not flatParallelCovered", "emptyNarrowInputOk", "f16Ok(dtypes.T)", "ranks.x >= 3", "attrs.keepdims == 0", "ranks.y == 0"],
|
| 786 |
"derive": { "axis": 0, "scalar": "dtypes.T" },
|
| 787 |
"passes": [
|
| 788 |
{
|
| 789 |
"id": "main",
|
| 790 |
"name": "ReduceMin.Rank3AllAxesNoKeepdims",
|
| 791 |
"shader": "reduce-serial-axis.wgsl.jinja",
|
| 792 |
+
"derive": { "indexing": "\"axis2d\"", "logicalBool": "tensorDtypes.x == \"bool\"" },
|
| 793 |
+
"bindings": ["x_t", "y", "params_all_axes"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 794 |
"dispatch": {
|
| 795 |
"x": "min(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
| 796 |
"y": "ceilDiv(ceilDiv((numel(shapes.y)), (reduceWorkgroupSize)), 65535)",
|
build/webgpu/metadata.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
| 1 |
{
|
| 2 |
"name": "ai.onnx.ReduceMin",
|
| 3 |
-
"id": "
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
-
"bench.json": "
|
| 11 |
"datamove-flat-copy-x4-tail.wgsl.jinja": "1u3sJCuYJ8lM8QU7/oOPB6Q6A7vkcOxCR03RGiakHWo=",
|
| 12 |
-
"manifest.json": "
|
| 13 |
-
"reduce-axis-split-reduce.wgsl.jinja": "
|
| 14 |
-
"reduce-axis0-splitk-combine.wgsl.jinja": "
|
| 15 |
-
"reduce-axis0-splitk-reduce.wgsl.jinja": "
|
| 16 |
-
"reduce-axis0-tilecols.wgsl.jinja": "
|
| 17 |
-
"reduce-flat-partial.wgsl.jinja": "+
|
| 18 |
"reduce-narrow-empty-identity.wgsl.jinja": "XmLNK4zp0ru14DEm8TL5CAL74RVu7aVPrFo3WSJX3wo=",
|
| 19 |
-
"reduce-noop-empty-axes.wgsl.jinja": "
|
| 20 |
-
"reduce-row-subgroup-rows.wgsl.jinja": "
|
| 21 |
-
"reduce-row-subgroup.wgsl.jinja": "
|
| 22 |
-
"reduce-row-tree.wgsl.jinja": "
|
| 23 |
-
"reduce-serial-axis.wgsl.jinja": "
|
| 24 |
-
"test.json": "
|
| 25 |
}
|
| 26 |
},
|
| 27 |
-
"provenance": { "kernel": { "sha": "
|
| 28 |
"webgpu": {
|
| 29 |
-
"manifestSpec": "2.
|
| 30 |
"variants": {
|
| 31 |
"narrow_empty_identity": ["reduce-narrow-empty-identity.wgsl.jinja"],
|
| 32 |
"contiguous_suffix_subgroup_vec4": ["reduce-row-subgroup.wgsl.jinja"],
|
|
|
|
| 1 |
{
|
| 2 |
"name": "ai.onnx.ReduceMin",
|
| 3 |
+
"id": "_ai_onnx_reducemin_webgpu_6673c73",
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
+
"bench.json": "t2isnzkULzGR9Ki2/j7Gg6PyxV3TDPc+ovEKMuVvYWI=",
|
| 11 |
"datamove-flat-copy-x4-tail.wgsl.jinja": "1u3sJCuYJ8lM8QU7/oOPB6Q6A7vkcOxCR03RGiakHWo=",
|
| 12 |
+
"manifest.json": "vZJEw1TU0yAeZ9SzFGoIujiecANsX96IVN1/QXbvqTQ=",
|
| 13 |
+
"reduce-axis-split-reduce.wgsl.jinja": "xo4jwtx/MeHIZkh2KU31ZCR+Ak58xOe4Cg3HWrcvdqo=",
|
| 14 |
+
"reduce-axis0-splitk-combine.wgsl.jinja": "Zf7tz8nrapi2KMZIbv8cT2f4pliJoJHj4iwS4hc/6ms=",
|
| 15 |
+
"reduce-axis0-splitk-reduce.wgsl.jinja": "RVy0BuBzgen99cwoM0KNiZ6OPnc8ColMSsME3IZKLI4=",
|
| 16 |
+
"reduce-axis0-tilecols.wgsl.jinja": "bxt1WvxNRg3bgU4R+s5JUlHZjqH0THZZic++EJ7X+yA=",
|
| 17 |
+
"reduce-flat-partial.wgsl.jinja": "S573jG+5ZoOcPEzKnD8h3nvPNYOuM8+ku15d+15ypBA=",
|
| 18 |
"reduce-narrow-empty-identity.wgsl.jinja": "XmLNK4zp0ru14DEm8TL5CAL74RVu7aVPrFo3WSJX3wo=",
|
| 19 |
+
"reduce-noop-empty-axes.wgsl.jinja": "MMZEXHeACtGwvDaH0nAyvMal+tAIjYrh2yxDuLfh1Lw=",
|
| 20 |
+
"reduce-row-subgroup-rows.wgsl.jinja": "5cMGE1dmrcawwHdoxQD8kPSurwA1wNHyoAQfxAOE7vU=",
|
| 21 |
+
"reduce-row-subgroup.wgsl.jinja": "7bRBWIQ7oHOq/E54hGrmXIzLznzVH9oTfYaf1jmBzV8=",
|
| 22 |
+
"reduce-row-tree.wgsl.jinja": "a+iEV/cBkrlsVz6NS0GY/TAsUSX5rO4N14wY1dKfDi0=",
|
| 23 |
+
"reduce-serial-axis.wgsl.jinja": "ZZ1kKKhlLr3cMnRDRBhWm0ZDMluJwcoqmpRmL9ukuYM=",
|
| 24 |
+
"test.json": "9zz9YIFSmjUPFmYqqDm3yRVIjl4dJQpSTgGyxTIeN7I="
|
| 25 |
}
|
| 26 |
},
|
| 27 |
+
"provenance": { "kernel": { "sha": "6fdf6301e2bbcc2f03bf1eaf493b7ad55ef33afc", "dirty": false } },
|
| 28 |
"webgpu": {
|
| 29 |
+
"manifestSpec": "2.1",
|
| 30 |
"variants": {
|
| 31 |
"narrow_empty_identity": ["reduce-narrow-empty-identity.wgsl.jinja"],
|
| 32 |
"contiguous_suffix_subgroup_vec4": ["reduce-row-subgroup.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
|
| 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 |
-
{%
|
| 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,19 @@
|
|
| 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 |
-
{%
|
| 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 +30,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 |
-
|
|
|
|
|
|
|
| 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" %}
|
|
@@ -74,13 +58,6 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
|
|
| 74 |
let finite_or_inf = select(global_max + log(sum), global_max, has_positive_inf);
|
| 75 |
y[col] = {{ yv }}select(finite_or_inf, nan_value, has_nan){{ vy }};
|
| 76 |
{% else %}
|
| 77 |
-
{% if intMode %}
|
| 78 |
-
{% if op == "prod" %}
|
| 79 |
-
var total = 1i;
|
| 80 |
-
{% else %}
|
| 81 |
-
var total = 0i;
|
| 82 |
-
{% endif %}
|
| 83 |
-
{% else %}
|
| 84 |
{% if op == "max" %}
|
| 85 |
var total = reduction_identity();
|
| 86 |
{% elif op == "min" %}
|
|
@@ -89,7 +66,6 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
|
|
| 89 |
var total = 1.0;
|
| 90 |
{% else %}
|
| 91 |
var total = 0.0;
|
| 92 |
-
{% endif %}
|
| 93 |
{% endif %}
|
| 94 |
for (var seg = 0u; seg < SPLIT; seg = seg + 1u) {
|
| 95 |
let p = partials[seg * params.cols + col];
|
|
@@ -101,9 +77,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 yv = "f16(" if outputF16 else "" %}
|
| 6 |
{% set vy = ")" if outputF16 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;
|
|
|
|
|
|
|
|
|
|
| 18 |
{% if op == "logsumexp" %}
|
| 19 |
const F32_MIN: f32 = -3.4028234663852886e38;
|
| 20 |
const F32_MAX: f32 = 3.4028234663852886e38;
|
|
|
|
| 30 |
@compute @workgroup_size(WG, 1, 1)
|
| 31 |
fn main(@builtin(global_invocation_id) gid: vec3<u32>,
|
| 32 |
@builtin(num_workgroups) nwg: vec3<u32>) {
|
| 33 |
+
// The start already folds gid.y in, so the stride must span every y row too;
|
| 34 |
+
// an x-only stride would send y = 0 lanes over columns the y >= 1 rows own.
|
| 35 |
+
let stride = nwg.x * nwg.y * WG;
|
| 36 |
let start = (gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG) + gid.x;
|
| 37 |
for (var col = start; col < params.cols; col = col + stride) {
|
| 38 |
{% if op == "logsumexp" %}
|
|
|
|
| 58 |
let finite_or_inf = select(global_max + log(sum), global_max, has_positive_inf);
|
| 59 |
y[col] = {{ yv }}select(finite_or_inf, nan_value, has_nan){{ vy }};
|
| 60 |
{% else %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
{% if op == "max" %}
|
| 62 |
var total = reduction_identity();
|
| 63 |
{% elif op == "min" %}
|
|
|
|
| 66 |
var total = 1.0;
|
| 67 |
{% else %}
|
| 68 |
var total = 0.0;
|
|
|
|
| 69 |
{% endif %}
|
| 70 |
for (var seg = 0u; seg < SPLIT; seg = seg + 1u) {
|
| 71 |
let p = partials[seg * params.cols + col];
|
|
|
|
| 77 |
total = total + p;
|
| 78 |
{% endif %}
|
| 79 |
}
|
|
|
|
|
|
|
|
|
|
| 80 |
{% if op == "l2" %}
|
| 81 |
y[col] = {{ yv }}sqrt(total){{ vy }};
|
| 82 |
{% 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 |
-
{%
|
| 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,17 +13,13 @@
|
|
| 13 |
{% set rowEnd = "params.rows" %}
|
| 14 |
{% set elem = "x[inputBase + row * params.cols + col]" %}
|
| 15 |
{% endif %}
|
| 16 |
-
{% set
|
| 17 |
-
{% set intMode = intMode is defined and intMode %}
|
| 18 |
{% set scalar = "f32" if castF32 else scalar %}
|
| 19 |
{% if castF32 %}
|
| 20 |
{% set elem = "f32(" ~ elem ~ ")" %}
|
| 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
|
| 81 |
-
|
|
|
|
| 82 |
let outputIndex = col;
|
| 83 |
let in_bounds = col < params.outputs;
|
| 84 |
let seg = wg.y;
|
|
@@ -203,9 +196,9 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid:
|
|
| 203 |
{% if op == "max" or op == "min" %}
|
| 204 |
var acc = lane_identity();
|
| 205 |
{% elif op == "prod" %}
|
| 206 |
-
var acc =
|
| 207 |
{% else %}
|
| 208 |
-
var acc =
|
| 209 |
{% endif %}
|
| 210 |
if (in_bounds) {
|
| 211 |
for (var row = {{ rowBegin }}; row < {{ rowEnd }}; row = row + ROW_LANES) {
|
|
@@ -240,15 +233,10 @@ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid:
|
|
| 240 |
{% if splitMode %}
|
| 241 |
partials[seg * params.outputs + outputIndex] = total;
|
| 242 |
{% else %}
|
| 243 |
-
{% if op == "l2"
|
| 244 |
-
y[outputIndex] = {{ scalar }}(sqrt(f32(total)));
|
| 245 |
-
{% elif op == "l2" %}
|
| 246 |
y[outputIndex] = {{ yv }}sqrt(total){{ vy }};
|
| 247 |
{% elif op == "logsum" %}
|
| 248 |
y[outputIndex] = {{ yv }}log(total){{ vy }};
|
| 249 |
-
{% elif op == "mean" and intMode %}
|
| 250 |
-
// Integer division truncates toward zero.
|
| 251 |
-
y[outputIndex] = total / {{ scalar }}(params.rows);
|
| 252 |
{% elif op == "mean" %}
|
| 253 |
y[outputIndex] = {{ yv }}total / f32(params.rows){{ vy }};
|
| 254 |
{% else %}
|
|
|
|
| 13 |
{% set rowEnd = "params.rows" %}
|
| 14 |
{% set elem = "x[inputBase + row * params.cols + col]" %}
|
| 15 |
{% endif %}
|
| 16 |
+
{% set intMode = false %}
|
|
|
|
| 17 |
{% set scalar = "f32" if castF32 else scalar %}
|
| 18 |
{% if castF32 %}
|
| 19 |
{% set elem = "f32(" ~ elem ~ ")" %}
|
| 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;
|
|
|
|
| 196 |
{% if op == "max" or op == "min" %}
|
| 197 |
var acc = lane_identity();
|
| 198 |
{% elif op == "prod" %}
|
| 199 |
+
var acc = 1.0;
|
| 200 |
{% else %}
|
| 201 |
+
var acc = 0.0;
|
| 202 |
{% endif %}
|
| 203 |
if (in_bounds) {
|
| 204 |
for (var row = {{ rowBegin }}; row < {{ rowEnd }}; row = row + ROW_LANES) {
|
|
|
|
| 233 |
{% if splitMode %}
|
| 234 |
partials[seg * params.outputs + outputIndex] = total;
|
| 235 |
{% else %}
|
| 236 |
+
{% if op == "l2" %}
|
|
|
|
|
|
|
| 237 |
y[outputIndex] = {{ yv }}sqrt(total){{ vy }};
|
| 238 |
{% elif op == "logsum" %}
|
| 239 |
y[outputIndex] = {{ yv }}log(total){{ vy }};
|
|
|
|
|
|
|
|
|
|
| 240 |
{% elif op == "mean" %}
|
| 241 |
y[outputIndex] = {{ yv }}total / f32(params.rows){{ vy }};
|
| 242 |
{% else %}
|
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
|
| 7 |
-
//
|
| 8 |
-
//
|
| 9 |
-
{% set 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 |
-
{%
|
| 30 |
-
}
|
| 31 |
-
{%- endmacro %}
|
| 32 |
-
|
| 33 |
|
| 34 |
const WG: u32 = {{ workgroupSize }}u;
|
| 35 |
var<workgroup> red: array<{{ "i32" if intMode else "f32" }}, WG>;
|
|
@@ -44,13 +31,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
|
|
| 44 |
@builtin(num_workgroups) nwg: vec3<u32>) {
|
| 45 |
let tid = lid.x;
|
| 46 |
let gstride = nwg.x * WG;
|
| 47 |
-
{% if
|
| 48 |
-
{% if op == "prod" %}
|
| 49 |
-
var acc = 1i;
|
| 50 |
-
{% else %}
|
| 51 |
-
var acc = 0i;
|
| 52 |
-
{% endif %}
|
| 53 |
-
{% elif op == "max" %}
|
| 54 |
var acc = reduction_identity();
|
| 55 |
{% elif op == "min" %}
|
| 56 |
var acc = reduction_identity();
|
|
@@ -100,23 +81,23 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
|
|
| 100 |
}
|
| 101 |
red[tid] = acc;
|
| 102 |
workgroupBarrier();
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
loop {
|
| 105 |
-
if (
|
| 106 |
-
if (
|
| 107 |
-
{%
|
| 108 |
-
|
| 109 |
-
{%
|
| 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 |
-
|
| 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 = false %}
|
|
|
|
| 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>;
|
|
|
|
| 31 |
@builtin(num_workgroups) nwg: vec3<u32>) {
|
| 32 |
let tid = lid.x;
|
| 33 |
let gstride = nwg.x * WG;
|
| 34 |
+
{% if op == "max" %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
var acc = reduction_identity();
|
| 36 |
{% elif op == "min" %}
|
| 37 |
var acc = reduction_identity();
|
|
|
|
| 81 |
}
|
| 82 |
red[tid] = acc;
|
| 83 |
workgroupBarrier();
|
| 84 |
+
{% macro wgsl_tree_fold_stmt(a, op, idx, svar) %}
|
| 85 |
+
{% if op == "max" or op == "min" %}
|
| 86 |
+
{{ a }}[{{ idx }}] = {{ op }}({{ a }}[{{ idx }}], {{ a }}[{{ idx }} + {{ svar }}]);{% else %}
|
| 87 |
+
{{ a }}[{{ idx }}] = {{ a }}[{{ idx }}] {{ "*" if op == "prod" else "+" }} {{ a }}[{{ idx }} + {{ svar }}];{% endif %}{% endmacro %}
|
| 88 |
+
{% 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) %}
|
| 89 |
+
var {{ svar }}{{ ": u32 " if typed else " " }}= {{ wg }} / 2u;
|
| 90 |
loop {
|
| 91 |
+
if ({{ svar }} == 0u) { break; }
|
| 92 |
+
if ({{ idx }} < {{ svar }}) {
|
| 93 |
+
{% for a in arrays %}
|
| 94 |
+
{{ wgsl_tree_fold_stmt(a, op, idx, svar) }}
|
| 95 |
+
{% endfor %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
}
|
| 97 |
+
{{ svar }} = {{ svar }} / 2u;
|
| 98 |
workgroupBarrier();
|
| 99 |
+
}{% endmacro %}
|
| 100 |
+
{{ wgsl_tree_fold(["red"], op=op, idx="tid", wg="WG", typed=true, form="head", breakInline=true) }}
|
| 101 |
if (tid == 0u) {
|
| 102 |
partials[wg.x] = red[0];
|
| 103 |
}
|
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 |
-
|
| 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 |
{% if op == "abs" %}
|
| 13 |
y[i] = abs(v);
|
|
|
|
| 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 |
{% if op == "abs" %}
|
| 16 |
y[i] = abs(v);
|
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 |
-
|
| 45 |
-
|
| 46 |
|
| 47 |
const WG: u32 = {{ workgroupSize }}u;
|
| 48 |
-
{%
|
|
|
|
| 49 |
{{ wgsl_minmax_identity("reduction_identity", op, accScalar) }}
|
| 50 |
-
{%
|
| 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 |
-
{%
|
| 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 |
-
{%
|
| 114 |
{% if op == "max" or op == "min" %}
|
| 115 |
let INIT: {{ accScalar }} = reduction_identity();
|
| 116 |
-
{%
|
| 117 |
let INIT: {{ accScalar }} = {{ "1.0" if not isInt else accScalar ~ "(1)" }};
|
| 118 |
-
{%
|
| 119 |
let INIT: {{ accScalar }} = {{ "0.0" if not isInt else accScalar ~ "(0)" }};
|
| 120 |
-
{%
|
| 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 |
-
{%
|
| 125 |
acc4 = max(acc4, v);
|
| 126 |
-
{%
|
| 127 |
acc4 = min(acc4, v);
|
| 128 |
-
{%
|
| 129 |
acc4 = acc4 * v;
|
| 130 |
-
{%
|
| 131 |
acc4 = acc4 + abs(v);
|
| 132 |
-
{%
|
| 133 |
acc4 = acc4 + v * v;
|
| 134 |
-
{%
|
| 135 |
acc4 = acc4 + v;
|
| 136 |
-
{%
|
| 137 |
}
|
| 138 |
-
{%
|
| 139 |
let acc = max(max(acc4.x, acc4.y), max(acc4.z, acc4.w));
|
| 140 |
let total = subgroupMax(acc);
|
| 141 |
-
{%
|
| 142 |
let acc = min(min(acc4.x, acc4.y), min(acc4.z, acc4.w));
|
| 143 |
let total = subgroupMin(acc);
|
| 144 |
-
{%
|
| 145 |
let acc = (acc4.x * acc4.y) * (acc4.z * acc4.w);
|
| 146 |
let total = subgroupMul(acc);
|
| 147 |
-
{%
|
| 148 |
let acc = (acc4.x + acc4.y) + (acc4.z + acc4.w);
|
| 149 |
let total = subgroupAdd(acc);
|
| 150 |
-
{%
|
| 151 |
if (rowValid && sgLane == 0u) {
|
| 152 |
-
{%
|
| 153 |
y[row] = total / {{ accScalar }}(params.chunkCount * 4u);
|
| 154 |
-
{%
|
| 155 |
y[row] = {{ yv }}total / f32(params.chunkCount * 4u){{ vy }};
|
| 156 |
-
{%
|
| 157 |
y[row] = {{ accScalar }}(sqrt(f32(total)));
|
| 158 |
-
{%
|
| 159 |
y[row] = {{ yv }}sqrt(total){{ vy }};
|
| 160 |
-
{%
|
| 161 |
y[row] = {{ yv }}log(total){{ vy }};
|
| 162 |
-
{%
|
| 163 |
y[row] = {{ yv }}total{{ vy }};
|
| 164 |
-
{%
|
| 165 |
}
|
| 166 |
-
{%
|
| 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 |
-
|
| 44 |
-
|
| 45 |
|
| 46 |
const WG: u32 = {{ workgroupSize }}u;
|
| 47 |
-
{%
|
|
|
|
| 48 |
{{ wgsl_minmax_identity("reduction_identity", op, scalar) }}
|
| 49 |
-
{%
|
| 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 |
-
{%
|
| 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 |
-
{%
|
| 81 |
-
{%
|
| 82 |
{{ emit_reduce("reduce_row", "subgroupMax", "total = max(total, wgPartial[i]);") }}
|
| 83 |
-
{%
|
| 84 |
{{ emit_reduce("reduce_row", "subgroupMin", "total = min(total, wgPartial[i]);") }}
|
| 85 |
-
{%
|
| 86 |
{{ emit_reduce("reduce_row", "subgroupMul", "total = total * wgPartial[i];") }}
|
| 87 |
-
{%
|
| 88 |
{{ emit_reduce("reduce_row_add", "subgroupAdd", "total = total + wgPartial[i];") }}
|
| 89 |
{{ emit_reduce("reduce_row_max", "subgroupMax", "total = max(total, wgPartial[i]);") }}
|
| 90 |
-
{%
|
| 91 |
{{ emit_reduce("reduce_row", "subgroupAdd", "total = total + wgPartial[i];") }}
|
| 92 |
-
{%
|
| 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 |
-
{%
|
| 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 |
-
{%
|
| 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 |
-
{%
|
| 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 |
-
{%
|
| 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 |
-
{%
|
| 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 |
-
{%
|
| 142 |
acc = acc + select(exp(v - rowMax), 0.0, hasPositiveInf || hasNan);
|
| 143 |
-
{%
|
| 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 |
-
{%
|
| 151 |
{% if op == "max" or op == "min" %}
|
| 152 |
let INIT: {{ scalar }} = reduction_identity();
|
| 153 |
-
{%
|
| 154 |
let INIT: f32 = 1.0;
|
| 155 |
-
{%
|
| 156 |
let INIT: f32 = 0.0;
|
| 157 |
-
{%
|
| 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 |
-
{%
|
| 163 |
acc4 = max(acc4, v);
|
| 164 |
-
{%
|
| 165 |
acc4 = min(acc4, v);
|
| 166 |
-
{%
|
| 167 |
acc4 = acc4 * v;
|
| 168 |
-
{%
|
| 169 |
acc4 = acc4 + abs(v);
|
| 170 |
-
{%
|
| 171 |
acc4 = acc4 + v * v;
|
| 172 |
-
{%
|
| 173 |
acc4 = acc4 + v;
|
| 174 |
-
{%
|
| 175 |
}
|
| 176 |
-
{%
|
| 177 |
let acc = max(max(acc4.x, acc4.y), max(acc4.z, acc4.w));
|
| 178 |
-
{%
|
| 179 |
let acc = min(min(acc4.x, acc4.y), min(acc4.z, acc4.w));
|
| 180 |
-
{%
|
| 181 |
let acc = (acc4.x * acc4.y) * (acc4.z * acc4.w);
|
| 182 |
-
{%
|
| 183 |
let acc = (acc4.x + acc4.y) + (acc4.z + acc4.w);
|
| 184 |
-
{%
|
| 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 |
-
{%
|
| 190 |
acc = max(acc, v);
|
| 191 |
-
{%
|
| 192 |
acc = min(acc, v);
|
| 193 |
-
{%
|
| 194 |
acc = acc * v;
|
| 195 |
-
{%
|
| 196 |
acc = acc + abs(v);
|
| 197 |
-
{%
|
| 198 |
acc = acc + v * v;
|
| 199 |
-
{%
|
| 200 |
acc = acc + v;
|
| 201 |
-
{%
|
| 202 |
}
|
| 203 |
-
{%
|
| 204 |
let total = reduce_row(acc, sgLid, sgId, numSg);
|
| 205 |
if (tid == 0u) {
|
| 206 |
-
{%
|
| 207 |
y[row] = {{ yv }}total / f32(params.cols){{ vy }};
|
| 208 |
-
{%
|
| 209 |
y[row] = {{ yv }}sqrt(total){{ vy }};
|
| 210 |
-
{%
|
| 211 |
y[row] = {{ yv }}log(total){{ vy }};
|
| 212 |
-
{%
|
| 213 |
y[row] = {{ yv }}total{{ vy }};
|
| 214 |
-
{%
|
| 215 |
}
|
| 216 |
-
{%
|
| 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:
|
| 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
|
| 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 |
-
{%
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|
|
@@ -86,7 +84,6 @@ fn negative_infinity() -> f32 {
|
|
| 86 |
{% elif op == "logsumexp" %}
|
| 87 |
|
| 88 |
const F32_MIN: f32 = -3.4028234663852886e38;
|
| 89 |
-
{% if not intMode %}
|
| 90 |
const F32_MAX: f32 = 3.4028234663852886e38;
|
| 91 |
|
| 92 |
fn is_nan_f32(value: f32) -> bool {
|
|
@@ -94,7 +91,6 @@ fn is_nan_f32(value: f32) -> bool {
|
|
| 94 |
return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
|
| 95 |
}
|
| 96 |
{% endif %}
|
| 97 |
-
{% endif %}
|
| 98 |
{% if indexing == "rankn" %}
|
| 99 |
|
| 100 |
fn input_offset(out_index: u32, reduce_index: u32) -> u32 {
|
|
@@ -131,7 +127,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 |
-
|
|
|
|
| 135 |
{% endif %}
|
| 136 |
{% if indexing == "multiaxis" %}
|
| 137 |
{% set mcount = namespace(value=1) %}
|
|
@@ -166,31 +163,8 @@ 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 |
-
|
| 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
|
| 178 |
-
// back to the integer output type.
|
| 179 |
-
// Integers are never NaN, so the f32 NaN-propagation path is unnecessary here.
|
| 180 |
-
if ({{ count }} == 0u) {
|
| 181 |
-
y[i] = {{ scalar }}(F32_MIN);
|
| 182 |
-
return;
|
| 183 |
-
}
|
| 184 |
-
var m = F32_MIN;
|
| 185 |
-
for (var r = 0u; r < {{ count }}; r = r + 1u) {
|
| 186 |
-
m = max(m, f32({{ at }}));
|
| 187 |
-
}
|
| 188 |
-
var acc = 0.0;
|
| 189 |
-
for (var r = 0u; r < {{ count }}; r = r + 1u) {
|
| 190 |
-
acc = acc + exp(f32({{ at }}) - m);
|
| 191 |
-
}
|
| 192 |
-
y[i] = {{ scalar }}(m + log(acc));
|
| 193 |
-
{% else %}
|
| 194 |
if ({{ count }} == 0u) {
|
| 195 |
y[i] = {{ yv }}negative_infinity(){{ vy }};
|
| 196 |
return;
|
|
@@ -220,7 +194,6 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
| 220 |
acc = acc + exp({{ at }} - m);
|
| 221 |
}
|
| 222 |
y[i] = {{ yv }}m + log(acc){{ vy }};
|
| 223 |
-
{% endif %}
|
| 224 |
{% elif op == "max" or op == "min" %}
|
| 225 |
if ({{ count }} == 0u) {
|
| 226 |
y[i] = {{ yv }}empty_value(){{ vy }};
|
|
@@ -234,11 +207,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
| 234 |
{% else %}
|
| 235 |
{% if op == "mean" %}
|
| 236 |
if ({{ count }} == 0u) {
|
| 237 |
-
{% if intMode %}
|
| 238 |
-
y[i] = {{ scalar }}(0);
|
| 239 |
-
{% else %}
|
| 240 |
y[i] = {{ yv }}0.0{{ vy }};
|
| 241 |
-
{% endif %}
|
| 242 |
return;
|
| 243 |
}
|
| 244 |
{% elif op == "logsum" %}
|
|
@@ -247,13 +216,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
| 247 |
return;
|
| 248 |
}
|
| 249 |
{% endif %}
|
| 250 |
-
{% if intMode %}
|
| 251 |
-
// Integer reduction accumulates in the output type. sum/prod/l1/sumsquare stay
|
| 252 |
-
// in integer arithmetic; l2/logsum widen for sqrt/log and truncate afterward.
|
| 253 |
-
var acc = {{ scalar }}({{ "1" if op == "prod" else "0" }});
|
| 254 |
-
{% else %}
|
| 255 |
var acc = {{ "1.0" if op == "prod" else "0.0" }};
|
| 256 |
-
{% endif %}
|
| 257 |
for (var r = 0u; r < {{ count }}; r = r + 1u) {
|
| 258 |
{% if op == "prod" %}
|
| 259 |
acc = acc * {{ at }};
|
|
@@ -267,19 +230,11 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
| 267 |
{% endif %}
|
| 268 |
}
|
| 269 |
{% if op == "l2" %}
|
| 270 |
-
{% if intMode %}
|
| 271 |
-
y[i] = {{ scalar }}(sqrt(f32(acc)));
|
| 272 |
-
{% else %}
|
| 273 |
y[i] = {{ yv }}sqrt(acc){{ vy }};
|
| 274 |
-
{% endif %}
|
| 275 |
{% elif op == "logsum" %}
|
| 276 |
y[i] = {{ yv }}log(acc){{ vy }};
|
| 277 |
{% elif op == "mean" %}
|
| 278 |
-
{% if intMode %}
|
| 279 |
-
y[i] = acc / {{ scalar }}({{ count }});
|
| 280 |
-
{% else %}
|
| 281 |
y[i] = {{ yv }}acc / f32({{ count }}){{ vy }};
|
| 282 |
-
{% endif %}
|
| 283 |
{% else %}
|
| 284 |
y[i] = {{ yv }}acc{{ vy }};
|
| 285 |
{% endif %}
|
|
|
|
| 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 intMode = false %}
|
|
|
|
|
|
|
| 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 |
|
|
|
|
| 84 |
{% elif op == "logsumexp" %}
|
| 85 |
|
| 86 |
const F32_MIN: f32 = -3.4028234663852886e38;
|
|
|
|
| 87 |
const F32_MAX: f32 = 3.4028234663852886e38;
|
| 88 |
|
| 89 |
fn is_nan_f32(value: f32) -> bool {
|
|
|
|
| 91 |
return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
|
| 92 |
}
|
| 93 |
{% endif %}
|
|
|
|
| 94 |
{% if indexing == "rankn" %}
|
| 95 |
|
| 96 |
fn input_offset(out_index: u32, reduce_index: u32) -> u32 {
|
|
|
|
| 127 |
{% if indexing == "multiaxis" %}
|
| 128 |
{% set hasReducedAxis = namespace(value=false) %}
|
| 129 |
{% for a in range(rank) %}{% if reduce[a] %}{% set hasReducedAxis.value = true %}{% endif %}{% endfor %}
|
| 130 |
+
|
| 131 |
+
{{ reduce_multi_axis_offset(hasReducedAxis.value) }}
|
| 132 |
{% endif %}
|
| 133 |
{% if indexing == "multiaxis" %}
|
| 134 |
{% set mcount = namespace(value=1) %}
|
|
|
|
| 163 |
|
| 164 |
@compute @workgroup_size({{ reduceWorkgroupSize }})
|
| 165 |
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 166 |
+
{{ flat_index_2d(reduceWorkgroupSize, bound="params.outCount") }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
{% if op == "logsumexp" %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
if ({{ count }} == 0u) {
|
| 169 |
y[i] = {{ yv }}negative_infinity(){{ vy }};
|
| 170 |
return;
|
|
|
|
| 194 |
acc = acc + exp({{ at }} - m);
|
| 195 |
}
|
| 196 |
y[i] = {{ yv }}m + log(acc){{ vy }};
|
|
|
|
| 197 |
{% elif op == "max" or op == "min" %}
|
| 198 |
if ({{ count }} == 0u) {
|
| 199 |
y[i] = {{ yv }}empty_value(){{ vy }};
|
|
|
|
| 207 |
{% else %}
|
| 208 |
{% if op == "mean" %}
|
| 209 |
if ({{ count }} == 0u) {
|
|
|
|
|
|
|
|
|
|
| 210 |
y[i] = {{ yv }}0.0{{ vy }};
|
|
|
|
| 211 |
return;
|
| 212 |
}
|
| 213 |
{% elif op == "logsum" %}
|
|
|
|
| 216 |
return;
|
| 217 |
}
|
| 218 |
{% endif %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
var acc = {{ "1.0" if op == "prod" else "0.0" }};
|
|
|
|
| 220 |
for (var r = 0u; r < {{ count }}; r = r + 1u) {
|
| 221 |
{% if op == "prod" %}
|
| 222 |
acc = acc * {{ at }};
|
|
|
|
| 230 |
{% endif %}
|
| 231 |
}
|
| 232 |
{% if op == "l2" %}
|
|
|
|
|
|
|
|
|
|
| 233 |
y[i] = {{ yv }}sqrt(acc){{ vy }};
|
|
|
|
| 234 |
{% elif op == "logsum" %}
|
| 235 |
y[i] = {{ yv }}log(acc){{ vy }};
|
| 236 |
{% elif op == "mean" %}
|
|
|
|
|
|
|
|
|
|
| 237 |
y[i] = {{ yv }}acc / f32({{ count }}){{ vy }};
|
|
|
|
| 238 |
{% else %}
|
| 239 |
y[i] = {{ yv }}acc{{ vy }};
|
| 240 |
{% endif %}
|
build/webgpu/test.json
CHANGED
|
@@ -1107,7 +1107,7 @@
|
|
| 1107 |
{
|
| 1108 |
"name": "ort_int8_axis1_extrema",
|
| 1109 |
"provenance": {
|
| 1110 |
-
"source": "ONNX Runtime
|
| 1111 |
"notes": "Covers the signed narrow-integer reduction route."
|
| 1112 |
},
|
| 1113 |
"attrs": { "axes": [1], "keepdims": 0 },
|
|
@@ -1121,7 +1121,7 @@
|
|
| 1121 |
{
|
| 1122 |
"name": "ort_uint8_axis0_extrema",
|
| 1123 |
"provenance": {
|
| 1124 |
-
"source": "ONNX Runtime
|
| 1125 |
"notes": "Covers the unsigned narrow-integer reduction route."
|
| 1126 |
},
|
| 1127 |
"attrs": { "axes": [0], "keepdims": 0 },
|
|
@@ -1135,7 +1135,7 @@
|
|
| 1135 |
{
|
| 1136 |
"name": "ort_int8_empty_axis1_highest_identity",
|
| 1137 |
"provenance": {
|
| 1138 |
-
"source": "ONNX Runtime
|
| 1139 |
"notes": "An empty INT8 minimum is the logical dtype maximum (127), not the physical i32 storage maximum."
|
| 1140 |
},
|
| 1141 |
"attrs": { "axes": [1], "keepdims": 1 },
|
|
@@ -1152,7 +1152,7 @@
|
|
| 1152 |
{
|
| 1153 |
"name": "ort_uint8_empty_axis1_highest_identity",
|
| 1154 |
"provenance": {
|
| 1155 |
-
"source": "ONNX Runtime
|
| 1156 |
"notes": "An empty UINT8 minimum is the logical dtype maximum (255), not the physical u32 storage maximum."
|
| 1157 |
},
|
| 1158 |
"attrs": { "axes": [1], "keepdims": 1 },
|
|
|
|
| 1107 |
{
|
| 1108 |
"name": "ort_int8_axis1_extrema",
|
| 1109 |
"provenance": {
|
| 1110 |
+
"source": "ONNX Runtime's CPU provider",
|
| 1111 |
"notes": "Covers the signed narrow-integer reduction route."
|
| 1112 |
},
|
| 1113 |
"attrs": { "axes": [1], "keepdims": 0 },
|
|
|
|
| 1121 |
{
|
| 1122 |
"name": "ort_uint8_axis0_extrema",
|
| 1123 |
"provenance": {
|
| 1124 |
+
"source": "ONNX Runtime's CPU provider",
|
| 1125 |
"notes": "Covers the unsigned narrow-integer reduction route."
|
| 1126 |
},
|
| 1127 |
"attrs": { "axes": [0], "keepdims": 0 },
|
|
|
|
| 1135 |
{
|
| 1136 |
"name": "ort_int8_empty_axis1_highest_identity",
|
| 1137 |
"provenance": {
|
| 1138 |
+
"source": "ONNX Runtime's CPU provider",
|
| 1139 |
"notes": "An empty INT8 minimum is the logical dtype maximum (127), not the physical i32 storage maximum."
|
| 1140 |
},
|
| 1141 |
"attrs": { "axes": [1], "keepdims": 1 },
|
|
|
|
| 1152 |
{
|
| 1153 |
"name": "ort_uint8_empty_axis1_highest_identity",
|
| 1154 |
"provenance": {
|
| 1155 |
+
"source": "ONNX Runtime's CPU provider",
|
| 1156 |
"notes": "An empty UINT8 minimum is the logical dtype maximum (255), not the physical u32 storage maximum."
|
| 1157 |
},
|
| 1158 |
"attrs": { "axes": [1], "keepdims": 1 },
|