sync 2e7068faf55e
Browse files- README.md +72 -0
- build/webgpu/bench.json +151 -0
- build/webgpu/manifest.json +571 -0
- build/webgpu/metadata.json +26 -0
- build/webgpu/softmax-longrow-normalize.wgsl.jinja +66 -0
- build/webgpu/softmax-longrow-stats.wgsl.jinja +213 -0
- build/webgpu/softmax-normalize.wgsl.jinja +123 -0
- build/webgpu/softmax-online-packed-rows.wgsl.jinja +131 -0
- build/webgpu/softmax-online.wgsl.jinja +252 -0
- build/webgpu/softmax-row-stage-strided-vec4.wgsl.jinja +138 -0
- build/webgpu/softmax-row-stage.wgsl.jinja +165 -0
- build/webgpu/softmax-strided-online-lane.wgsl.jinja +116 -0
- build/webgpu/softmax-strided-packed4-tail.wgsl.jinja +176 -0
- build/webgpu/test.json +720 -0
README.md
CHANGED
|
@@ -1,3 +1,75 @@
|
|
| 1 |
---
|
|
|
|
| 2 |
license: apache-2.0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
library_name: kernels
|
| 3 |
license: apache-2.0
|
| 4 |
+
tags:
|
| 5 |
+
- kernel
|
| 6 |
+
- webgpu
|
| 7 |
+
- wgsl
|
| 8 |
---
|
| 9 |
+
# ai.onnx.LogSoftmax
|
| 10 |
+
|
| 11 |
+
`ai.onnx` · standard ONNX operator · ONNX opset ≥ 13
|
| 12 |
+
|
| 13 |
+
## Description
|
| 14 |
+
|
| 15 |
+
Computes `log(softmax(input, axis))` along a single axis using a numerically stable shifted reduction. The output has the same shape as the input.
|
| 16 |
+
|
| 17 |
+
See the [ONNX `LogSoftmax` spec](https://onnx.ai/onnx/operators/onnx__LogSoftmax.html) for the reference semantics.
|
| 18 |
+
|
| 19 |
+
## Inputs
|
| 20 |
+
|
| 21 |
+
| Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
|
| 22 |
+
| --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
+
| `input` | `x` | `T` | — | — | The input tensor of rank >= 1. | required |
|
| 24 |
+
|
| 25 |
+
## Outputs
|
| 26 |
+
|
| 27 |
+
| Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
|
| 28 |
+
| --- | --- | --- | --- | --- | --- | --- |
|
| 29 |
+
| `output` | `y` | `T` | same as `input` | same as `input` | The log-softmax values; same shape as the input. | required |
|
| 30 |
+
|
| 31 |
+
## Attributes
|
| 32 |
+
|
| 33 |
+
Default values (overridable per request):
|
| 34 |
+
|
| 35 |
+
| Attribute | Default | Description |
|
| 36 |
+
| --- | --- | --- |
|
| 37 |
+
| `axis` | `-1` | The axis along which log-softmax is computed. Negative values count from the end; the default `-1` operates over the last dimension. Accepted range is `[-r, r-1]` where `r` is the input rank. |
|
| 38 |
+
|
| 39 |
+
## Type constraints
|
| 40 |
+
|
| 41 |
+
| Variable | Allowed dtypes |
|
| 42 |
+
| --- | --- |
|
| 43 |
+
| `T` | `float32`, `float16` |
|
| 44 |
+
|
| 45 |
+
## Files
|
| 46 |
+
|
| 47 |
+
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
|
| 48 |
+
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 49 |
+
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 50 |
+
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
| 51 |
+
- [`softmax-longrow-normalize.wgsl.jinja`](build/webgpu/softmax-longrow-normalize.wgsl.jinja)
|
| 52 |
+
- [`softmax-longrow-stats.wgsl.jinja`](build/webgpu/softmax-longrow-stats.wgsl.jinja)
|
| 53 |
+
- [`softmax-normalize.wgsl.jinja`](build/webgpu/softmax-normalize.wgsl.jinja)
|
| 54 |
+
- [`softmax-online-packed-rows.wgsl.jinja`](build/webgpu/softmax-online-packed-rows.wgsl.jinja)
|
| 55 |
+
- [`softmax-online.wgsl.jinja`](build/webgpu/softmax-online.wgsl.jinja)
|
| 56 |
+
- [`softmax-row-stage-strided-vec4.wgsl.jinja`](build/webgpu/softmax-row-stage-strided-vec4.wgsl.jinja)
|
| 57 |
+
- [`softmax-row-stage.wgsl.jinja`](build/webgpu/softmax-row-stage.wgsl.jinja)
|
| 58 |
+
- [`softmax-strided-online-lane.wgsl.jinja`](build/webgpu/softmax-strided-online-lane.wgsl.jinja)
|
| 59 |
+
- [`softmax-strided-packed4-tail.wgsl.jinja`](build/webgpu/softmax-strided-packed4-tail.wgsl.jinja)
|
| 60 |
+
|
| 61 |
+
## Use with `@huggingface/kernels`
|
| 62 |
+
|
| 63 |
+
The loader derives every required output's shape and logical dtype from the manifest contract and this call.
|
| 64 |
+
It then allocates the result tensors automatically.
|
| 65 |
+
|
| 66 |
+
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
| 67 |
+
|
| 68 |
+
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 69 |
+
|
| 70 |
+
```js
|
| 71 |
+
import { getKernel } from "@huggingface/kernels";
|
| 72 |
+
|
| 73 |
+
const kernel = await getKernel("webgpu-kernels/ai.onnx.LogSoftmax", { version: 1 });
|
| 74 |
+
const { y } = await kernel({ x: { data: xData, shape: [1, 3] } });
|
| 75 |
+
```
|
build/webgpu/bench.json
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"op": "ai.onnx.LogSoftmax",
|
| 3 |
+
"tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
|
| 4 |
+
"cases": [
|
| 5 |
+
{
|
| 6 |
+
"name": "attention_scores",
|
| 7 |
+
"inputs": { "x": { "dtype": "float32", "shape": [32, 128] } },
|
| 8 |
+
"outputs": { "y": { "dtype": "float32", "shape": [32, 128] } }
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"name": "logsoftmax-f32-1024x2048",
|
| 12 |
+
"preset": "smoke",
|
| 13 |
+
"vars": { "dtype": "float32", "rows": 1024, "cols": 2048 },
|
| 14 |
+
"attrs": { "axis": 1 },
|
| 15 |
+
"inputs": { "x": { "shape": [1024, 2048], "dtype": "float32", "dist": "normal", "seed": 211, "scale": 2 } },
|
| 16 |
+
"outputs": { "y": { "shape": [1024, 2048], "dtype": "float32" } },
|
| 17 |
+
"bench": {
|
| 18 |
+
"primary": true,
|
| 19 |
+
"metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }]
|
| 20 |
+
}
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"name": "logsoftmax-f32-16384x512",
|
| 24 |
+
"preset": "stress",
|
| 25 |
+
"vars": { "dtype": "float32", "rows": 16384, "cols": 512 },
|
| 26 |
+
"attrs": { "axis": 1 },
|
| 27 |
+
"inputs": { "x": { "shape": [16384, 512], "dtype": "float32", "dist": "normal", "seed": 212, "scale": 2 } },
|
| 28 |
+
"outputs": { "y": { "shape": [16384, 512], "dtype": "float32" } },
|
| 29 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"name": "logsoftmax-logits-1x262144",
|
| 33 |
+
"preset": "model",
|
| 34 |
+
"vars": { "dtype": "float32", "rows": 1, "cols": 262144 },
|
| 35 |
+
"attrs": { "axis": 1 },
|
| 36 |
+
"inputs": { "x": { "shape": [1, 262144], "dtype": "float32", "dist": "normal", "seed": 213, "scale": 4 } },
|
| 37 |
+
"outputs": { "y": { "shape": [1, 262144], "dtype": "float32" } },
|
| 38 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"name": "logsoftmax-f32-strided-axis1-64x1024x64",
|
| 42 |
+
"preset": "stress",
|
| 43 |
+
"vars": { "dtype": "float32", "rows": 64, "mid": 1024, "inner": 64 },
|
| 44 |
+
"attrs": { "axis": 1 },
|
| 45 |
+
"inputs": { "x": { "shape": [64, 1024, 64], "dtype": "float32", "dist": "normal", "seed": 311, "scale": 2 } },
|
| 46 |
+
"outputs": { "y": { "shape": [64, 1024, 64], "dtype": "float32" } },
|
| 47 |
+
"bench": {
|
| 48 |
+
"metrics": [{ "type": "bandwidth", "value": "args.rows * args.mid * args.inner * dtypeBytes(args.dtype) * 2" }]
|
| 49 |
+
}
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"name": "logsoftmax-f16-1024x2048-axis1",
|
| 53 |
+
"preset": "stress",
|
| 54 |
+
"vars": { "dtype": "float16", "rows": 1024, "cols": 2048 },
|
| 55 |
+
"attrs": { "axis": 1 },
|
| 56 |
+
"inputs": { "x": { "shape": [1024, 2048], "dtype": "float16", "dist": "normal", "seed": 312, "scale": 2 } },
|
| 57 |
+
"outputs": { "y": { "shape": [1024, 2048], "dtype": "float16" } },
|
| 58 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"name": "logsoftmax-f32-align-cliff-1024x2046",
|
| 62 |
+
"preset": "stress",
|
| 63 |
+
"vars": { "dtype": "float32", "rows": 1024, "cols": 2046 },
|
| 64 |
+
"attrs": { "axis": 1 },
|
| 65 |
+
"inputs": { "x": { "shape": [1024, 2046], "dtype": "float32", "dist": "normal", "seed": 313, "scale": 2 } },
|
| 66 |
+
"outputs": { "y": { "shape": [1024, 2046], "dtype": "float32" } },
|
| 67 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"name": "logsoftmax-f32-tallwide-70000x2048",
|
| 71 |
+
"preset": "model",
|
| 72 |
+
"vars": { "dtype": "float32", "rows": 70000, "cols": 2048 },
|
| 73 |
+
"attrs": { "axis": 1 },
|
| 74 |
+
"inputs": { "x": { "shape": [70000, 2048], "dtype": "float32", "dist": "normal", "seed": 314, "scale": 2 } },
|
| 75 |
+
"outputs": { "y": { "shape": [70000, 2048], "dtype": "float32" } },
|
| 76 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
|
| 77 |
+
},
|
| 78 |
+
{
|
| 79 |
+
"name": "logsoftmax-f32-nosg-3pass-occupancy-131072x32",
|
| 80 |
+
"preset": "stress",
|
| 81 |
+
"vars": { "dtype": "float32", "rows": 131072, "cols": 32 },
|
| 82 |
+
"attrs": { "axis": 1 },
|
| 83 |
+
"inputs": { "x": { "shape": [131072, 32], "dtype": "float32", "dist": "normal", "seed": 411, "scale": 2 } },
|
| 84 |
+
"outputs": { "y": { "shape": [131072, 32], "dtype": "float32" } },
|
| 85 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
|
| 86 |
+
},
|
| 87 |
+
{
|
| 88 |
+
"name": "logsoftmax-f32-strided-axis1-64x2048x130-packed4-control",
|
| 89 |
+
"preset": "stress",
|
| 90 |
+
"provenance": {
|
| 91 |
+
"source": "strided-axis selector crossover",
|
| 92 |
+
"notes": "Below-crossover control for the large-inner tail pathology. At 8,320 independent strided rows the cooperative packed-4 reducer still beats the serial-lane capacity path."
|
| 93 |
+
},
|
| 94 |
+
"vars": { "dtype": "float32", "outer": 64, "axisDim": 2048, "inner": 130 },
|
| 95 |
+
"attrs": { "axis": 1 },
|
| 96 |
+
"inputs": { "x": { "shape": [64, 2048, 130], "dtype": "float32", "dist": "normal", "seed": 414, "scale": 2 } },
|
| 97 |
+
"outputs": { "y": { "shape": [64, 2048, 130], "dtype": "float32", "dist": "empty" } },
|
| 98 |
+
"bench": {
|
| 99 |
+
"metrics": [
|
| 100 |
+
{ "type": "bandwidth", "value": "args.outer * args.axisDim * args.inner * dtypeBytes(args.dtype) * 2" }
|
| 101 |
+
]
|
| 102 |
+
}
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"name": "logsoftmax-f32-strided-axis1-96x2048x130-crossover",
|
| 106 |
+
"preset": "stress",
|
| 107 |
+
"provenance": {
|
| 108 |
+
"source": "strided-axis selector crossover",
|
| 109 |
+
"notes": "Above-crossover probe for the large-inner tail pathology. At 12,480 independent strided rows the serial-lane capacity path overtakes the cooperative packed-4 reducer."
|
| 110 |
+
},
|
| 111 |
+
"vars": { "dtype": "float32", "outer": 96, "axisDim": 2048, "inner": 130 },
|
| 112 |
+
"attrs": { "axis": 1 },
|
| 113 |
+
"inputs": { "x": { "shape": [96, 2048, 130], "dtype": "float32", "dist": "normal", "seed": 413, "scale": 2 } },
|
| 114 |
+
"outputs": { "y": { "shape": [96, 2048, 130], "dtype": "float32", "dist": "empty" } },
|
| 115 |
+
"bench": {
|
| 116 |
+
"metrics": [
|
| 117 |
+
{ "type": "bandwidth", "value": "args.outer * args.axisDim * args.inner * dtypeBytes(args.dtype) * 2" }
|
| 118 |
+
]
|
| 119 |
+
}
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"name": "logsoftmax-f32-strided-axis1-120x2048x130-large-inner",
|
| 123 |
+
"preset": "stress",
|
| 124 |
+
"provenance": {
|
| 125 |
+
"source": "authored for variant coverage",
|
| 126 |
+
"notes": "Inner=130 exercises the two-element tail of the scalar-aligned packed-4 online reducer while sharing one workgroup across four adjacent rows."
|
| 127 |
+
},
|
| 128 |
+
"vars": { "dtype": "float32", "outer": 120, "axisDim": 2048, "inner": 130 },
|
| 129 |
+
"attrs": { "axis": 1 },
|
| 130 |
+
"inputs": { "x": { "shape": [120, 2048, 130], "dtype": "float32", "dist": "normal", "seed": 412, "scale": 2 } },
|
| 131 |
+
"outputs": { "y": { "shape": [120, 2048, 130], "dtype": "float32", "dist": "empty" } },
|
| 132 |
+
"bench": {
|
| 133 |
+
"metrics": [
|
| 134 |
+
{ "type": "bandwidth", "value": "args.outer * args.axisDim * args.inner * dtypeBytes(args.dtype) * 2" }
|
| 135 |
+
]
|
| 136 |
+
}
|
| 137 |
+
},
|
| 138 |
+
{
|
| 139 |
+
"name": "logsoftmax-f32-8192x512-midwidth",
|
| 140 |
+
"provenance": {
|
| 141 |
+
"notes": "Covers the 512-column midpoint between the 128- and 2048-column cases and probes the width term in online_wg_vec4's selector."
|
| 142 |
+
},
|
| 143 |
+
"preset": "smoke",
|
| 144 |
+
"vars": { "dtype": "float32", "rows": 8192, "cols": 512 },
|
| 145 |
+
"attrs": { "axis": 1 },
|
| 146 |
+
"inputs": { "x": { "shape": [8192, 512], "dtype": "float32", "dist": "normal", "seed": 215, "scale": 2 } },
|
| 147 |
+
"outputs": { "y": { "shape": [8192, 512], "dtype": "float32" } },
|
| 148 |
+
"bench": { "metrics": [{ "type": "bandwidth", "value": "args.rows * args.cols * dtypeBytes(args.dtype) * 2" }] }
|
| 149 |
+
}
|
| 150 |
+
]
|
| 151 |
+
}
|
build/webgpu/manifest.json
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"domain": "ai.onnx",
|
| 3 |
+
"name": "LogSoftmax",
|
| 4 |
+
"sinceVersion": 13,
|
| 5 |
+
"description": "Computes `log(softmax(input, axis))` along a single axis using a numerically stable shifted reduction. The output has the same shape as the input.",
|
| 6 |
+
"inputs": [{ "role": "input", "dtype": "T", "description": "The input tensor of rank >= 1." }],
|
| 7 |
+
"outputs": [
|
| 8 |
+
{
|
| 9 |
+
"role": "output",
|
| 10 |
+
"dtype": "T",
|
| 11 |
+
"rank": "ranks.input",
|
| 12 |
+
"description": "The log-softmax values; same shape as the input.",
|
| 13 |
+
"shape": "shapes.input"
|
| 14 |
+
}
|
| 15 |
+
],
|
| 16 |
+
"attributes": { "axis": -1 },
|
| 17 |
+
"attributeDescriptions": {
|
| 18 |
+
"axis": "The axis along which log-softmax is computed. Negative values count from the end; the default `-1` operates over the last dimension. Accepted range is `[-r, r-1]` where `r` is the input rank."
|
| 19 |
+
},
|
| 20 |
+
"typeConstraints": { "T": ["float32", "float16"] },
|
| 21 |
+
"args": {
|
| 22 |
+
"x": { "kind": "tensor", "semantic": "input", "role": "input" },
|
| 23 |
+
"y": { "kind": "tensor", "semantic": "output", "role": "output" }
|
| 24 |
+
},
|
| 25 |
+
"tunables": {
|
| 26 |
+
"WORKGROUP_SIZE": 256,
|
| 27 |
+
"BLOCK_COLS": 1024,
|
| 28 |
+
"LONGROW_SPLIT_MIN_COLS": 65536,
|
| 29 |
+
"STRIDED_CAPACITY_MIN_ROWS": 12288
|
| 30 |
+
},
|
| 31 |
+
"derive": {
|
| 32 |
+
"storageBufferLimit": "min(device.limits.maxStorageBufferBindingSize, device.limits.maxBufferSize)",
|
| 33 |
+
"axis": "attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x",
|
| 34 |
+
"lastCols": "dim(shapes.x, ranks.x - 1)",
|
| 35 |
+
"lastRows": "rows(shapes.x, ranks.x - 1)",
|
| 36 |
+
"axisInner": "inner(shapes.x, axis)",
|
| 37 |
+
"axisOuter": "outer(shapes.x, axis)",
|
| 38 |
+
"axisRows": "axisOuter * axisInner",
|
| 39 |
+
"longrowBlocks": "ceilDiv(lastCols, tunables.BLOCK_COLS)",
|
| 40 |
+
"lastRowScratchFits": "4 * lastRows <= storageBufferLimit",
|
| 41 |
+
"axisRowScratchFits": "4 * axisRows <= storageBufferLimit",
|
| 42 |
+
"longrowBlockScratchFits": "4 * lastRows * longrowBlocks <= storageBufferLimit",
|
| 43 |
+
"lastDispatchFits": "ceilDiv(lastRows, device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension",
|
| 44 |
+
"axisDispatchFits": "ceilDiv(axisRows, device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension",
|
| 45 |
+
"axisThreadDispatchFits": "ceilDiv(ceilDiv(axisRows, tunables.WORKGROUP_SIZE), device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension",
|
| 46 |
+
"lastAxisOk": "numel(shapes.x) == numel(shapes.y) and ranks.x >= 1 and (attrs.axis == -1 or attrs.axis == ranks.x - 1) and f16Ok(dtypes.T)",
|
| 47 |
+
"stridedAxisOk": "numel(shapes.x) == numel(shapes.y) and ranks.x >= 2 and attrs.axis + ranks.x >= 0 and attrs.axis < ranks.x and not (attrs.axis == -1 or attrs.axis == ranks.x - 1)"
|
| 48 |
+
},
|
| 49 |
+
"bindingSets": {
|
| 50 |
+
"longrowBlockStats": [
|
| 51 |
+
{ "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 52 |
+
{ "name": "blockMax", "semantic": "blockMax", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 53 |
+
{ "name": "blockSum", "semantic": "blockSum", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 54 |
+
{
|
| 55 |
+
"name": "params",
|
| 56 |
+
"semantic": "kernel.params",
|
| 57 |
+
"buffer": { "type": "uniform" },
|
| 58 |
+
"struct": {
|
| 59 |
+
"name": "Params",
|
| 60 |
+
"fields": [
|
| 61 |
+
{ "name": "cols", "type": "u32", "value": "lastCols" },
|
| 62 |
+
{ "name": "blocks", "type": "u32", "value": "ceilDiv(lastCols, tunables.BLOCK_COLS)" }
|
| 63 |
+
]
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"longrowRowStats": [
|
| 68 |
+
{ "name": "blockMax", "semantic": "blockMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 69 |
+
{ "name": "blockSum", "semantic": "blockSum", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 70 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 71 |
+
{ "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 72 |
+
{
|
| 73 |
+
"name": "params",
|
| 74 |
+
"semantic": "kernel.params",
|
| 75 |
+
"buffer": { "type": "uniform" },
|
| 76 |
+
"struct": {
|
| 77 |
+
"name": "Params",
|
| 78 |
+
"fields": [{ "name": "blocks", "type": "u32", "value": "ceilDiv(lastCols, tunables.BLOCK_COLS)" }]
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
],
|
| 82 |
+
"longrowNormalize": [
|
| 83 |
+
{ "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 84 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 85 |
+
{ "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 86 |
+
{ "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$T" },
|
| 87 |
+
{
|
| 88 |
+
"name": "params",
|
| 89 |
+
"semantic": "kernel.params",
|
| 90 |
+
"buffer": { "type": "uniform" },
|
| 91 |
+
"struct": { "name": "Params", "fields": [{ "name": "cols", "type": "u32", "value": "lastCols" }] }
|
| 92 |
+
}
|
| 93 |
+
],
|
| 94 |
+
"online_vec4": [
|
| 95 |
+
{
|
| 96 |
+
"name": "x",
|
| 97 |
+
"arg": "x",
|
| 98 |
+
"semantic": "X",
|
| 99 |
+
"buffer": { "type": "read-only-storage" },
|
| 100 |
+
"elementType": "$vectorScalar"
|
| 101 |
+
},
|
| 102 |
+
{ "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$vectorScalar" },
|
| 103 |
+
{
|
| 104 |
+
"name": "params",
|
| 105 |
+
"semantic": "kernel.params",
|
| 106 |
+
"buffer": { "type": "uniform" },
|
| 107 |
+
"struct": {
|
| 108 |
+
"name": "Params",
|
| 109 |
+
"fields": [
|
| 110 |
+
{ "name": "rows", "type": "u32", "value": "lastRows" },
|
| 111 |
+
{ "name": "vecCols", "type": "u32", "value": "lastCols / 4" }
|
| 112 |
+
]
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
],
|
| 116 |
+
"online_scalar": [
|
| 117 |
+
{
|
| 118 |
+
"name": "x",
|
| 119 |
+
"arg": "x",
|
| 120 |
+
"semantic": "input",
|
| 121 |
+
"buffer": { "type": "read-only-storage" },
|
| 122 |
+
"elementType": "$scalar"
|
| 123 |
+
},
|
| 124 |
+
{ "name": "y", "arg": "y", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "$scalar" },
|
| 125 |
+
{
|
| 126 |
+
"name": "params",
|
| 127 |
+
"semantic": "kernel.params",
|
| 128 |
+
"buffer": { "type": "uniform" },
|
| 129 |
+
"struct": {
|
| 130 |
+
"name": "Params",
|
| 131 |
+
"fields": [
|
| 132 |
+
{ "name": "rows", "type": "u32", "value": "lastRows" },
|
| 133 |
+
{ "name": "cols", "type": "u32", "value": "lastCols" }
|
| 134 |
+
]
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
],
|
| 138 |
+
"stableReduceMax": [
|
| 139 |
+
{ "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 140 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 141 |
+
{
|
| 142 |
+
"name": "params",
|
| 143 |
+
"semantic": "kernel.params",
|
| 144 |
+
"buffer": { "type": "uniform" },
|
| 145 |
+
"struct": {
|
| 146 |
+
"name": "Params",
|
| 147 |
+
"fields": [
|
| 148 |
+
{ "name": "rows", "type": "u32", "value": "lastRows" },
|
| 149 |
+
{ "name": "cols", "type": "u32", "value": "lastCols" }
|
| 150 |
+
]
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
],
|
| 154 |
+
"stableExpSum": [
|
| 155 |
+
{ "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 156 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 157 |
+
{ "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$T" },
|
| 158 |
+
{ "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 159 |
+
{
|
| 160 |
+
"name": "params",
|
| 161 |
+
"semantic": "kernel.params",
|
| 162 |
+
"buffer": { "type": "uniform" },
|
| 163 |
+
"struct": {
|
| 164 |
+
"name": "Params",
|
| 165 |
+
"fields": [
|
| 166 |
+
{ "name": "rows", "type": "u32", "value": "lastRows" },
|
| 167 |
+
{ "name": "cols", "type": "u32", "value": "lastCols" }
|
| 168 |
+
]
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
],
|
| 172 |
+
"normalize": [
|
| 173 |
+
{ "name": "x", "arg": "x", "semantic": "input", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 174 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 175 |
+
{ "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 176 |
+
{ "name": "y", "arg": "y", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "$T" },
|
| 177 |
+
{
|
| 178 |
+
"name": "params",
|
| 179 |
+
"semantic": "kernel.params",
|
| 180 |
+
"buffer": { "type": "uniform" },
|
| 181 |
+
"struct": { "name": "Params", "fields": [{ "name": "count", "type": "u32", "value": "numel(shapes.x)" }] }
|
| 182 |
+
}
|
| 183 |
+
],
|
| 184 |
+
"stableNormalize": [
|
| 185 |
+
{ "name": "x", "arg": "x", "semantic": "input", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 186 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 187 |
+
{ "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 188 |
+
{ "name": "y", "arg": "y", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "$T" },
|
| 189 |
+
{
|
| 190 |
+
"name": "params",
|
| 191 |
+
"semantic": "kernel.params",
|
| 192 |
+
"buffer": { "type": "uniform" },
|
| 193 |
+
"struct": {
|
| 194 |
+
"name": "Params",
|
| 195 |
+
"fields": [
|
| 196 |
+
{ "name": "count", "type": "u32", "value": "numel(shapes.x)" },
|
| 197 |
+
{ "name": "cols", "type": "u32", "value": "lastCols" }
|
| 198 |
+
]
|
| 199 |
+
}
|
| 200 |
+
}
|
| 201 |
+
],
|
| 202 |
+
"stridedScalarReduce": [
|
| 203 |
+
{ "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 204 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "storage" }, "elementType": "f32" }
|
| 205 |
+
],
|
| 206 |
+
"stridedScalarExpSum": [
|
| 207 |
+
{ "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 208 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 209 |
+
{ "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$T" },
|
| 210 |
+
{ "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "storage" }, "elementType": "f32" }
|
| 211 |
+
],
|
| 212 |
+
"stridedOnline": [
|
| 213 |
+
{ "name": "x", "arg": "x", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 214 |
+
{ "name": "y", "arg": "y", "buffer": { "type": "storage" }, "elementType": "$T" }
|
| 215 |
+
]
|
| 216 |
+
},
|
| 217 |
+
"variants": [
|
| 218 |
+
{
|
| 219 |
+
"id": "longrow_split",
|
| 220 |
+
"priority": 30,
|
| 221 |
+
"when": ["lastCols >= tunables.LONGROW_SPLIT_MIN_COLS", "rows(shapes.x, ranks.x - 1) <= device.limits.maxComputeWorkgroupsPerDimension", "ceilDiv(dim(shapes.x, ranks.x - 1), tunables.BLOCK_COLS) <= device.limits.maxComputeWorkgroupsPerDimension", "lastRowScratchFits", "longrowBlockScratchFits", "lastAxisOk"],
|
| 222 |
+
"intermediates": [
|
| 223 |
+
{
|
| 224 |
+
"id": "blockMax",
|
| 225 |
+
"dtype": "float32",
|
| 226 |
+
"shape": "[rows(shapes.x, ranks.x - 1) * ceilDiv(dim(shapes.x, ranks.x - 1), tunables.BLOCK_COLS)]"
|
| 227 |
+
},
|
| 228 |
+
{
|
| 229 |
+
"id": "blockSum",
|
| 230 |
+
"dtype": "float32",
|
| 231 |
+
"shape": "[rows(shapes.x, ranks.x - 1) * ceilDiv(dim(shapes.x, ranks.x - 1), tunables.BLOCK_COLS)]"
|
| 232 |
+
},
|
| 233 |
+
{ "id": "rowMax", "dtype": "float32", "shape": "[rows(shapes.x, ranks.x - 1)]" },
|
| 234 |
+
{ "id": "rowSum", "dtype": "float32", "shape": "[rows(shapes.x, ranks.x - 1)]" }
|
| 235 |
+
],
|
| 236 |
+
"passes": [
|
| 237 |
+
{
|
| 238 |
+
"id": "block_stats",
|
| 239 |
+
"name": "LogSoftmax.LongRowBlockStats",
|
| 240 |
+
"source": { "shader": "softmax-longrow-stats.wgsl.jinja", "inputs": { "stage": "\"block\"" } },
|
| 241 |
+
"bindings": "longrowBlockStats",
|
| 242 |
+
"dispatch": {
|
| 243 |
+
"x": "ceilDiv(dim(shapes.x, ranks.x - 1), tunables.BLOCK_COLS)",
|
| 244 |
+
"y": "rows(shapes.x, ranks.x - 1)"
|
| 245 |
+
}
|
| 246 |
+
},
|
| 247 |
+
{
|
| 248 |
+
"id": "row_stats",
|
| 249 |
+
"name": "LogSoftmax.LongRowStats",
|
| 250 |
+
"source": { "shader": "softmax-longrow-stats.wgsl.jinja", "inputs": { "stage": "\"row\"" } },
|
| 251 |
+
"bindings": "longrowRowStats",
|
| 252 |
+
"dispatch": { "x": "rows(shapes.x, ranks.x - 1)" }
|
| 253 |
+
},
|
| 254 |
+
{
|
| 255 |
+
"id": "normalize",
|
| 256 |
+
"name": "LogSoftmax.LongRowNormalize",
|
| 257 |
+
"source": { "shader": "softmax-longrow-normalize.wgsl.jinja", "inputs": { "op": "\"logsoftmax\"" } },
|
| 258 |
+
"bindings": "longrowNormalize",
|
| 259 |
+
"dispatch": {
|
| 260 |
+
"x": "ceilDiv(dim(shapes.x, ranks.x - 1), tunables.BLOCK_COLS)",
|
| 261 |
+
"y": "rows(shapes.x, ranks.x - 1)"
|
| 262 |
+
}
|
| 263 |
+
}
|
| 264 |
+
]
|
| 265 |
+
},
|
| 266 |
+
{
|
| 267 |
+
"id": "online_wg_vec4",
|
| 268 |
+
"priority": 16,
|
| 269 |
+
"when": ["(device.features.has(\"subgroups\") and dim(shapes.x, ranks.x - 1) >= 1024) or (not device.features.has(\"subgroups\") and dim(shapes.x, ranks.x - 1) > 32)", "dim(shapes.x, ranks.x - 1) % 4 == 0", "ceilDiv(rows(shapes.x, ranks.x - 1), device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension", "lastAxisOk"],
|
| 270 |
+
"constants": {
|
| 271 |
+
"combineSubgroups": "device.features.has(\"subgroups\") and dim(shapes.x, ranks.x - 1) >= 1024",
|
| 272 |
+
"scalar": "dtypes.T",
|
| 273 |
+
"vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
|
| 274 |
+
"usesF16": "dtypes.T == \"f16\""
|
| 275 |
+
},
|
| 276 |
+
"passes": [
|
| 277 |
+
{
|
| 278 |
+
"id": "main",
|
| 279 |
+
"name": "LogSoftmax.OnlineWorkgroupVec4",
|
| 280 |
+
"source": { "shader": "softmax-online.wgsl.jinja", "inputs": { "op": "\"logsoftmax\"", "useVec4": "true" } },
|
| 281 |
+
"bindings": "online_vec4",
|
| 282 |
+
"dispatch": { "workgroups": "rows(shapes.x, ranks.x - 1)" }
|
| 283 |
+
}
|
| 284 |
+
]
|
| 285 |
+
},
|
| 286 |
+
{
|
| 287 |
+
"id": "online_wg",
|
| 288 |
+
"priority": 15,
|
| 289 |
+
"when": ["(device.features.has(\"subgroups\") and dim(shapes.x, ranks.x - 1) > 0) or dim(shapes.x, ranks.x - 1) > 32", "ceilDiv(rows(shapes.x, ranks.x - 1), device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension", "lastAxisOk"],
|
| 290 |
+
"constants": {
|
| 291 |
+
"combineSubgroups": "device.features.has(\"subgroups\")",
|
| 292 |
+
"scalar": "dtypes.T",
|
| 293 |
+
"usesF16": "dtypes.T == \"f16\""
|
| 294 |
+
},
|
| 295 |
+
"passes": [
|
| 296 |
+
{
|
| 297 |
+
"id": "main",
|
| 298 |
+
"name": "LogSoftmax.OnlineWorkgroup",
|
| 299 |
+
"source": { "shader": "softmax-online.wgsl.jinja", "inputs": { "op": "\"logsoftmax\"", "useVec4": "false" } },
|
| 300 |
+
"bindings": "online_scalar",
|
| 301 |
+
"dispatch": { "workgroups": "rows(shapes.x, ranks.x - 1)" }
|
| 302 |
+
}
|
| 303 |
+
]
|
| 304 |
+
},
|
| 305 |
+
{
|
| 306 |
+
"id": "online_packed_rows_vec4_nosg",
|
| 307 |
+
"priority": 17,
|
| 308 |
+
"when": ["dim(shapes.x, ranks.x - 1) > 0", "dim(shapes.x, ranks.x - 1) <= 32", "dim(shapes.x, ranks.x - 1) % 4 == 0", "rows(shapes.x, ranks.x - 1) > 32", "lastAxisOk"],
|
| 309 |
+
"constants": {
|
| 310 |
+
"scalar": "dtypes.T",
|
| 311 |
+
"vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
|
| 312 |
+
"usesF16": "dtypes.T == \"f16\"",
|
| 313 |
+
"lanesPerRow": 8,
|
| 314 |
+
"rowsPerWorkgroup": 32
|
| 315 |
+
},
|
| 316 |
+
"passes": [
|
| 317 |
+
{
|
| 318 |
+
"id": "main",
|
| 319 |
+
"name": "LogSoftmax.OnlinePackedRowsVec4NoSg",
|
| 320 |
+
"source": {
|
| 321 |
+
"shader": "softmax-online-packed-rows.wgsl.jinja",
|
| 322 |
+
"inputs": {
|
| 323 |
+
"op": "\"logsoftmax\"",
|
| 324 |
+
"lanesPerRow": "constants.lanesPerRow",
|
| 325 |
+
"rowsPerWorkgroup": "constants.rowsPerWorkgroup"
|
| 326 |
+
}
|
| 327 |
+
},
|
| 328 |
+
"bindings": "online_vec4",
|
| 329 |
+
"dispatch": { "gridStride": "ceilDiv(rows(shapes.x, ranks.x - 1), constants.rowsPerWorkgroup)" }
|
| 330 |
+
}
|
| 331 |
+
]
|
| 332 |
+
},
|
| 333 |
+
{
|
| 334 |
+
"id": "online_local_vec4_nosg",
|
| 335 |
+
"priority": 16,
|
| 336 |
+
"tunables": { "WORKGROUP_SIZE": 8 },
|
| 337 |
+
"when": ["dim(shapes.x, ranks.x - 1) > 0", "dim(shapes.x, ranks.x - 1) <= 32", "dim(shapes.x, ranks.x - 1) % 4 == 0", "ceilDiv(rows(shapes.x, ranks.x - 1), device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension", "lastAxisOk"],
|
| 338 |
+
"constants": {
|
| 339 |
+
"combineSubgroups": false,
|
| 340 |
+
"scalar": "dtypes.T",
|
| 341 |
+
"vectorScalar": "\"vec4<\" ~ dtypes.T ~ \">\"",
|
| 342 |
+
"usesF16": "dtypes.T == \"f16\""
|
| 343 |
+
},
|
| 344 |
+
"passes": [
|
| 345 |
+
{
|
| 346 |
+
"id": "main",
|
| 347 |
+
"name": "LogSoftmax.OnlineLocalVec4NoSg",
|
| 348 |
+
"source": { "shader": "softmax-online.wgsl.jinja", "inputs": { "op": "\"logsoftmax\"", "useVec4": "true" } },
|
| 349 |
+
"bindings": "online_vec4",
|
| 350 |
+
"dispatch": { "workgroups": "rows(shapes.x, ranks.x - 1)" }
|
| 351 |
+
}
|
| 352 |
+
]
|
| 353 |
+
},
|
| 354 |
+
{
|
| 355 |
+
"id": "online_local_nosg",
|
| 356 |
+
"priority": 14,
|
| 357 |
+
"tunables": { "WORKGROUP_SIZE": 32 },
|
| 358 |
+
"when": ["dim(shapes.x, ranks.x - 1) > 0", "dim(shapes.x, ranks.x - 1) <= 32", "ceilDiv(rows(shapes.x, ranks.x - 1), device.limits.maxComputeWorkgroupsPerDimension) <= device.limits.maxComputeWorkgroupsPerDimension", "lastAxisOk"],
|
| 359 |
+
"constants": { "combineSubgroups": false, "scalar": "dtypes.T", "usesF16": "dtypes.T == \"f16\"" },
|
| 360 |
+
"passes": [
|
| 361 |
+
{
|
| 362 |
+
"id": "main",
|
| 363 |
+
"name": "LogSoftmax.OnlineLocalNoSg",
|
| 364 |
+
"source": { "shader": "softmax-online.wgsl.jinja", "inputs": { "op": "\"logsoftmax\"", "useVec4": "false" } },
|
| 365 |
+
"bindings": "online_scalar",
|
| 366 |
+
"dispatch": { "workgroups": "rows(shapes.x, ranks.x - 1)" }
|
| 367 |
+
}
|
| 368 |
+
]
|
| 369 |
+
},
|
| 370 |
+
{
|
| 371 |
+
"id": "stable_3pass",
|
| 372 |
+
"priority": 8,
|
| 373 |
+
"when": ["lastAxisOk", "lastDispatchFits", "lastRowScratchFits"],
|
| 374 |
+
"intermediates": [
|
| 375 |
+
{ "id": "rowMax", "dtype": "float32", "shape": "[numel(shapes.x) / dim(shapes.x, ranks.x - 1)]" },
|
| 376 |
+
{ "id": "rowSum", "dtype": "float32", "shape": "[numel(shapes.x) / dim(shapes.x, ranks.x - 1)]" }
|
| 377 |
+
],
|
| 378 |
+
"passes": [
|
| 379 |
+
{
|
| 380 |
+
"id": "reduce_max",
|
| 381 |
+
"name": "LogSoftmax.ReduceMax",
|
| 382 |
+
"source": { "shader": "softmax-row-stage.wgsl.jinja", "inputs": { "stage": "\"max\"", "strided": "false" } },
|
| 383 |
+
"bindings": "stableReduceMax",
|
| 384 |
+
"dispatch": { "workgroups": "numel(shapes.x) / dim(shapes.x, ranks.x - 1)" }
|
| 385 |
+
},
|
| 386 |
+
{
|
| 387 |
+
"id": "exp_sum",
|
| 388 |
+
"name": "LogSoftmax.ExpSum",
|
| 389 |
+
"source": {
|
| 390 |
+
"shader": "softmax-row-stage.wgsl.jinja",
|
| 391 |
+
"inputs": { "stage": "\"exp_sum\"", "strided": "false" }
|
| 392 |
+
},
|
| 393 |
+
"bindings": "stableExpSum",
|
| 394 |
+
"dispatch": { "workgroups": "numel(shapes.x) / dim(shapes.x, ranks.x - 1)" }
|
| 395 |
+
},
|
| 396 |
+
{
|
| 397 |
+
"id": "normalize",
|
| 398 |
+
"name": "LogSoftmax.Normalize",
|
| 399 |
+
"source": {
|
| 400 |
+
"shader": "softmax-normalize.wgsl.jinja",
|
| 401 |
+
"inputs": { "strided": "false", "op": "\"logsoftmax\"" }
|
| 402 |
+
},
|
| 403 |
+
"bindings": "stableNormalize",
|
| 404 |
+
"dispatch": { "threads": "numel(shapes.x)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
|
| 405 |
+
}
|
| 406 |
+
]
|
| 407 |
+
},
|
| 408 |
+
{
|
| 409 |
+
"id": "strided_3pass_vec4",
|
| 410 |
+
"priority": 14,
|
| 411 |
+
"derive": { "axis": "attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x" },
|
| 412 |
+
"when": ["dtypes.T == \"f32\"", "stridedAxisOk", "inner(shapes.x, attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x) % 4 == 0", "axisDispatchFits", "axisRowScratchFits"],
|
| 413 |
+
"intermediates": [
|
| 414 |
+
{ "id": "rowMax", "dtype": "float32", "shape": "[numel(shapes.x) / dim(shapes.x, axis)]" },
|
| 415 |
+
{ "id": "rowSum", "dtype": "float32", "shape": "[numel(shapes.x) / dim(shapes.x, axis)]" }
|
| 416 |
+
],
|
| 417 |
+
"passes": [
|
| 418 |
+
{
|
| 419 |
+
"id": "reduce_max",
|
| 420 |
+
"name": "LogSoftmax.StridedReduceMaxVec4",
|
| 421 |
+
"source": {
|
| 422 |
+
"shader": "softmax-row-stage-strided-vec4.wgsl.jinja",
|
| 423 |
+
"inputs": { "stage": "\"max\"", "xShape": "shapes.x", "axis": "axis" }
|
| 424 |
+
},
|
| 425 |
+
"bindings": [
|
| 426 |
+
{
|
| 427 |
+
"name": "x",
|
| 428 |
+
"arg": "x",
|
| 429 |
+
"semantic": "input",
|
| 430 |
+
"buffer": { "type": "read-only-storage" },
|
| 431 |
+
"elementType": "vec4<f32>"
|
| 432 |
+
},
|
| 433 |
+
{ "name": "rowMax", "semantic": "rowMax", "buffer": { "type": "storage" }, "elementType": "vec4<f32>" }
|
| 434 |
+
],
|
| 435 |
+
"dispatch": { "workgroups": "numel(shapes.x) / dim(shapes.x, axis) / 4" }
|
| 436 |
+
},
|
| 437 |
+
{
|
| 438 |
+
"id": "exp_sum",
|
| 439 |
+
"name": "LogSoftmax.StridedExpSumVec4",
|
| 440 |
+
"source": {
|
| 441 |
+
"shader": "softmax-row-stage-strided-vec4.wgsl.jinja",
|
| 442 |
+
"inputs": { "stage": "\"exp_sum\"", "xShape": "shapes.x", "axis": "axis" }
|
| 443 |
+
},
|
| 444 |
+
"bindings": [
|
| 445 |
+
{
|
| 446 |
+
"name": "x",
|
| 447 |
+
"arg": "x",
|
| 448 |
+
"semantic": "input",
|
| 449 |
+
"buffer": { "type": "read-only-storage" },
|
| 450 |
+
"elementType": "vec4<f32>"
|
| 451 |
+
},
|
| 452 |
+
{
|
| 453 |
+
"name": "rowMax",
|
| 454 |
+
"semantic": "rowMax",
|
| 455 |
+
"buffer": { "type": "read-only-storage" },
|
| 456 |
+
"elementType": "vec4<f32>"
|
| 457 |
+
},
|
| 458 |
+
{
|
| 459 |
+
"name": "y",
|
| 460 |
+
"arg": "y",
|
| 461 |
+
"semantic": "output",
|
| 462 |
+
"buffer": { "type": "storage" },
|
| 463 |
+
"elementType": "vec4<f32>"
|
| 464 |
+
},
|
| 465 |
+
{ "name": "rowSum", "semantic": "rowSum", "buffer": { "type": "storage" }, "elementType": "vec4<f32>" }
|
| 466 |
+
],
|
| 467 |
+
"dispatch": { "workgroups": "numel(shapes.x) / dim(shapes.x, axis) / 4" }
|
| 468 |
+
},
|
| 469 |
+
{
|
| 470 |
+
"id": "normalize",
|
| 471 |
+
"name": "LogSoftmax.StridedNormalize",
|
| 472 |
+
"source": {
|
| 473 |
+
"shader": "softmax-normalize.wgsl.jinja",
|
| 474 |
+
"inputs": { "strided": "true", "xShape": "shapes.x", "axis": "axis", "op": "\"logsoftmax\"" }
|
| 475 |
+
},
|
| 476 |
+
"bindings": "normalize",
|
| 477 |
+
"dispatch": { "threads": "numel(shapes.x)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
|
| 478 |
+
}
|
| 479 |
+
]
|
| 480 |
+
},
|
| 481 |
+
{
|
| 482 |
+
"id": "strided_online_packed4_tail",
|
| 483 |
+
"priority": 18,
|
| 484 |
+
"derive": { "axis": "attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x" },
|
| 485 |
+
"when": ["dtypes.T == \"f32\"", "stridedAxisOk", "inner(shapes.x, attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x) >= 4", "inner(shapes.x, attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x) % 4 != 0", "dim(shapes.x, axis) < 2048 or axisRows < tunables.STRIDED_CAPACITY_MIN_ROWS", "axisDispatchFits"],
|
| 486 |
+
"passes": [
|
| 487 |
+
{
|
| 488 |
+
"id": "main",
|
| 489 |
+
"name": "LogSoftmax.StridedOnlinePacked4Tail",
|
| 490 |
+
"source": {
|
| 491 |
+
"shader": "softmax-strided-packed4-tail.wgsl.jinja",
|
| 492 |
+
"inputs": { "xShape": "shapes.x", "axis": "axis" }
|
| 493 |
+
},
|
| 494 |
+
"bindings": [
|
| 495 |
+
{
|
| 496 |
+
"name": "x",
|
| 497 |
+
"arg": "x",
|
| 498 |
+
"semantic": "input",
|
| 499 |
+
"buffer": { "type": "read-only-storage" },
|
| 500 |
+
"elementType": "f32"
|
| 501 |
+
},
|
| 502 |
+
{ "name": "y", "arg": "y", "semantic": "output", "buffer": { "type": "storage" }, "elementType": "f32" }
|
| 503 |
+
],
|
| 504 |
+
"dispatch": {
|
| 505 |
+
"workgroups": "numel(shapes.x) / dim(shapes.x, axis) / inner(shapes.x, axis) * ceilDiv(inner(shapes.x, axis), 4)"
|
| 506 |
+
}
|
| 507 |
+
}
|
| 508 |
+
]
|
| 509 |
+
},
|
| 510 |
+
{
|
| 511 |
+
"id": "strided_3pass",
|
| 512 |
+
"priority": 12,
|
| 513 |
+
"derive": { "axis": "attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x" },
|
| 514 |
+
"demoteWhen": ["dtypes.T == \"f32\" and inner(shapes.x, attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x) >= 4 and inner(shapes.x, attrs.axis if attrs.axis >= 0 else attrs.axis + ranks.x) % 4 != 0"],
|
| 515 |
+
"when": ["stridedAxisOk", "f16Ok(dtypes.T)", "axisDispatchFits", "axisRowScratchFits"],
|
| 516 |
+
"intermediates": [
|
| 517 |
+
{ "id": "rowMax", "dtype": "float32", "shape": "[numel(shapes.x) / dim(shapes.x, axis)]" },
|
| 518 |
+
{ "id": "rowSum", "dtype": "float32", "shape": "[numel(shapes.x) / dim(shapes.x, axis)]" }
|
| 519 |
+
],
|
| 520 |
+
"passes": [
|
| 521 |
+
{
|
| 522 |
+
"id": "reduce_max",
|
| 523 |
+
"name": "LogSoftmax.StridedReduceMax",
|
| 524 |
+
"source": {
|
| 525 |
+
"shader": "softmax-row-stage.wgsl.jinja",
|
| 526 |
+
"inputs": { "stage": "\"max\"", "strided": "true", "xShape": "shapes.x", "axis": "axis" }
|
| 527 |
+
},
|
| 528 |
+
"bindings": "stridedScalarReduce",
|
| 529 |
+
"dispatch": { "workgroups": "numel(shapes.x) / dim(shapes.x, axis)" }
|
| 530 |
+
},
|
| 531 |
+
{
|
| 532 |
+
"id": "exp_sum",
|
| 533 |
+
"name": "LogSoftmax.StridedExpSum",
|
| 534 |
+
"source": {
|
| 535 |
+
"shader": "softmax-row-stage.wgsl.jinja",
|
| 536 |
+
"inputs": { "stage": "\"exp_sum\"", "strided": "true", "xShape": "shapes.x", "axis": "axis" }
|
| 537 |
+
},
|
| 538 |
+
"bindings": "stridedScalarExpSum",
|
| 539 |
+
"dispatch": { "workgroups": "numel(shapes.x) / dim(shapes.x, axis)" }
|
| 540 |
+
},
|
| 541 |
+
{
|
| 542 |
+
"id": "normalize",
|
| 543 |
+
"name": "LogSoftmax.StridedNormalize",
|
| 544 |
+
"source": {
|
| 545 |
+
"shader": "softmax-normalize.wgsl.jinja",
|
| 546 |
+
"inputs": { "strided": "true", "xShape": "shapes.x", "axis": "axis", "op": "\"logsoftmax\"" }
|
| 547 |
+
},
|
| 548 |
+
"bindings": "normalize",
|
| 549 |
+
"dispatch": { "threads": "numel(shapes.x)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
|
| 550 |
+
}
|
| 551 |
+
]
|
| 552 |
+
},
|
| 553 |
+
{
|
| 554 |
+
"id": "strided_online_capacity_fallback",
|
| 555 |
+
"priority": -1,
|
| 556 |
+
"when": ["stridedAxisOk", "f16Ok(dtypes.T)", "axisThreadDispatchFits"],
|
| 557 |
+
"passes": [
|
| 558 |
+
{
|
| 559 |
+
"id": "main",
|
| 560 |
+
"name": "LogSoftmax.StridedOnlineCapacityFallback",
|
| 561 |
+
"source": {
|
| 562 |
+
"shader": "softmax-strided-online-lane.wgsl.jinja",
|
| 563 |
+
"inputs": { "xShape": "shapes.x", "axis": "axis", "op": "\"logsoftmax\"" }
|
| 564 |
+
},
|
| 565 |
+
"bindings": "stridedOnline",
|
| 566 |
+
"dispatch": { "threads": "axisRows", "workgroupSize": "tunables.WORKGROUP_SIZE" }
|
| 567 |
+
}
|
| 568 |
+
]
|
| 569 |
+
}
|
| 570 |
+
]
|
| 571 |
+
}
|
build/webgpu/metadata.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "ai.onnx.LogSoftmax",
|
| 3 |
+
"id": "_ai_onnx_logsoftmax_webgpu_0804fe4",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"backend": { "type": "webgpu" },
|
| 7 |
+
"digest": {
|
| 8 |
+
"algorithm": "sha256",
|
| 9 |
+
"files": {
|
| 10 |
+
"bench.json": "rqFaX5XmJwQz0QQ1ET5FRMCuYEMotdcOJR+z6Y57a/0=",
|
| 11 |
+
"manifest.json": "QDbHBdv1drmn3+Mqxdqx24xatM7c38mQxNZrDzDPmM8=",
|
| 12 |
+
"softmax-longrow-normalize.wgsl.jinja": "AamVdoPDaWPQ3sk5loxtcuNu7oftC5J7hsPwLRDA7M0=",
|
| 13 |
+
"softmax-longrow-stats.wgsl.jinja": "K0PI+9cmRgjJsOOcBRyugN0Dm7yxBy0xvyPamEpCgxo=",
|
| 14 |
+
"softmax-normalize.wgsl.jinja": "eekHyttLyBk2X/mG54dUS2kIc5KycR3grH6000pqh1A=",
|
| 15 |
+
"softmax-online-packed-rows.wgsl.jinja": "94Xmz8Mtj/ji7XpIQUpsDdO6uShKJtWknzEI0Er+sT8=",
|
| 16 |
+
"softmax-online.wgsl.jinja": "qL/179d83HTFuyCUkfHqE0MBbtFMOEl8lzAH7aWmUXA=",
|
| 17 |
+
"softmax-row-stage-strided-vec4.wgsl.jinja": "l1byT6xoLl6Q7kSPd/uws6X5NsS2PwlSv2TBMCioVZA=",
|
| 18 |
+
"softmax-row-stage.wgsl.jinja": "dxlM6y02DwXeLeavHDiq70h0NCRZgz+bniCltyEgRX4=",
|
| 19 |
+
"softmax-strided-online-lane.wgsl.jinja": "BTLdB6LamPLNJsNmsllObl/9Y/Cx1EbuwdqeYpcppXg=",
|
| 20 |
+
"softmax-strided-packed4-tail.wgsl.jinja": "z7+TYeybaE/yXds/ODe4wMyosZeIrCtTfUmZsA2eIXo=",
|
| 21 |
+
"test.json": "uvZ0xakkYLGTptboBDjo8khdaQQ2h+QWiu4Z/wLWmfQ="
|
| 22 |
+
}
|
| 23 |
+
},
|
| 24 |
+
"provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
|
| 25 |
+
"webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.LogSoftmax" }
|
| 26 |
+
}
|
build/webgpu/softmax-longrow-normalize.wgsl.jinja
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Final normalization pass for long-row split softmax. Each workgroup handles
|
| 2 |
+
// one block of a row and re-reads the input against the completed row maximum
|
| 3 |
+
// and denominator. The epilogue supports softmax and log-softmax.
|
| 4 |
+
{% if T == "f16" %}
|
| 5 |
+
enable f16;
|
| 6 |
+
{% endif %}
|
| 7 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 8 |
+
|
| 9 |
+
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 10 |
+
const BLOCK_COLS: u32 = {{ tunables.BLOCK_COLS }}u;
|
| 11 |
+
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 12 |
+
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
| 13 |
+
// accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
|
| 14 |
+
// a zero final denominator according to their public semantics. Using -inf
|
| 15 |
+
// here changes +inf-row behavior.
|
| 16 |
+
const FLT_MAX: f32 = 3.4028234663852886e38;
|
| 17 |
+
|
| 18 |
+
fn is_finite_f32(value: f32) -> bool {
|
| 19 |
+
return select(false, value <= FLT_MAX, value >= -FLT_MAX);
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
// x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
|
| 23 |
+
// exactly at the row max. `x - x` on an infinite max is a legal fast-math
|
| 24 |
+
// fold to 0, which would silently turn +inf rows finite — the explicit
|
| 25 |
+
// equality test keeps the NaN propagation of the serial kernels.
|
| 26 |
+
fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
| 27 |
+
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 28 |
+
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 29 |
+
}
|
| 30 |
+
// Bit-exact NaN test: `v != v` can be folded to false by fast-math backends,
|
| 31 |
+
// and a constant NaN bit pattern is rejected at WGSL const-evaluation.
|
| 32 |
+
fn is_nan_f32(value: f32) -> bool {
|
| 33 |
+
let bits = bitcast<u32>(value);
|
| 34 |
+
return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@compute @workgroup_size(WG)
|
| 39 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>,
|
| 40 |
+
@builtin(local_invocation_id) lid: vec3<u32>) {
|
| 41 |
+
let block = wg.x;
|
| 42 |
+
let row = wg.y;
|
| 43 |
+
let rowBase = row * params.cols;
|
| 44 |
+
let colStart = block * BLOCK_COLS;
|
| 45 |
+
let colEnd = min(params.cols, colStart + BLOCK_COLS);
|
| 46 |
+
|
| 47 |
+
let m = rowMax[row];
|
| 48 |
+
let d = rowSum[row];
|
| 49 |
+
// NaN row (any NaN input, or +inf max) -> all-NaN row, with the quiet NaN
|
| 50 |
+
// derived from the runtime value (a constant NaN bit pattern is rejected at
|
| 51 |
+
// WGSL const-evaluation).
|
| 52 |
+
let rowIsNan = m > FLT_MAX || is_nan_f32(d) || d == 0.0;
|
| 53 |
+
let nanOut = bitcast<f32>(bitcast<u32>(d) | 0x7fc00000u);
|
| 54 |
+
let logD = log(d);
|
| 55 |
+
|
| 56 |
+
for (var col = colStart + lid.x; col < colEnd; col = col + WG) {
|
| 57 |
+
let v = f32(x[rowBase + col]);
|
| 58 |
+
// (x - m) - log(d). x == m gives exactly 0 - log(d) (IEEE subtraction of
|
| 59 |
+
// equal finite values is +0), matching the serial kernel's shift trick.
|
| 60 |
+
var out = shifted_value(v, m) - logD;
|
| 61 |
+
if (rowIsNan) {
|
| 62 |
+
out = nanOut;
|
| 63 |
+
}
|
| 64 |
+
y[rowBase + col] = {{ T }}(out);
|
| 65 |
+
}
|
| 66 |
+
}
|
build/webgpu/softmax-longrow-stats.wgsl.jinja
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Online-softmax (maximum, sum-exp) statistics for very long contiguous
|
| 2 |
+
// last-axis rows. The statistics are the same for softmax and log-softmax.
|
| 3 |
+
// The block stage assigns one workgroup to each (block, row), merges per-thread
|
| 4 |
+
// online pairs, and writes one partial pair. The row stage assigns one
|
| 5 |
+
// workgroup per row and folds those block partials into rowMax and rowSum.
|
| 6 |
+
{% if source.stage == "block" and T == "f16" %}
|
| 7 |
+
enable f16;
|
| 8 |
+
{% endif %}
|
| 9 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 10 |
+
|
| 11 |
+
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 12 |
+
{% if source.stage == "block" %}
|
| 13 |
+
const BLOCK_COLS: u32 = {{ tunables.BLOCK_COLS }}u;
|
| 14 |
+
{% endif %}
|
| 15 |
+
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 16 |
+
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
| 17 |
+
// accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
|
| 18 |
+
// a zero final denominator according to their public semantics. Using -inf
|
| 19 |
+
// here changes +inf-row behavior.
|
| 20 |
+
const FLT_MAX: f32 = 3.4028234663852886e38;
|
| 21 |
+
|
| 22 |
+
fn is_finite_f32(value: f32) -> bool {
|
| 23 |
+
return select(false, value <= FLT_MAX, value >= -FLT_MAX);
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
// x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
|
| 27 |
+
// exactly at the row max. `x - x` on an infinite max is a legal fast-math
|
| 28 |
+
// fold to 0, which would silently turn +inf rows finite — the explicit
|
| 29 |
+
// equality test keeps the NaN propagation of the serial kernels.
|
| 30 |
+
fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
| 31 |
+
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 32 |
+
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 33 |
+
}
|
| 34 |
+
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 35 |
+
return exp(shifted_value(value, maxValue));
|
| 36 |
+
}
|
| 37 |
+
// Workgroup-cooperative merge of per-thread online-softmax (m, d) partials:
|
| 38 |
+
// mNew = max(m1, m2)
|
| 39 |
+
// dNew = d1 * exp(m1 - mNew) + d2 * exp(m2 - mNew)
|
| 40 |
+
// Both the subgroup and portable barrier-tree engines return the same merged
|
| 41 |
+
// pair to every invocation. Repeated merges require a workgroup barrier between
|
| 42 |
+
// calls before their shared partial storage is reused.
|
| 43 |
+
{% set combineSubgroups = combineSubgroups is defined and combineSubgroups %}
|
| 44 |
+
{% if combineSubgroups %}
|
| 45 |
+
// Per-subgroup partials are published into a deterministic slot: the subgroup's
|
| 46 |
+
// ordinal index within the workgroup (lidx / sgSize). The online (m, d) merge
|
| 47 |
+
// is not float-associative, so thread 0 must fold partials in a fixed order.
|
| 48 |
+
// Subgroups partition a workgroup into contiguous ordinal ranges on supported
|
| 49 |
+
// backends, so the ordinal slot is unique per subgroup and every slot in
|
| 50 |
+
// [0, subgroupCount) is written (each subgroup elects one leader).
|
| 51 |
+
// Sized for the worst case of one partial per invocation.
|
| 52 |
+
var<workgroup> partialM: array<f32, WG>;
|
| 53 |
+
var<workgroup> partialD: array<f32, WG>;
|
| 54 |
+
var<workgroup> combinedMD: vec2<f32>;
|
| 55 |
+
|
| 56 |
+
// When the whole workgroup is one subgroup the subgroup reduce already covers
|
| 57 |
+
// it (no barriers, no shared state); otherwise subgroup leaders publish
|
| 58 |
+
// partials through shared memory and thread 0 folds them in ordinal order.
|
| 59 |
+
fn combine_partials(m: f32, d: f32, lidx: u32, sgSize: u32) -> vec2<f32> {
|
| 60 |
+
let sgM = subgroupMax(m);
|
| 61 |
+
// A lane with no elements contributes d == 0 (exact identity). A +inf
|
| 62 |
+
// element made exp(inf - inf) = NaN stick in that lane's d; a NaN element
|
| 63 |
+
// landed in d via exp(NaN); both survive the merge and are detected by the
|
| 64 |
+
// code after the reduction.
|
| 65 |
+
let sgD = subgroupAdd(d * exp_shift(m, sgM));
|
| 66 |
+
if (sgSize == WG) {
|
| 67 |
+
return vec2<f32>(sgM, sgD);
|
| 68 |
+
}
|
| 69 |
+
let subgroupCount = (WG + sgSize - 1u) / sgSize;
|
| 70 |
+
// Pre-seed every fold slot with the (max, denom) identity. The fold below reads a
|
| 71 |
+
// fixed subgroupCount slots in ordinal order (for determinism), but a slot whose
|
| 72 |
+
// subgroup elects no leader this call — e.g. a fully out-of-window key tile in the
|
| 73 |
+
// flash-attention loop that re-uses this shared memory each iteration — would
|
| 74 |
+
// otherwise read stale shared memory. Identity makes such a slot a no-op.
|
| 75 |
+
// (max identity = -FLT_MAX, denom identity = 0.)
|
| 76 |
+
if (lidx < subgroupCount) {
|
| 77 |
+
partialM[lidx] = -FLT_MAX;
|
| 78 |
+
partialD[lidx] = 0.0;
|
| 79 |
+
}
|
| 80 |
+
workgroupBarrier();
|
| 81 |
+
if (subgroupElect()) {
|
| 82 |
+
let slot = lidx / sgSize;
|
| 83 |
+
partialM[slot] = sgM;
|
| 84 |
+
partialD[slot] = sgD;
|
| 85 |
+
}
|
| 86 |
+
workgroupBarrier();
|
| 87 |
+
if (lidx == 0u) {
|
| 88 |
+
var accM = -FLT_MAX;
|
| 89 |
+
var accD = 0.0;
|
| 90 |
+
for (var i = 0u; i < subgroupCount; i = i + 1u) {
|
| 91 |
+
let mNew = max(accM, partialM[i]);
|
| 92 |
+
accD = accD * exp_shift(accM, mNew) + partialD[i] * exp_shift(partialM[i], mNew);
|
| 93 |
+
accM = mNew;
|
| 94 |
+
}
|
| 95 |
+
combinedMD = vec2<f32>(accM, accD);
|
| 96 |
+
}
|
| 97 |
+
workgroupBarrier();
|
| 98 |
+
return combinedMD;
|
| 99 |
+
}
|
| 100 |
+
{% else %}
|
| 101 |
+
{% set mdStreamed = mdStreams is defined %}
|
| 102 |
+
{% set mdStreams = mdStreams if mdStreams is defined else 1 %}
|
| 103 |
+
{% set mdExtent = "WG" if mdStreams == 1 else "WG * " ~ mdStreams ~ "u" %}
|
| 104 |
+
var<workgroup> partialM: array<f32, {{ mdExtent }}>;
|
| 105 |
+
var<workgroup> partialD: array<f32, {{ mdExtent }}>;
|
| 106 |
+
{% if mdStreamed %}
|
| 107 |
+
|
| 108 |
+
// In-place fold of {{ mdStreams }} streams. The caller stores its per-thread
|
| 109 |
+
// partials into partialM/partialD first and reads the merged pair of stream s
|
| 110 |
+
// from slot s * WG afterwards.
|
| 111 |
+
fn combine_partials_streams(lidx: u32) {
|
| 112 |
+
workgroupBarrier();
|
| 113 |
+
var stride = WG / 2u;
|
| 114 |
+
loop {
|
| 115 |
+
if (stride == 0u) {
|
| 116 |
+
break;
|
| 117 |
+
}
|
| 118 |
+
if (lidx < stride) {
|
| 119 |
+
{% for s in range(mdStreams) %}
|
| 120 |
+
{
|
| 121 |
+
let slot = {{ s }}u * WG + lidx;
|
| 122 |
+
let m1 = partialM[slot];
|
| 123 |
+
let d1 = partialD[slot];
|
| 124 |
+
let m2 = partialM[slot + stride];
|
| 125 |
+
let d2 = partialD[slot + stride];
|
| 126 |
+
let mNew = max(m1, m2);
|
| 127 |
+
partialD[slot] = d1 * exp_shift(m1, mNew) + d2 * exp_shift(m2, mNew);
|
| 128 |
+
partialM[slot] = mNew;
|
| 129 |
+
}
|
| 130 |
+
{% endfor %}
|
| 131 |
+
}
|
| 132 |
+
workgroupBarrier();
|
| 133 |
+
stride = stride / 2u;
|
| 134 |
+
}
|
| 135 |
+
}
|
| 136 |
+
{% else %}
|
| 137 |
+
|
| 138 |
+
fn combine_partials(m: f32, d: f32, lidx: u32) -> vec2<f32> {
|
| 139 |
+
partialM[lidx] = m;
|
| 140 |
+
partialD[lidx] = d;
|
| 141 |
+
workgroupBarrier();
|
| 142 |
+
var stride = WG / 2u;
|
| 143 |
+
loop {
|
| 144 |
+
if (stride == 0u) {
|
| 145 |
+
break;
|
| 146 |
+
}
|
| 147 |
+
if (lidx < stride) {
|
| 148 |
+
let m1 = partialM[lidx];
|
| 149 |
+
let d1 = partialD[lidx];
|
| 150 |
+
let m2 = partialM[lidx + stride];
|
| 151 |
+
let d2 = partialD[lidx + stride];
|
| 152 |
+
let mNew = max(m1, m2);
|
| 153 |
+
partialD[lidx] = d1 * exp_shift(m1, mNew) + d2 * exp_shift(m2, mNew);
|
| 154 |
+
partialM[lidx] = mNew;
|
| 155 |
+
}
|
| 156 |
+
workgroupBarrier();
|
| 157 |
+
stride = stride / 2u;
|
| 158 |
+
}
|
| 159 |
+
let merged = vec2<f32>(partialM[0], partialD[0]);
|
| 160 |
+
// Trailing barrier so back-to-back calls cannot race a next call's partial
|
| 161 |
+
// stores against this call's reads of slot 0.
|
| 162 |
+
workgroupBarrier();
|
| 163 |
+
return merged;
|
| 164 |
+
}
|
| 165 |
+
{% endif %}
|
| 166 |
+
{% endif %}
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
@compute @workgroup_size(WG)
|
| 170 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>,
|
| 171 |
+
@builtin(local_invocation_id) lid: vec3<u32>) {
|
| 172 |
+
{% if source.stage == "block" %}
|
| 173 |
+
let block = wg.x;
|
| 174 |
+
let row = wg.y;
|
| 175 |
+
let rowBase = row * params.cols;
|
| 176 |
+
let colStart = block * BLOCK_COLS;
|
| 177 |
+
let colEnd = min(params.cols, colStart + BLOCK_COLS);
|
| 178 |
+
|
| 179 |
+
var m = -FLT_MAX;
|
| 180 |
+
var d = 0.0;
|
| 181 |
+
for (var col = colStart + lid.x; col < colEnd; col = col + WG) {
|
| 182 |
+
let v = f32(x[rowBase + col]);
|
| 183 |
+
let mNew = max(m, v);
|
| 184 |
+
d = d * exp_shift(m, mNew) + exp_shift(v, mNew);
|
| 185 |
+
m = mNew;
|
| 186 |
+
}
|
| 187 |
+
{% else %}
|
| 188 |
+
let row = wg.x;
|
| 189 |
+
let base = row * params.blocks;
|
| 190 |
+
|
| 191 |
+
var m = -FLT_MAX;
|
| 192 |
+
var d = 0.0;
|
| 193 |
+
for (var block = lid.x; block < params.blocks; block = block + WG) {
|
| 194 |
+
let bm = blockMax[base + block];
|
| 195 |
+
let bd = blockSum[base + block];
|
| 196 |
+
let mNew = max(m, bm);
|
| 197 |
+
d = d * exp_shift(m, mNew) + bd * exp_shift(bm, mNew);
|
| 198 |
+
m = mNew;
|
| 199 |
+
}
|
| 200 |
+
{% endif %}
|
| 201 |
+
|
| 202 |
+
let merged = combine_partials(m, d, lid.x);
|
| 203 |
+
if (lid.x == 0u) {
|
| 204 |
+
{% if source.stage == "block" %}
|
| 205 |
+
let out = row * params.blocks + block;
|
| 206 |
+
blockMax[out] = merged.x;
|
| 207 |
+
blockSum[out] = merged.y;
|
| 208 |
+
{% else %}
|
| 209 |
+
rowMax[row] = merged.x;
|
| 210 |
+
rowSum[row] = merged.y;
|
| 211 |
+
{% endif %}
|
| 212 |
+
}
|
| 213 |
+
}
|
build/webgpu/softmax-normalize.wgsl.jinja
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Element-parallel pass 3 of three-pass softmax/log-softmax. Strided-axis mode
|
| 2 |
+
// changes only how an element finds its row. Softmax divides the exponential
|
| 3 |
+
// values already in y by the row sum. Log-softmax rereads x against the finished
|
| 4 |
+
// row statistics and writes (x - rowMax) - log(rowSum), so entries whose
|
| 5 |
+
// exponentials underflow remain finite.
|
| 6 |
+
{% if T == "f16" %}
|
| 7 |
+
enable f16;
|
| 8 |
+
{% endif %}
|
| 9 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 10 |
+
{% if source.strided %}
|
| 11 |
+
|
| 12 |
+
{% set stridedAxisUses = ["row_for_offset"] %}
|
| 13 |
+
// Compiled indexing for row-wise reductions over a non-last axis. A row is one
|
| 14 |
+
// slice along the reduction axis, indexed as outer * INNER_SIZE + inner.
|
| 15 |
+
{% set reduce_size = source.xShape[source.axis] %}
|
| 16 |
+
{% set inner_size = namespace(value=1) %}
|
| 17 |
+
{% for i in range(source.axis + 1, source.xShape | length) %}
|
| 18 |
+
{% set inner_size.value = inner_size.value * source.xShape[i] %}
|
| 19 |
+
{% endfor %}
|
| 20 |
+
{% set outer_size = namespace(value=1) %}
|
| 21 |
+
{% for i in range(source.axis) %}
|
| 22 |
+
{% set outer_size.value = outer_size.value * source.xShape[i] %}
|
| 23 |
+
{% endfor %}
|
| 24 |
+
{% set usesAll = stridedAxisUses is not defined %}
|
| 25 |
+
{% set usesElementOffset = usesAll or "element_offset" in stridedAxisUses %}
|
| 26 |
+
{% set usesRowForOffset = usesAll or "row_for_offset" in stridedAxisUses %}
|
| 27 |
+
{% set usesReduceSize = usesAll or usesElementOffset or "REDUCE_SIZE" in stridedAxisUses %}
|
| 28 |
+
{% set usesInnerSize = usesAll or usesElementOffset or "INNER_SIZE" in stridedAxisUses %}
|
| 29 |
+
{% set usesStridedRows = usesAll or "STRIDED_ROWS" in stridedAxisUses %}
|
| 30 |
+
{% set usesInnerSizeSafe = usesAll or usesElementOffset or usesRowForOffset or "INNER_SIZE_SAFE" in stridedAxisUses %}
|
| 31 |
+
{% set usesAxisStrideSafe = usesAll or usesRowForOffset or "AXIS_STRIDE_SAFE" in stridedAxisUses %}
|
| 32 |
+
{% if usesReduceSize %}
|
| 33 |
+
const REDUCE_SIZE: u32 = {{ reduce_size }}u;
|
| 34 |
+
{% endif %}
|
| 35 |
+
{% if usesInnerSize %}
|
| 36 |
+
const INNER_SIZE: u32 = {{ inner_size.value }}u;
|
| 37 |
+
{% endif %}
|
| 38 |
+
{% if usesStridedRows %}
|
| 39 |
+
// Total one-per-row workgroup count (product of every non-axis dim). Lets the
|
| 40 |
+
// row-wise reduce kernels 2D-fold their dispatch past the maxComputeWorkgroupsPerDimension limit and guard
|
| 41 |
+
// the over-dispatched tail without a runtime `params` uniform.
|
| 42 |
+
const STRIDED_ROWS: u32 = {{ outer_size.value * inner_size.value }}u;
|
| 43 |
+
{% endif %}
|
| 44 |
+
{% if usesInnerSizeSafe %}
|
| 45 |
+
const INNER_SIZE_SAFE: u32 = {{ inner_size.value if inner_size.value > 0 else 1 }}u;
|
| 46 |
+
{% endif %}
|
| 47 |
+
{% if usesAxisStrideSafe %}
|
| 48 |
+
const AXIS_STRIDE_SAFE: u32 = {{ (reduce_size * inner_size.value) if (reduce_size * inner_size.value) > 0 else 1 }}u;
|
| 49 |
+
{% endif %}
|
| 50 |
+
|
| 51 |
+
{% if usesElementOffset %}
|
| 52 |
+
// Flat offset of element k (along the reduce axis) of `row`.
|
| 53 |
+
fn element_offset(row: u32, k: u32) -> u32 {
|
| 54 |
+
let outer = row / INNER_SIZE_SAFE;
|
| 55 |
+
let inner = row % INNER_SIZE_SAFE;
|
| 56 |
+
return outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
{% endif %}
|
| 60 |
+
{% if usesRowForOffset %}
|
| 61 |
+
// Row that owns the flat element `offset` (inverse of element_offset over
|
| 62 |
+
// the non-axis coordinates).
|
| 63 |
+
fn row_for_offset(offset: u32) -> u32 {
|
| 64 |
+
let outer = offset / AXIS_STRIDE_SAFE;
|
| 65 |
+
let inner = offset % INNER_SIZE_SAFE;
|
| 66 |
+
return outer * INNER_SIZE_SAFE + inner;
|
| 67 |
+
}
|
| 68 |
+
{%- endif -%}
|
| 69 |
+
{% endif %}
|
| 70 |
+
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 71 |
+
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
| 72 |
+
// accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
|
| 73 |
+
// a zero final denominator according to their public semantics. Using -inf
|
| 74 |
+
// here changes +inf-row behavior.
|
| 75 |
+
const FLT_MAX: f32 = 3.4028234663852886e38;
|
| 76 |
+
|
| 77 |
+
fn is_finite_f32(value: f32) -> bool {
|
| 78 |
+
return select(false, value <= FLT_MAX, value >= -FLT_MAX);
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
// x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
|
| 82 |
+
// exactly at the row max. `x - x` on an infinite max is a legal fast-math
|
| 83 |
+
// fold to 0, which would silently turn +inf rows finite — the explicit
|
| 84 |
+
// equality test keeps the NaN propagation of the serial kernels.
|
| 85 |
+
fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
| 86 |
+
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 87 |
+
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 88 |
+
}
|
| 89 |
+
// Bit-exact NaN test: `v != v` can be folded to false by fast-math backends,
|
| 90 |
+
// and a constant NaN bit pattern is rejected at WGSL const-evaluation.
|
| 91 |
+
fn is_nan_f32(value: f32) -> bool {
|
| 92 |
+
let bits = bitcast<u32>(value);
|
| 93 |
+
return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 98 |
+
fn main(@builtin(global_invocation_id) gid : vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
|
| 99 |
+
// 2D-folded flat index: gid.y carries the high bits past the
|
| 100 |
+
// maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
|
| 101 |
+
let i = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
|
| 102 |
+
if (i >= params.count) {
|
| 103 |
+
return;
|
| 104 |
+
}
|
| 105 |
+
{% if source.strided %}
|
| 106 |
+
let row = row_for_offset(i);
|
| 107 |
+
{% else %}
|
| 108 |
+
let row = i / params.cols;
|
| 109 |
+
{% endif %}
|
| 110 |
+
let m = rowMax[row];
|
| 111 |
+
let d = rowSum[row];
|
| 112 |
+
// NaN row (any NaN input, or +inf max) -> all-NaN row, quiet NaN derived from
|
| 113 |
+
// the runtime value (a constant NaN bit pattern is rejected at const-eval).
|
| 114 |
+
let rowIsNan = m > FLT_MAX || is_nan_f32(d) || d == 0.0;
|
| 115 |
+
let nanOut = bitcast<f32>(bitcast<u32>(d) | 0x7fc00000u);
|
| 116 |
+
// (x - m) - log(d). x == m gives exactly 0 - log(d) (IEEE subtraction of equal
|
| 117 |
+
// finite values is +0), matching the serial kernel's shift trick.
|
| 118 |
+
var out = shifted_value(f32(x[i]), m) - log(d);
|
| 119 |
+
if (rowIsNan) {
|
| 120 |
+
out = nanOut;
|
| 121 |
+
}
|
| 122 |
+
y[i] = {{ T }}(out);
|
| 123 |
+
}
|
build/webgpu/softmax-online-packed-rows.wgsl.jinja
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Portable many-short-row online softmax / log-softmax. A 256-thread
|
| 2 |
+
// workgroup is partitioned into independent fixed-width row groups (normally
|
| 3 |
+
// 32 rows x 8 vec4 lanes). This preserves fully coalesced 32-column traffic
|
| 4 |
+
// while one workgroup covers many short rows.
|
| 5 |
+
{% if usesF16 %}
|
| 6 |
+
enable f16;
|
| 7 |
+
{% endif %}
|
| 8 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 9 |
+
|
| 10 |
+
const LANES_PER_ROW: u32 = {{ source.lanesPerRow }}u;
|
| 11 |
+
const ROWS_PER_WORKGROUP: u32 = {{ source.rowsPerWorkgroup }}u;
|
| 12 |
+
const WG: u32 = LANES_PER_ROW * ROWS_PER_WORKGROUP;
|
| 13 |
+
|
| 14 |
+
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 15 |
+
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
| 16 |
+
// accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
|
| 17 |
+
// a zero final denominator according to their public semantics. Using -inf
|
| 18 |
+
// here changes +inf-row behavior.
|
| 19 |
+
const FLT_MAX: f32 = 3.4028234663852886e38;
|
| 20 |
+
|
| 21 |
+
fn is_finite_f32(value: f32) -> bool {
|
| 22 |
+
return select(false, value <= FLT_MAX, value >= -FLT_MAX);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
// x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
|
| 26 |
+
// exactly at the row max. `x - x` on an infinite max is a legal fast-math
|
| 27 |
+
// fold to 0, which would silently turn +inf rows finite — the explicit
|
| 28 |
+
// equality test keeps the NaN propagation of the serial kernels.
|
| 29 |
+
fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
| 30 |
+
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 31 |
+
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 32 |
+
}
|
| 33 |
+
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 34 |
+
return exp(shifted_value(value, maxValue));
|
| 35 |
+
}
|
| 36 |
+
// Bit-exact NaN test: `v != v` can be folded to false by fast-math backends,
|
| 37 |
+
// and a constant NaN bit pattern is rejected at WGSL const-evaluation.
|
| 38 |
+
fn is_nan_f32(value: f32) -> bool {
|
| 39 |
+
let bits = bitcast<u32>(value);
|
| 40 |
+
return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
// Each logical row owns one contiguous LANES_PER_ROW slice. All reductions
|
| 45 |
+
// are workgroup-portable; no subgroup width or subgroup feature is assumed.
|
| 46 |
+
var<workgroup> partialM: array<f32, WG>;
|
| 47 |
+
var<workgroup> partialD: array<f32, WG>;
|
| 48 |
+
|
| 49 |
+
@compute @workgroup_size({{ source.lanesPerRow * source.rowsPerWorkgroup }})
|
| 50 |
+
fn main(
|
| 51 |
+
@builtin(workgroup_id) wid: vec3<u32>,
|
| 52 |
+
@builtin(num_workgroups) nwg: vec3<u32>,
|
| 53 |
+
@builtin(local_invocation_id) lid: vec3<u32>,
|
| 54 |
+
) {
|
| 55 |
+
let lane = lid.x % LANES_PER_ROW;
|
| 56 |
+
let localRow = lid.x / LANES_PER_ROW;
|
| 57 |
+
let partialBase = localRow * LANES_PER_ROW;
|
| 58 |
+
let rowGroups = (params.rows + ROWS_PER_WORKGROUP - 1u) / ROWS_PER_WORKGROUP;
|
| 59 |
+
|
| 60 |
+
// A clamped grid-stride dispatch avoids rectangular over-dispatch once the
|
| 61 |
+
// row-group count exceeds one dispatch dimension while still covering every
|
| 62 |
+
// row of arbitrarily tall tensors.
|
| 63 |
+
for (var rowGroup = wid.x; rowGroup < rowGroups; rowGroup += nwg.x) {
|
| 64 |
+
let row = rowGroup * ROWS_PER_WORKGROUP + localRow;
|
| 65 |
+
let validRow = row < params.rows;
|
| 66 |
+
|
| 67 |
+
var m = -FLT_MAX;
|
| 68 |
+
var d = 0.0;
|
| 69 |
+
if (validRow) {
|
| 70 |
+
let rowBase = row * params.vecCols;
|
| 71 |
+
for (var c = lane; c < params.vecCols; c += LANES_PER_ROW) {
|
| 72 |
+
let v = vec4<f32>(x[rowBase + c]);
|
| 73 |
+
let mNew = max(m, max(max(v.x, v.y), max(v.z, v.w)));
|
| 74 |
+
d = d * exp_shift(m, mNew)
|
| 75 |
+
+ exp_shift(v.x, mNew) + exp_shift(v.y, mNew)
|
| 76 |
+
+ exp_shift(v.z, mNew) + exp_shift(v.w, mNew);
|
| 77 |
+
m = mNew;
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
let partialIndex = partialBase + lane;
|
| 82 |
+
partialM[partialIndex] = m;
|
| 83 |
+
partialD[partialIndex] = d;
|
| 84 |
+
workgroupBarrier();
|
| 85 |
+
|
| 86 |
+
var stride = LANES_PER_ROW / 2u;
|
| 87 |
+
loop {
|
| 88 |
+
if (stride == 0u) {
|
| 89 |
+
break;
|
| 90 |
+
}
|
| 91 |
+
if (lane < stride) {
|
| 92 |
+
let left = partialBase + lane;
|
| 93 |
+
let right = left + stride;
|
| 94 |
+
let m1 = partialM[left];
|
| 95 |
+
let d1 = partialD[left];
|
| 96 |
+
let m2 = partialM[right];
|
| 97 |
+
let d2 = partialD[right];
|
| 98 |
+
let mNew = max(m1, m2);
|
| 99 |
+
partialD[left] = d1 * exp_shift(m1, mNew) + d2 * exp_shift(m2, mNew);
|
| 100 |
+
partialM[left] = mNew;
|
| 101 |
+
}
|
| 102 |
+
workgroupBarrier();
|
| 103 |
+
stride /= 2u;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
// The last reduction barrier publishes slot zero to every lane. Copy the
|
| 107 |
+
// result to private values before any lane can start the next row group.
|
| 108 |
+
let finalM = partialM[partialBase];
|
| 109 |
+
let finalD = partialD[partialBase];
|
| 110 |
+
let dBits = bitcast<u32>(finalD);
|
| 111 |
+
let rowIsNan = finalM > FLT_MAX || is_nan_f32(finalD) || finalD == 0.0;
|
| 112 |
+
let nanOut = bitcast<f32>(dBits | 0x7fc00000u);
|
| 113 |
+
|
| 114 |
+
if (validRow) {
|
| 115 |
+
let rowBase = row * params.vecCols;
|
| 116 |
+
let logD = log(finalD);
|
| 117 |
+
for (var c = lane; c < params.vecCols; c += LANES_PER_ROW) {
|
| 118 |
+
let v = vec4<f32>(x[rowBase + c]);
|
| 119 |
+
var o = vec4<f32>(
|
| 120 |
+
shifted_value(v.x, finalM),
|
| 121 |
+
shifted_value(v.y, finalM),
|
| 122 |
+
shifted_value(v.z, finalM),
|
| 123 |
+
shifted_value(v.w, finalM)) - logD;
|
| 124 |
+
if (rowIsNan) {
|
| 125 |
+
o = vec4<f32>(nanOut);
|
| 126 |
+
}
|
| 127 |
+
y[rowBase + c] = vec4<{{ scalar }}>(o);
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
}
|
build/webgpu/softmax-online.wgsl.jinja
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% if usesF16 %}
|
| 2 |
+
enable f16;
|
| 3 |
+
{% endif %}
|
| 4 |
+
{% if combineSubgroups %}
|
| 5 |
+
enable subgroups;
|
| 6 |
+
{% endif %}
|
| 7 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 8 |
+
|
| 9 |
+
// Workgroup-level online softmax / log-softmax over one contiguous last-axis
|
| 10 |
+
// row per workgroup. Each thread keeps an online (max, denom) pair over its
|
| 11 |
+
// strided chunk, partials merge with the online-softmax rule (see
|
| 12 |
+
// the shared online-softmax combiner) and a fused second sweep writes
|
| 13 |
+
// the result. The epilogue is either softmax or log-softmax, and vec4 row access
|
| 14 |
+
// is used when rows are vectorized.
|
| 15 |
+
const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 16 |
+
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 17 |
+
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
| 18 |
+
// accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
|
| 19 |
+
// a zero final denominator according to their public semantics. Using -inf
|
| 20 |
+
// here changes +inf-row behavior.
|
| 21 |
+
const FLT_MAX: f32 = 3.4028234663852886e38;
|
| 22 |
+
|
| 23 |
+
fn is_finite_f32(value: f32) -> bool {
|
| 24 |
+
return select(false, value <= FLT_MAX, value >= -FLT_MAX);
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
// x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
|
| 28 |
+
// exactly at the row max. `x - x` on an infinite max is a legal fast-math
|
| 29 |
+
// fold to 0, which would silently turn +inf rows finite — the explicit
|
| 30 |
+
// equality test keeps the NaN propagation of the serial kernels.
|
| 31 |
+
fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
| 32 |
+
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 33 |
+
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 34 |
+
}
|
| 35 |
+
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 36 |
+
return exp(shifted_value(value, maxValue));
|
| 37 |
+
}
|
| 38 |
+
// Bit-exact NaN test: `v != v` can be folded to false by fast-math backends,
|
| 39 |
+
// and a constant NaN bit pattern is rejected at WGSL const-evaluation.
|
| 40 |
+
fn is_nan_f32(value: f32) -> bool {
|
| 41 |
+
let bits = bitcast<u32>(value);
|
| 42 |
+
return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
// Workgroup-cooperative merge of per-thread online-softmax (m, d) partials:
|
| 46 |
+
// mNew = max(m1, m2)
|
| 47 |
+
// dNew = d1 * exp(m1 - mNew) + d2 * exp(m2 - mNew)
|
| 48 |
+
// Both the subgroup and portable barrier-tree engines return the same merged
|
| 49 |
+
// pair to every invocation. Repeated merges require a workgroup barrier between
|
| 50 |
+
// calls before their shared partial storage is reused.
|
| 51 |
+
{% set combineSubgroups = combineSubgroups is defined and combineSubgroups %}
|
| 52 |
+
{% if combineSubgroups %}
|
| 53 |
+
// Per-subgroup partials are published into a deterministic slot: the subgroup's
|
| 54 |
+
// ordinal index within the workgroup (lidx / sgSize). The online (m, d) merge
|
| 55 |
+
// is not float-associative, so thread 0 must fold partials in a fixed order.
|
| 56 |
+
// Subgroups partition a workgroup into contiguous ordinal ranges on supported
|
| 57 |
+
// backends, so the ordinal slot is unique per subgroup and every slot in
|
| 58 |
+
// [0, subgroupCount) is written (each subgroup elects one leader).
|
| 59 |
+
// Sized for the worst case of one partial per invocation.
|
| 60 |
+
var<workgroup> partialM: array<f32, WG>;
|
| 61 |
+
var<workgroup> partialD: array<f32, WG>;
|
| 62 |
+
var<workgroup> combinedMD: vec2<f32>;
|
| 63 |
+
|
| 64 |
+
// When the whole workgroup is one subgroup the subgroup reduce already covers
|
| 65 |
+
// it (no barriers, no shared state); otherwise subgroup leaders publish
|
| 66 |
+
// partials through shared memory and thread 0 folds them in ordinal order.
|
| 67 |
+
fn combine_partials(m: f32, d: f32, lidx: u32, sgSize: u32) -> vec2<f32> {
|
| 68 |
+
let sgM = subgroupMax(m);
|
| 69 |
+
// A lane with no elements contributes d == 0 (exact identity). A +inf
|
| 70 |
+
// element made exp(inf - inf) = NaN stick in that lane's d; a NaN element
|
| 71 |
+
// landed in d via exp(NaN); both survive the merge and are detected by the
|
| 72 |
+
// code after the reduction.
|
| 73 |
+
let sgD = subgroupAdd(d * exp_shift(m, sgM));
|
| 74 |
+
if (sgSize == WG) {
|
| 75 |
+
return vec2<f32>(sgM, sgD);
|
| 76 |
+
}
|
| 77 |
+
let subgroupCount = (WG + sgSize - 1u) / sgSize;
|
| 78 |
+
// Pre-seed every fold slot with the (max, denom) identity. The fold below reads a
|
| 79 |
+
// fixed subgroupCount slots in ordinal order (for determinism), but a slot whose
|
| 80 |
+
// subgroup elects no leader this call — e.g. a fully out-of-window key tile in the
|
| 81 |
+
// flash-attention loop that re-uses this shared memory each iteration — would
|
| 82 |
+
// otherwise read stale shared memory. Identity makes such a slot a no-op.
|
| 83 |
+
// (max identity = -FLT_MAX, denom identity = 0.)
|
| 84 |
+
if (lidx < subgroupCount) {
|
| 85 |
+
partialM[lidx] = -FLT_MAX;
|
| 86 |
+
partialD[lidx] = 0.0;
|
| 87 |
+
}
|
| 88 |
+
workgroupBarrier();
|
| 89 |
+
if (subgroupElect()) {
|
| 90 |
+
let slot = lidx / sgSize;
|
| 91 |
+
partialM[slot] = sgM;
|
| 92 |
+
partialD[slot] = sgD;
|
| 93 |
+
}
|
| 94 |
+
workgroupBarrier();
|
| 95 |
+
if (lidx == 0u) {
|
| 96 |
+
var accM = -FLT_MAX;
|
| 97 |
+
var accD = 0.0;
|
| 98 |
+
for (var i = 0u; i < subgroupCount; i = i + 1u) {
|
| 99 |
+
let mNew = max(accM, partialM[i]);
|
| 100 |
+
accD = accD * exp_shift(accM, mNew) + partialD[i] * exp_shift(partialM[i], mNew);
|
| 101 |
+
accM = mNew;
|
| 102 |
+
}
|
| 103 |
+
combinedMD = vec2<f32>(accM, accD);
|
| 104 |
+
}
|
| 105 |
+
workgroupBarrier();
|
| 106 |
+
return combinedMD;
|
| 107 |
+
}
|
| 108 |
+
{% else %}
|
| 109 |
+
{% set mdStreamed = mdStreams is defined %}
|
| 110 |
+
{% set mdStreams = mdStreams if mdStreams is defined else 1 %}
|
| 111 |
+
{% set mdExtent = "WG" if mdStreams == 1 else "WG * " ~ mdStreams ~ "u" %}
|
| 112 |
+
var<workgroup> partialM: array<f32, {{ mdExtent }}>;
|
| 113 |
+
var<workgroup> partialD: array<f32, {{ mdExtent }}>;
|
| 114 |
+
{% if mdStreamed %}
|
| 115 |
+
|
| 116 |
+
// In-place fold of {{ mdStreams }} streams. The caller stores its per-thread
|
| 117 |
+
// partials into partialM/partialD first and reads the merged pair of stream s
|
| 118 |
+
// from slot s * WG afterwards.
|
| 119 |
+
fn combine_partials_streams(lidx: u32) {
|
| 120 |
+
workgroupBarrier();
|
| 121 |
+
var stride = WG / 2u;
|
| 122 |
+
loop {
|
| 123 |
+
if (stride == 0u) {
|
| 124 |
+
break;
|
| 125 |
+
}
|
| 126 |
+
if (lidx < stride) {
|
| 127 |
+
{% for s in range(mdStreams) %}
|
| 128 |
+
{
|
| 129 |
+
let slot = {{ s }}u * WG + lidx;
|
| 130 |
+
let m1 = partialM[slot];
|
| 131 |
+
let d1 = partialD[slot];
|
| 132 |
+
let m2 = partialM[slot + stride];
|
| 133 |
+
let d2 = partialD[slot + stride];
|
| 134 |
+
let mNew = max(m1, m2);
|
| 135 |
+
partialD[slot] = d1 * exp_shift(m1, mNew) + d2 * exp_shift(m2, mNew);
|
| 136 |
+
partialM[slot] = mNew;
|
| 137 |
+
}
|
| 138 |
+
{% endfor %}
|
| 139 |
+
}
|
| 140 |
+
workgroupBarrier();
|
| 141 |
+
stride = stride / 2u;
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
{% else %}
|
| 145 |
+
|
| 146 |
+
fn combine_partials(m: f32, d: f32, lidx: u32) -> vec2<f32> {
|
| 147 |
+
partialM[lidx] = m;
|
| 148 |
+
partialD[lidx] = d;
|
| 149 |
+
workgroupBarrier();
|
| 150 |
+
var stride = WG / 2u;
|
| 151 |
+
loop {
|
| 152 |
+
if (stride == 0u) {
|
| 153 |
+
break;
|
| 154 |
+
}
|
| 155 |
+
if (lidx < stride) {
|
| 156 |
+
let m1 = partialM[lidx];
|
| 157 |
+
let d1 = partialD[lidx];
|
| 158 |
+
let m2 = partialM[lidx + stride];
|
| 159 |
+
let d2 = partialD[lidx + stride];
|
| 160 |
+
let mNew = max(m1, m2);
|
| 161 |
+
partialD[lidx] = d1 * exp_shift(m1, mNew) + d2 * exp_shift(m2, mNew);
|
| 162 |
+
partialM[lidx] = mNew;
|
| 163 |
+
}
|
| 164 |
+
workgroupBarrier();
|
| 165 |
+
stride = stride / 2u;
|
| 166 |
+
}
|
| 167 |
+
let merged = vec2<f32>(partialM[0], partialD[0]);
|
| 168 |
+
// Trailing barrier so back-to-back calls cannot race a next call's partial
|
| 169 |
+
// stores against this call's reads of slot 0.
|
| 170 |
+
workgroupBarrier();
|
| 171 |
+
return merged;
|
| 172 |
+
}
|
| 173 |
+
{% endif %}
|
| 174 |
+
{% endif %}
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 178 |
+
fn main(@builtin(workgroup_id) wid: vec3<u32>,
|
| 179 |
+
@builtin(num_workgroups) nwg: vec3<u32>,
|
| 180 |
+
@builtin(local_invocation_id) lid: vec3<u32>{% if combineSubgroups %},
|
| 181 |
+
@builtin(subgroup_size) sgSize: u32{% endif %}) {
|
| 182 |
+
// 2D-folded row index: wid.y carries the high bits past the
|
| 183 |
+
// maxComputeWorkgroupsPerDimension dispatch limit (rows > maxComputeWorkgroupsPerDimension).
|
| 184 |
+
let row = wid.x + wid.y * nwg.x;
|
| 185 |
+
if (row >= params.rows) {
|
| 186 |
+
return;
|
| 187 |
+
}
|
| 188 |
+
{% if source.useVec4 %}
|
| 189 |
+
let rowBase = row * params.vecCols;
|
| 190 |
+
{% else %}
|
| 191 |
+
let rowBase = row * params.cols;
|
| 192 |
+
{% endif %}
|
| 193 |
+
|
| 194 |
+
// Thread-local online (m, d) over a strided chunk; f32 accumulation.
|
| 195 |
+
var m = -FLT_MAX;
|
| 196 |
+
var d = 0.0;
|
| 197 |
+
{% if source.useVec4 %}
|
| 198 |
+
for (var c = lid.x; c < params.vecCols; c = c + WG) {
|
| 199 |
+
let v = vec4<f32>(x[rowBase + c]);
|
| 200 |
+
let mNew = max(m, max(max(v.x, v.y), max(v.z, v.w)));
|
| 201 |
+
d = d * exp_shift(m, mNew)
|
| 202 |
+
+ exp_shift(v.x, mNew) + exp_shift(v.y, mNew) + exp_shift(v.z, mNew) + exp_shift(v.w, mNew);
|
| 203 |
+
m = mNew;
|
| 204 |
+
}
|
| 205 |
+
{% else %}
|
| 206 |
+
for (var c = lid.x; c < params.cols; c = c + WG) {
|
| 207 |
+
let v = f32(x[rowBase + c]);
|
| 208 |
+
let mNew = max(m, v);
|
| 209 |
+
d = d * exp_shift(m, mNew) + exp_shift(v, mNew);
|
| 210 |
+
m = mNew;
|
| 211 |
+
}
|
| 212 |
+
{% endif %}
|
| 213 |
+
|
| 214 |
+
let rowMD = combine_partials(m, d, lid.x{% if combineSubgroups %}, sgSize{% endif %});
|
| 215 |
+
let finalM = rowMD.x;
|
| 216 |
+
let finalD = rowMD.y;
|
| 217 |
+
|
| 218 |
+
// Runtime-derived quiet NaN + bit-exact NaN test, as in the 1-pass kernel:
|
| 219 |
+
// `m - m` (Inf - Inf) and `d != d` are both legal fast-math folds (to 0 and
|
| 220 |
+
// false), and a constant NaN bit pattern is rejected at WGSL
|
| 221 |
+
// const-evaluation.
|
| 222 |
+
let dBits = bitcast<u32>(finalD);
|
| 223 |
+
let rowIsNan = finalM > FLT_MAX || is_nan_f32(finalD) || finalD == 0.0;
|
| 224 |
+
let nanOut = bitcast<f32>(dBits | 0x7fc00000u);
|
| 225 |
+
|
| 226 |
+
// (x - m) - log(d). x == m gives exactly 0 - log(d) (IEEE subtraction of
|
| 227 |
+
// equal finite values is +0), matching the serial kernel's shift trick.
|
| 228 |
+
let logD = log(finalD);
|
| 229 |
+
{% if source.useVec4 %}
|
| 230 |
+
for (var c = lid.x; c < params.vecCols; c = c + WG) {
|
| 231 |
+
let v = vec4<f32>(x[rowBase + c]);
|
| 232 |
+
var o = vec4<f32>(
|
| 233 |
+
shifted_value(v.x, finalM),
|
| 234 |
+
shifted_value(v.y, finalM),
|
| 235 |
+
shifted_value(v.z, finalM),
|
| 236 |
+
shifted_value(v.w, finalM)) - logD;
|
| 237 |
+
if (rowIsNan) {
|
| 238 |
+
o = vec4<f32>(nanOut);
|
| 239 |
+
}
|
| 240 |
+
y[rowBase + c] = vec4<{{ scalar }}>(o);
|
| 241 |
+
}
|
| 242 |
+
{% else %}
|
| 243 |
+
for (var c = lid.x; c < params.cols; c = c + WG) {
|
| 244 |
+
let v = f32(x[rowBase + c]);
|
| 245 |
+
var o = shifted_value(v, finalM) - logD;
|
| 246 |
+
if (rowIsNan) {
|
| 247 |
+
o = nanOut;
|
| 248 |
+
}
|
| 249 |
+
y[rowBase + c] = {{ scalar }}(o);
|
| 250 |
+
}
|
| 251 |
+
{% endif %}
|
| 252 |
+
}
|
build/webgpu/softmax-row-stage-strided-vec4.wgsl.jinja
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Vec4-over-inner specialization for passes 1 and 2 of strided three-pass
|
| 2 |
+
// softmax. A strided row reduces REDUCE_SIZE elements at INNER_SIZE stride, so
|
| 3 |
+
// adjacent scalar threads would read non-coalesced locations. When INNER_SIZE
|
| 4 |
+
// is divisible by four, four adjacent inner positions remain contiguous at
|
| 5 |
+
// every axis coordinate. One workgroup therefore handles four rows packed as a
|
| 6 |
+
// vec4: one coalesced 128-bit access per axis step through one reduction tree.
|
| 7 |
+
// rowMax and rowSum use the same bytes the scalar normalize pass reads.
|
| 8 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 9 |
+
|
| 10 |
+
const WGS: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 11 |
+
{% set stridedAxisUses = ["REDUCE_SIZE", "INNER_SIZE", "STRIDED_ROWS"] %}
|
| 12 |
+
// Compiled indexing for row-wise reductions over a non-last axis. A row is one
|
| 13 |
+
// slice along the reduction axis, indexed as outer * INNER_SIZE + inner.
|
| 14 |
+
{% set reduce_size = source.xShape[source.axis] %}
|
| 15 |
+
{% set inner_size = namespace(value=1) %}
|
| 16 |
+
{% for i in range(source.axis + 1, source.xShape | length) %}
|
| 17 |
+
{% set inner_size.value = inner_size.value * source.xShape[i] %}
|
| 18 |
+
{% endfor %}
|
| 19 |
+
{% set outer_size = namespace(value=1) %}
|
| 20 |
+
{% for i in range(source.axis) %}
|
| 21 |
+
{% set outer_size.value = outer_size.value * source.xShape[i] %}
|
| 22 |
+
{% endfor %}
|
| 23 |
+
{% set usesAll = stridedAxisUses is not defined %}
|
| 24 |
+
{% set usesElementOffset = usesAll or "element_offset" in stridedAxisUses %}
|
| 25 |
+
{% set usesRowForOffset = usesAll or "row_for_offset" in stridedAxisUses %}
|
| 26 |
+
{% set usesReduceSize = usesAll or usesElementOffset or "REDUCE_SIZE" in stridedAxisUses %}
|
| 27 |
+
{% set usesInnerSize = usesAll or usesElementOffset or "INNER_SIZE" in stridedAxisUses %}
|
| 28 |
+
{% set usesStridedRows = usesAll or "STRIDED_ROWS" in stridedAxisUses %}
|
| 29 |
+
{% set usesInnerSizeSafe = usesAll or usesElementOffset or usesRowForOffset or "INNER_SIZE_SAFE" in stridedAxisUses %}
|
| 30 |
+
{% set usesAxisStrideSafe = usesAll or usesRowForOffset or "AXIS_STRIDE_SAFE" in stridedAxisUses %}
|
| 31 |
+
{% if usesReduceSize %}
|
| 32 |
+
const REDUCE_SIZE: u32 = {{ reduce_size }}u;
|
| 33 |
+
{% endif %}
|
| 34 |
+
{% if usesInnerSize %}
|
| 35 |
+
const INNER_SIZE: u32 = {{ inner_size.value }}u;
|
| 36 |
+
{% endif %}
|
| 37 |
+
{% if usesStridedRows %}
|
| 38 |
+
// Total one-per-row workgroup count (product of every non-axis dim). Lets the
|
| 39 |
+
// row-wise reduce kernels 2D-fold their dispatch past the maxComputeWorkgroupsPerDimension limit and guard
|
| 40 |
+
// the over-dispatched tail without a runtime `params` uniform.
|
| 41 |
+
const STRIDED_ROWS: u32 = {{ outer_size.value * inner_size.value }}u;
|
| 42 |
+
{% endif %}
|
| 43 |
+
{% if usesInnerSizeSafe %}
|
| 44 |
+
const INNER_SIZE_SAFE: u32 = {{ inner_size.value if inner_size.value > 0 else 1 }}u;
|
| 45 |
+
{% endif %}
|
| 46 |
+
{% if usesAxisStrideSafe %}
|
| 47 |
+
const AXIS_STRIDE_SAFE: u32 = {{ (reduce_size * inner_size.value) if (reduce_size * inner_size.value) > 0 else 1 }}u;
|
| 48 |
+
{% endif %}
|
| 49 |
+
|
| 50 |
+
{% if usesElementOffset %}
|
| 51 |
+
// Flat offset of element k (along the reduce axis) of `row`.
|
| 52 |
+
fn element_offset(row: u32, k: u32) -> u32 {
|
| 53 |
+
let outer = row / INNER_SIZE_SAFE;
|
| 54 |
+
let inner = row % INNER_SIZE_SAFE;
|
| 55 |
+
return outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
{% endif %}
|
| 59 |
+
{% if usesRowForOffset %}
|
| 60 |
+
// Row that owns the flat element `offset` (inverse of element_offset over
|
| 61 |
+
// the non-axis coordinates).
|
| 62 |
+
fn row_for_offset(offset: u32) -> u32 {
|
| 63 |
+
let outer = offset / AXIS_STRIDE_SAFE;
|
| 64 |
+
let inner = offset % INNER_SIZE_SAFE;
|
| 65 |
+
return outer * INNER_SIZE_SAFE + inner;
|
| 66 |
+
}
|
| 67 |
+
{%- endif -%}
|
| 68 |
+
{% if source.stage == "max" %}
|
| 69 |
+
// max(1u,..) so an empty strided tensor (INNER_SIZE 0) still compiles — the kernel
|
| 70 |
+
// returns before any divide (VEC4_ROWS 0 -> guard), this only avoids a const `/0`.
|
| 71 |
+
const INNER4: u32 = max(1u, INNER_SIZE / 4u); // vec4 groups of inner positions per outer
|
| 72 |
+
const STRIDE4: u32 = INNER_SIZE / 4u; // vec4 stride along the reduce axis
|
| 73 |
+
const VEC4_ROWS: u32 = STRIDED_ROWS / 4u; // = outer_size * INNER4
|
| 74 |
+
{% else %}
|
| 75 |
+
// max(1u,..) so an empty strided tensor (INNER_SIZE 0) still compiles (kernel
|
| 76 |
+
// returns before any divide; only avoids a const `/0`).
|
| 77 |
+
const INNER4: u32 = max(1u, INNER_SIZE / 4u);
|
| 78 |
+
const STRIDE4: u32 = INNER_SIZE / 4u;
|
| 79 |
+
const VEC4_ROWS: u32 = STRIDED_ROWS / 4u;
|
| 80 |
+
{% endif %}
|
| 81 |
+
|
| 82 |
+
var<workgroup> partial: array<vec4<f32>, WGS>;
|
| 83 |
+
{% set reduce_fn = "reduce_max_vec4" if source.stage == "max" else "reduce_add_vec4" %}
|
| 84 |
+
fn {{ reduce_fn }}(value: vec4<f32>, tid: u32) -> vec4<f32> {
|
| 85 |
+
partial[tid] = value;
|
| 86 |
+
workgroupBarrier();
|
| 87 |
+
var stride: u32 = WGS / 2u;
|
| 88 |
+
loop {
|
| 89 |
+
if (stride == 0u) { break; }
|
| 90 |
+
{% if source.stage == "max" %}
|
| 91 |
+
if (tid < stride) { partial[tid] = max(partial[tid], partial[tid + stride]); }
|
| 92 |
+
{% else %}
|
| 93 |
+
if (tid < stride) { partial[tid] = partial[tid] + partial[tid + stride]; }
|
| 94 |
+
{% endif %}
|
| 95 |
+
stride = stride / 2u;
|
| 96 |
+
workgroupBarrier();
|
| 97 |
+
}
|
| 98 |
+
return partial[0];
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
@compute @workgroup_size(WGS)
|
| 102 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>,
|
| 103 |
+
@builtin(num_workgroups) nwg: vec3<u32>) {
|
| 104 |
+
let rg = wg.x + wg.y * nwg.x; // vec4-row-group index
|
| 105 |
+
if (rg >= VEC4_ROWS) { return; }
|
| 106 |
+
let outer = rg / INNER4;
|
| 107 |
+
let inner_group = rg % INNER4;
|
| 108 |
+
{% if source.stage == "max" %}
|
| 109 |
+
// vec4 index of element k=0 for this group (offset is a multiple of 4 since
|
| 110 |
+
// INNER_SIZE % 4 == 0 and inner_group*4 is too).
|
| 111 |
+
{% endif %}
|
| 112 |
+
let base4 = (outer * REDUCE_SIZE * INNER_SIZE + inner_group * 4u) / 4u;
|
| 113 |
+
|
| 114 |
+
{% if source.stage == "max" %}
|
| 115 |
+
var acc = vec4<f32>(-3.4028234663852886e38);
|
| 116 |
+
for (var k = lid.x; k < REDUCE_SIZE; k = k + WGS) {
|
| 117 |
+
acc = max(acc, x[base4 + k * STRIDE4]);
|
| 118 |
+
}
|
| 119 |
+
let rowM = reduce_max_vec4(acc, lid.x);
|
| 120 |
+
if (lid.x == 0u) { rowMax[rg] = rowM; }
|
| 121 |
+
{% else %}
|
| 122 |
+
let m = rowMax[rg];
|
| 123 |
+
// +Inf row max -> NaN row (ONNX). Derive the quiet NaN per-component from the
|
| 124 |
+
// runtime bits (a constant NaN pattern is rejected at const-eval); m - m folds
|
| 125 |
+
// to 0 for finite m so the exp arg stays correct on the non-Inf lanes.
|
| 126 |
+
let nanv = bitcast<vec4<f32>>(bitcast<vec4<u32>>(m) | vec4<u32>(0x7fc00000u));
|
| 127 |
+
let isInf = m > vec4<f32>(3.4028234663852886e38);
|
| 128 |
+
var acc = vec4<f32>(0.0);
|
| 129 |
+
for (var k = lid.x; k < REDUCE_SIZE; k = k + WGS) {
|
| 130 |
+
let idx = base4 + k * STRIDE4;
|
| 131 |
+
let e = select(exp(x[idx] - m), nanv, isInf);
|
| 132 |
+
y[idx] = e;
|
| 133 |
+
acc = acc + e;
|
| 134 |
+
}
|
| 135 |
+
let rowSumValue = reduce_add_vec4(acc, lid.x);
|
| 136 |
+
if (lid.x == 0u) { rowSum[rg] = rowSumValue; }
|
| 137 |
+
{% endif %}
|
| 138 |
+
}
|
build/webgpu/softmax-row-stage.wgsl.jinja
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// First and second passes of the three-pass softmax, with one workgroup per
|
| 2 |
+
// row. The max stage reduces the row maximum. The exp_sum stage writes
|
| 3 |
+
// exp(x - rowMax) and reduces the row denominator.
|
| 4 |
+
{% if T == "f16" %}
|
| 5 |
+
enable f16;
|
| 6 |
+
{% endif %}
|
| 7 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 8 |
+
|
| 9 |
+
const WGS: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 10 |
+
{% if source.strided %}
|
| 11 |
+
{% set stridedAxisUses = ["STRIDED_ROWS", "element_offset"] %}
|
| 12 |
+
// Compiled indexing for row-wise reductions over a non-last axis. A row is one
|
| 13 |
+
// slice along the reduction axis, indexed as outer * INNER_SIZE + inner.
|
| 14 |
+
{% set reduce_size = source.xShape[source.axis] %}
|
| 15 |
+
{% set inner_size = namespace(value=1) %}
|
| 16 |
+
{% for i in range(source.axis + 1, source.xShape | length) %}
|
| 17 |
+
{% set inner_size.value = inner_size.value * source.xShape[i] %}
|
| 18 |
+
{% endfor %}
|
| 19 |
+
{% set outer_size = namespace(value=1) %}
|
| 20 |
+
{% for i in range(source.axis) %}
|
| 21 |
+
{% set outer_size.value = outer_size.value * source.xShape[i] %}
|
| 22 |
+
{% endfor %}
|
| 23 |
+
{% set usesAll = stridedAxisUses is not defined %}
|
| 24 |
+
{% set usesElementOffset = usesAll or "element_offset" in stridedAxisUses %}
|
| 25 |
+
{% set usesRowForOffset = usesAll or "row_for_offset" in stridedAxisUses %}
|
| 26 |
+
{% set usesReduceSize = usesAll or usesElementOffset or "REDUCE_SIZE" in stridedAxisUses %}
|
| 27 |
+
{% set usesInnerSize = usesAll or usesElementOffset or "INNER_SIZE" in stridedAxisUses %}
|
| 28 |
+
{% set usesStridedRows = usesAll or "STRIDED_ROWS" in stridedAxisUses %}
|
| 29 |
+
{% set usesInnerSizeSafe = usesAll or usesElementOffset or usesRowForOffset or "INNER_SIZE_SAFE" in stridedAxisUses %}
|
| 30 |
+
{% set usesAxisStrideSafe = usesAll or usesRowForOffset or "AXIS_STRIDE_SAFE" in stridedAxisUses %}
|
| 31 |
+
{% if usesReduceSize %}
|
| 32 |
+
const REDUCE_SIZE: u32 = {{ reduce_size }}u;
|
| 33 |
+
{% endif %}
|
| 34 |
+
{% if usesInnerSize %}
|
| 35 |
+
const INNER_SIZE: u32 = {{ inner_size.value }}u;
|
| 36 |
+
{% endif %}
|
| 37 |
+
{% if usesStridedRows %}
|
| 38 |
+
// Total one-per-row workgroup count (product of every non-axis dim). Lets the
|
| 39 |
+
// row-wise reduce kernels 2D-fold their dispatch past the maxComputeWorkgroupsPerDimension limit and guard
|
| 40 |
+
// the over-dispatched tail without a runtime `params` uniform.
|
| 41 |
+
const STRIDED_ROWS: u32 = {{ outer_size.value * inner_size.value }}u;
|
| 42 |
+
{% endif %}
|
| 43 |
+
{% if usesInnerSizeSafe %}
|
| 44 |
+
const INNER_SIZE_SAFE: u32 = {{ inner_size.value if inner_size.value > 0 else 1 }}u;
|
| 45 |
+
{% endif %}
|
| 46 |
+
{% if usesAxisStrideSafe %}
|
| 47 |
+
const AXIS_STRIDE_SAFE: u32 = {{ (reduce_size * inner_size.value) if (reduce_size * inner_size.value) > 0 else 1 }}u;
|
| 48 |
+
{% endif %}
|
| 49 |
+
|
| 50 |
+
{% if usesElementOffset %}
|
| 51 |
+
// Flat offset of element k (along the reduce axis) of `row`.
|
| 52 |
+
fn element_offset(row: u32, k: u32) -> u32 {
|
| 53 |
+
let outer = row / INNER_SIZE_SAFE;
|
| 54 |
+
let inner = row % INNER_SIZE_SAFE;
|
| 55 |
+
return outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
{% endif %}
|
| 59 |
+
{% if usesRowForOffset %}
|
| 60 |
+
// Row that owns the flat element `offset` (inverse of element_offset over
|
| 61 |
+
// the non-axis coordinates).
|
| 62 |
+
fn row_for_offset(offset: u32) -> u32 {
|
| 63 |
+
let outer = offset / AXIS_STRIDE_SAFE;
|
| 64 |
+
let inner = offset % INNER_SIZE_SAFE;
|
| 65 |
+
return outer * INNER_SIZE_SAFE + inner;
|
| 66 |
+
}
|
| 67 |
+
{%- endif -%}
|
| 68 |
+
{% endif %}
|
| 69 |
+
{% macro wgsl_tree_reduce_f32(name, mode, buffer="partial", wg="WG", trailingBarrier=true) %}
|
| 70 |
+
fn {{ name }}(value: f32, tid: u32) -> f32 {
|
| 71 |
+
{{ buffer }}[tid] = value;
|
| 72 |
+
workgroupBarrier();
|
| 73 |
+
// Ceil-halving keeps every lane when the workgroup size is not a power of
|
| 74 |
+
// two. For even n this matches the power-of-two tree order; for odd n, lanes
|
| 75 |
+
// [0, n-half) fold the upper tail while the middle lane carries forward.
|
| 76 |
+
var n: u32 = {{ wg }};
|
| 77 |
+
loop {
|
| 78 |
+
let half = (n + 1u) / 2u;
|
| 79 |
+
if (tid < n - half) {
|
| 80 |
+
{% if mode == "max" %}
|
| 81 |
+
{{ buffer }}[tid] = max({{ buffer }}[tid], {{ buffer }}[tid + half]);
|
| 82 |
+
{% else %}
|
| 83 |
+
{{ buffer }}[tid] = {{ buffer }}[tid] + {{ buffer }}[tid + half];
|
| 84 |
+
{% endif %}
|
| 85 |
+
}
|
| 86 |
+
workgroupBarrier();
|
| 87 |
+
n = half;
|
| 88 |
+
if (n == 1u) {
|
| 89 |
+
break;
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
// The default trailing barrier makes this helper safe for back-to-back calls: every lane reads
|
| 93 |
+
// slot 0 here, so the next call's first store must not run until all lanes have read it.
|
| 94 |
+
// `trailingBarrier=false` is safe only when the buffer is never written again before kernel exit.
|
| 95 |
+
let reduced = {{ buffer }}[0];
|
| 96 |
+
{% if trailingBarrier %}
|
| 97 |
+
workgroupBarrier();
|
| 98 |
+
{% endif %}
|
| 99 |
+
return reduced;
|
| 100 |
+
}
|
| 101 |
+
{% endmacro %}
|
| 102 |
+
|
| 103 |
+
var<workgroup> partial: array<f32, WGS>;
|
| 104 |
+
{% if source.stage == "max" %}
|
| 105 |
+
{{ wgsl_tree_reduce_f32("reduce_max_f32", "max", "partial", "WGS") }}
|
| 106 |
+
{% else %}
|
| 107 |
+
{{ wgsl_tree_reduce_f32("reduce_add_f32", "add", "partial", "WGS") }}
|
| 108 |
+
{% endif %}
|
| 109 |
+
{% set count = "REDUCE_SIZE" if source.strided else "params.cols" %}
|
| 110 |
+
{% set offset_of = "element_offset(row, k)" if source.strided else "row * params.cols + k" %}
|
| 111 |
+
|
| 112 |
+
@compute @workgroup_size(WGS)
|
| 113 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>,
|
| 114 |
+
@builtin(num_workgroups) nwg: vec3<u32>) {
|
| 115 |
+
{% if source.strided %}
|
| 116 |
+
// Strided mode has no `params` uniform (offsets come from compile-time
|
| 117 |
+
// constants), so it folds against the compile-time STRIDED_ROWS instead of
|
| 118 |
+
// params.rows. wg.y carries the high bits of the row past the maxComputeWorkgroupsPerDimension limit;
|
| 119 |
+
{% if source.stage == "max" %}
|
| 120 |
+
// the guard drops the over-dispatched tail before any rowMax[row] store.
|
| 121 |
+
{% else %}
|
| 122 |
+
// the guard drops the over-dispatched tail before any rowSum[row]/y[offset]
|
| 123 |
+
// store.
|
| 124 |
+
{% endif %}
|
| 125 |
+
let row = wg.x + wg.y * nwg.x;
|
| 126 |
+
if (row >= STRIDED_ROWS) { return; }
|
| 127 |
+
{% else %}
|
| 128 |
+
// 2D-folded row index: wg.y carries the high bits past the maxComputeWorkgroupsPerDimension
|
| 129 |
+
{% if source.stage == "max" %}
|
| 130 |
+
// workgroup-per-dimension dispatch limit (rows > maxComputeWorkgroupsPerDimension). The guard returns
|
| 131 |
+
// the whole over-dispatched workgroup before any read/write — without it the
|
| 132 |
+
// clamped OOB rowMax[row] store would corrupt the last real row. wg.x/wg.y/
|
| 133 |
+
{% else %}
|
| 134 |
+
// workgroup-per-dimension dispatch limit (rows > maxComputeWorkgroupsPerDimension). Return the whole
|
| 135 |
+
// over-dispatched workgroup before any read/write — without it the clamped
|
| 136 |
+
// OOB rowSum[row]/y[offset] stores would corrupt the last real row. wg.x/wg.y/
|
| 137 |
+
{% endif %}
|
| 138 |
+
// nwg.x are workgroup-uniform, so the return cannot diverge across a barrier.
|
| 139 |
+
let row = wg.x + wg.y * nwg.x;
|
| 140 |
+
if (row >= params.rows) { return; }
|
| 141 |
+
{% endif %}
|
| 142 |
+
{% if source.stage == "max" %}
|
| 143 |
+
var acc = -3.4028234663852886e38;
|
| 144 |
+
for (var k = lid.x; k < {{ count }}; k = k + WGS) {
|
| 145 |
+
acc = max(acc, f32(x[{{ offset_of }}]));
|
| 146 |
+
}
|
| 147 |
+
let rowM = reduce_max_f32(acc, lid.x);
|
| 148 |
+
if (lid.x == 0u) { rowMax[row] = rowM; }
|
| 149 |
+
{% else %}
|
| 150 |
+
let m = rowMax[row];
|
| 151 |
+
var acc = 0.0;
|
| 152 |
+
for (var k = lid.x; k < {{ count }}; k = k + WGS) {
|
| 153 |
+
let offset = {{ offset_of }};
|
| 154 |
+
// +Inf row max must yield NaN (ONNX semantics). `m - m` (Inf - Inf) is
|
| 155 |
+
// folded to 0 by fast-math backends, and a constant NaN bit pattern is
|
| 156 |
+
// rejected at WGSL const-evaluation — so derive the quiet NaN from the
|
| 157 |
+
// runtime value (any bits | 0x7fc00000 is a NaN).
|
| 158 |
+
let e = select(exp(f32(x[offset]) - m), bitcast<f32>(bitcast<u32>(m) | 0x7fc00000u), m > 3.4028234663852886e38);
|
| 159 |
+
y[offset] = {{ T }}(e);
|
| 160 |
+
acc = acc + e;
|
| 161 |
+
}
|
| 162 |
+
let rowSumValue = reduce_add_f32(acc, lid.x);
|
| 163 |
+
if (lid.x == 0u) { rowSum[row] = rowSumValue; }
|
| 164 |
+
{% endif %}
|
| 165 |
+
}
|
build/webgpu/softmax-strided-online-lane.wgsl.jinja
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Scratch-free online softmax/log-softmax for a non-last reduction axis. One
|
| 2 |
+
// invocation owns one (outer, inner) row, so neighboring lanes make coalesced
|
| 3 |
+
// reads at every reduction step. This is both the large-inner fast path and the
|
| 4 |
+
// capacity-safe fallback when row-stat scratch cannot be allocated.
|
| 5 |
+
{% set operation = source.op if source.op is defined else "softmax" %}
|
| 6 |
+
{% if T == "f16" %}
|
| 7 |
+
enable f16;
|
| 8 |
+
{% endif %}
|
| 9 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 10 |
+
{% set stridedAxisUses = ["STRIDED_ROWS", "element_offset"] %}
|
| 11 |
+
// Compiled indexing for row-wise reductions over a non-last axis. A row is one
|
| 12 |
+
// slice along the reduction axis, indexed as outer * INNER_SIZE + inner.
|
| 13 |
+
{% set reduce_size = source.xShape[source.axis] %}
|
| 14 |
+
{% set inner_size = namespace(value=1) %}
|
| 15 |
+
{% for i in range(source.axis + 1, source.xShape | length) %}
|
| 16 |
+
{% set inner_size.value = inner_size.value * source.xShape[i] %}
|
| 17 |
+
{% endfor %}
|
| 18 |
+
{% set outer_size = namespace(value=1) %}
|
| 19 |
+
{% for i in range(source.axis) %}
|
| 20 |
+
{% set outer_size.value = outer_size.value * source.xShape[i] %}
|
| 21 |
+
{% endfor %}
|
| 22 |
+
{% set usesAll = stridedAxisUses is not defined %}
|
| 23 |
+
{% set usesElementOffset = usesAll or "element_offset" in stridedAxisUses %}
|
| 24 |
+
{% set usesRowForOffset = usesAll or "row_for_offset" in stridedAxisUses %}
|
| 25 |
+
{% set usesReduceSize = usesAll or usesElementOffset or "REDUCE_SIZE" in stridedAxisUses %}
|
| 26 |
+
{% set usesInnerSize = usesAll or usesElementOffset or "INNER_SIZE" in stridedAxisUses %}
|
| 27 |
+
{% set usesStridedRows = usesAll or "STRIDED_ROWS" in stridedAxisUses %}
|
| 28 |
+
{% set usesInnerSizeSafe = usesAll or usesElementOffset or usesRowForOffset or "INNER_SIZE_SAFE" in stridedAxisUses %}
|
| 29 |
+
{% set usesAxisStrideSafe = usesAll or usesRowForOffset or "AXIS_STRIDE_SAFE" in stridedAxisUses %}
|
| 30 |
+
{% if usesReduceSize %}
|
| 31 |
+
const REDUCE_SIZE: u32 = {{ reduce_size }}u;
|
| 32 |
+
{% endif %}
|
| 33 |
+
{% if usesInnerSize %}
|
| 34 |
+
const INNER_SIZE: u32 = {{ inner_size.value }}u;
|
| 35 |
+
{% endif %}
|
| 36 |
+
{% if usesStridedRows %}
|
| 37 |
+
// Total one-per-row workgroup count (product of every non-axis dim). Lets the
|
| 38 |
+
// row-wise reduce kernels 2D-fold their dispatch past the maxComputeWorkgroupsPerDimension limit and guard
|
| 39 |
+
// the over-dispatched tail without a runtime `params` uniform.
|
| 40 |
+
const STRIDED_ROWS: u32 = {{ outer_size.value * inner_size.value }}u;
|
| 41 |
+
{% endif %}
|
| 42 |
+
{% if usesInnerSizeSafe %}
|
| 43 |
+
const INNER_SIZE_SAFE: u32 = {{ inner_size.value if inner_size.value > 0 else 1 }}u;
|
| 44 |
+
{% endif %}
|
| 45 |
+
{% if usesAxisStrideSafe %}
|
| 46 |
+
const AXIS_STRIDE_SAFE: u32 = {{ (reduce_size * inner_size.value) if (reduce_size * inner_size.value) > 0 else 1 }}u;
|
| 47 |
+
{% endif %}
|
| 48 |
+
|
| 49 |
+
{% if usesElementOffset %}
|
| 50 |
+
// Flat offset of element k (along the reduce axis) of `row`.
|
| 51 |
+
fn element_offset(row: u32, k: u32) -> u32 {
|
| 52 |
+
let outer = row / INNER_SIZE_SAFE;
|
| 53 |
+
let inner = row % INNER_SIZE_SAFE;
|
| 54 |
+
return outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
{% endif %}
|
| 58 |
+
{% if usesRowForOffset %}
|
| 59 |
+
// Row that owns the flat element `offset` (inverse of element_offset over
|
| 60 |
+
// the non-axis coordinates).
|
| 61 |
+
fn row_for_offset(offset: u32) -> u32 {
|
| 62 |
+
let outer = offset / AXIS_STRIDE_SAFE;
|
| 63 |
+
let inner = offset % INNER_SIZE_SAFE;
|
| 64 |
+
return outer * INNER_SIZE_SAFE + inner;
|
| 65 |
+
}
|
| 66 |
+
{%- endif -%}
|
| 67 |
+
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 68 |
+
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
| 69 |
+
// accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
|
| 70 |
+
// a zero final denominator according to their public semantics. Using -inf
|
| 71 |
+
// here changes +inf-row behavior.
|
| 72 |
+
const FLT_MAX: f32 = 3.4028234663852886e38;
|
| 73 |
+
|
| 74 |
+
fn is_finite_f32(value: f32) -> bool {
|
| 75 |
+
return select(false, value <= FLT_MAX, value >= -FLT_MAX);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
// x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
|
| 79 |
+
// exactly at the row max. `x - x` on an infinite max is a legal fast-math
|
| 80 |
+
// fold to 0, which would silently turn +inf rows finite — the explicit
|
| 81 |
+
// equality test keeps the NaN propagation of the serial kernels.
|
| 82 |
+
fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
| 83 |
+
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 84 |
+
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
const WGS: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 88 |
+
@compute @workgroup_size(WGS)
|
| 89 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>,
|
| 90 |
+
@builtin(num_workgroups) nwg: vec3<u32>) {
|
| 91 |
+
let row = gid.x + gid.y * nwg.x * WGS;
|
| 92 |
+
if (row >= STRIDED_ROWS) { return; }
|
| 93 |
+
|
| 94 |
+
var row_max = -{{ "FLT_MAX" if operation == "logsoftmax" else "F32_MAX" }};
|
| 95 |
+
var row_sum = 0.0;
|
| 96 |
+
for (var k = 0u; k < REDUCE_SIZE; k += 1u) {
|
| 97 |
+
let value = f32(x[element_offset(row, k)]);
|
| 98 |
+
if (value > row_max) {
|
| 99 |
+
row_sum = row_sum * exp(row_max - value) + 1.0;
|
| 100 |
+
row_max = value;
|
| 101 |
+
} else {
|
| 102 |
+
row_sum += exp(value - row_max);
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
let sum_bits = bitcast<u32>(row_sum);
|
| 107 |
+
let sum_is_nan = (sum_bits & 0x7f800000u) == 0x7f800000u && (sum_bits & 0x007fffffu) != 0u;
|
| 108 |
+
for (var k = 0u; k < REDUCE_SIZE; k += 1u) {
|
| 109 |
+
let offset = element_offset(row, k);
|
| 110 |
+
var out = shifted_value(f32(x[offset]), row_max) - log(row_sum);
|
| 111 |
+
if (row_max > FLT_MAX || sum_is_nan) {
|
| 112 |
+
out = bitcast<f32>(bitcast<u32>(row_sum) | 0x7fc00000u);
|
| 113 |
+
}
|
| 114 |
+
y[offset] = {{ T }}(out);
|
| 115 |
+
}
|
| 116 |
+
}
|
build/webgpu/softmax-strided-packed4-tail.wgsl.jinja
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Online LogSoftmax for a non-last axis whose contiguous inner dimension has a
|
| 2 |
+
// one-to-three-element tail. Scalar bindings keep every outer slice legal even
|
| 3 |
+
// when its starting address is not vec4-aligned. Four adjacent inner rows share
|
| 4 |
+
// one workgroup and vec4 reduction tree, and uniform guards suppress invalid
|
| 5 |
+
// components of the final packed group.
|
| 6 |
+
{{ env.wgsl.resourceDeclarations }}
|
| 7 |
+
|
| 8 |
+
const WGS: u32 = {{ tunables.WORKGROUP_SIZE }}u;
|
| 9 |
+
{% set stridedAxisUses = ["REDUCE_SIZE", "INNER_SIZE", "STRIDED_ROWS", "INNER_SIZE_SAFE"] %}
|
| 10 |
+
// Compiled indexing for row-wise reductions over a non-last axis. A row is one
|
| 11 |
+
// slice along the reduction axis, indexed as outer * INNER_SIZE + inner.
|
| 12 |
+
{% set reduce_size = source.xShape[source.axis] %}
|
| 13 |
+
{% set inner_size = namespace(value=1) %}
|
| 14 |
+
{% for i in range(source.axis + 1, source.xShape | length) %}
|
| 15 |
+
{% set inner_size.value = inner_size.value * source.xShape[i] %}
|
| 16 |
+
{% endfor %}
|
| 17 |
+
{% set outer_size = namespace(value=1) %}
|
| 18 |
+
{% for i in range(source.axis) %}
|
| 19 |
+
{% set outer_size.value = outer_size.value * source.xShape[i] %}
|
| 20 |
+
{% endfor %}
|
| 21 |
+
{% set usesAll = stridedAxisUses is not defined %}
|
| 22 |
+
{% set usesElementOffset = usesAll or "element_offset" in stridedAxisUses %}
|
| 23 |
+
{% set usesRowForOffset = usesAll or "row_for_offset" in stridedAxisUses %}
|
| 24 |
+
{% set usesReduceSize = usesAll or usesElementOffset or "REDUCE_SIZE" in stridedAxisUses %}
|
| 25 |
+
{% set usesInnerSize = usesAll or usesElementOffset or "INNER_SIZE" in stridedAxisUses %}
|
| 26 |
+
{% set usesStridedRows = usesAll or "STRIDED_ROWS" in stridedAxisUses %}
|
| 27 |
+
{% set usesInnerSizeSafe = usesAll or usesElementOffset or usesRowForOffset or "INNER_SIZE_SAFE" in stridedAxisUses %}
|
| 28 |
+
{% set usesAxisStrideSafe = usesAll or usesRowForOffset or "AXIS_STRIDE_SAFE" in stridedAxisUses %}
|
| 29 |
+
{% if usesReduceSize %}
|
| 30 |
+
const REDUCE_SIZE: u32 = {{ reduce_size }}u;
|
| 31 |
+
{% endif %}
|
| 32 |
+
{% if usesInnerSize %}
|
| 33 |
+
const INNER_SIZE: u32 = {{ inner_size.value }}u;
|
| 34 |
+
{% endif %}
|
| 35 |
+
{% if usesStridedRows %}
|
| 36 |
+
// Total one-per-row workgroup count (product of every non-axis dim). Lets the
|
| 37 |
+
// row-wise reduce kernels 2D-fold their dispatch past the maxComputeWorkgroupsPerDimension limit and guard
|
| 38 |
+
// the over-dispatched tail without a runtime `params` uniform.
|
| 39 |
+
const STRIDED_ROWS: u32 = {{ outer_size.value * inner_size.value }}u;
|
| 40 |
+
{% endif %}
|
| 41 |
+
{% if usesInnerSizeSafe %}
|
| 42 |
+
const INNER_SIZE_SAFE: u32 = {{ inner_size.value if inner_size.value > 0 else 1 }}u;
|
| 43 |
+
{% endif %}
|
| 44 |
+
{% if usesAxisStrideSafe %}
|
| 45 |
+
const AXIS_STRIDE_SAFE: u32 = {{ (reduce_size * inner_size.value) if (reduce_size * inner_size.value) > 0 else 1 }}u;
|
| 46 |
+
{% endif %}
|
| 47 |
+
|
| 48 |
+
{% if usesElementOffset %}
|
| 49 |
+
// Flat offset of element k (along the reduce axis) of `row`.
|
| 50 |
+
fn element_offset(row: u32, k: u32) -> u32 {
|
| 51 |
+
let outer = row / INNER_SIZE_SAFE;
|
| 52 |
+
let inner = row % INNER_SIZE_SAFE;
|
| 53 |
+
return outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
{% endif %}
|
| 57 |
+
{% if usesRowForOffset %}
|
| 58 |
+
// Row that owns the flat element `offset` (inverse of element_offset over
|
| 59 |
+
// the non-axis coordinates).
|
| 60 |
+
fn row_for_offset(offset: u32) -> u32 {
|
| 61 |
+
let outer = offset / AXIS_STRIDE_SAFE;
|
| 62 |
+
let inner = offset % INNER_SIZE_SAFE;
|
| 63 |
+
return outer * INNER_SIZE_SAFE + inner;
|
| 64 |
+
}
|
| 65 |
+
{%- endif -%}
|
| 66 |
+
// FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
|
| 67 |
+
// `m - m` finite so an empty lane / all--inf row contributes the exact
|
| 68 |
+
// accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
|
| 69 |
+
// a zero final denominator according to their public semantics. Using -inf
|
| 70 |
+
// here changes +inf-row behavior.
|
| 71 |
+
const FLT_MAX: f32 = 3.4028234663852886e38;
|
| 72 |
+
|
| 73 |
+
fn is_finite_f32(value: f32) -> bool {
|
| 74 |
+
return select(false, value <= FLT_MAX, value >= -FLT_MAX);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
// x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
|
| 78 |
+
// exactly at the row max. `x - x` on an infinite max is a legal fast-math
|
| 79 |
+
// fold to 0, which would silently turn +inf rows finite — the explicit
|
| 80 |
+
// equality test keeps the NaN propagation of the serial kernels.
|
| 81 |
+
fn shifted_value(value: f32, maxValue: f32) -> f32 {
|
| 82 |
+
let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
|
| 83 |
+
return select(value - maxValue, 0.0, equalFiniteMax);
|
| 84 |
+
}
|
| 85 |
+
fn exp_shift(value: f32, maxValue: f32) -> f32 {
|
| 86 |
+
return exp(shifted_value(value, maxValue));
|
| 87 |
+
}
|
| 88 |
+
// Bit-exact NaN test: `v != v` can be folded to false by fast-math backends,
|
| 89 |
+
// and a constant NaN bit pattern is rejected at WGSL const-evaluation.
|
| 90 |
+
fn is_nan_f32(value: f32) -> bool {
|
| 91 |
+
let bits = bitcast<u32>(value);
|
| 92 |
+
return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
const PACKED_INNER: u32 = (INNER_SIZE + 3u) / 4u;
|
| 96 |
+
const OUTER_SIZE: u32 = STRIDED_ROWS / INNER_SIZE_SAFE;
|
| 97 |
+
const PACKED_ROWS: u32 = OUTER_SIZE * PACKED_INNER;
|
| 98 |
+
|
| 99 |
+
var<workgroup> partial: array<vec4<f32>, WGS>;
|
| 100 |
+
|
| 101 |
+
fn reduce_max4(value: vec4<f32>, tid: u32) -> vec4<f32> {
|
| 102 |
+
partial[tid] = value;
|
| 103 |
+
workgroupBarrier();
|
| 104 |
+
for (var stride = WGS >> 1u; stride > 0u; stride = stride >> 1u) {
|
| 105 |
+
if (tid < stride) {
|
| 106 |
+
partial[tid] = max(partial[tid], partial[tid + stride]);
|
| 107 |
+
}
|
| 108 |
+
workgroupBarrier();
|
| 109 |
+
}
|
| 110 |
+
return partial[0];
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
fn reduce_add4(value: vec4<f32>, tid: u32) -> vec4<f32> {
|
| 114 |
+
partial[tid] = value;
|
| 115 |
+
workgroupBarrier();
|
| 116 |
+
for (var stride = WGS >> 1u; stride > 0u; stride = stride >> 1u) {
|
| 117 |
+
if (tid < stride) {
|
| 118 |
+
partial[tid] = partial[tid] + partial[tid + stride];
|
| 119 |
+
}
|
| 120 |
+
workgroupBarrier();
|
| 121 |
+
}
|
| 122 |
+
return partial[0];
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
fn stable_exp(value: f32, maximum: f32) -> f32 {
|
| 126 |
+
let nanv = bitcast<f32>(bitcast<u32>(maximum) | 0x7fc00000u);
|
| 127 |
+
return select(exp_shift(value, maximum), nanv, maximum > FLT_MAX);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
fn normalized(value: f32, maximum: f32, sum: f32) -> f32 {
|
| 131 |
+
let rowIsNan = maximum > FLT_MAX || is_nan_f32(sum);
|
| 132 |
+
let nanOut = bitcast<f32>(bitcast<u32>(sum) | 0x7fc00000u);
|
| 133 |
+
return select(shifted_value(value, maximum) - log(sum), nanOut, rowIsNan);
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
@compute @workgroup_size(WGS)
|
| 137 |
+
fn main(
|
| 138 |
+
@builtin(workgroup_id) wg: vec3<u32>,
|
| 139 |
+
@builtin(local_invocation_id) lid: vec3<u32>,
|
| 140 |
+
@builtin(num_workgroups) nwg: vec3<u32>
|
| 141 |
+
) {
|
| 142 |
+
let group = wg.x + wg.y * nwg.x;
|
| 143 |
+
if (group >= PACKED_ROWS) {
|
| 144 |
+
return;
|
| 145 |
+
}
|
| 146 |
+
let outer = group / PACKED_INNER;
|
| 147 |
+
let inner0 = (group % PACKED_INNER) * 4u;
|
| 148 |
+
|
| 149 |
+
var localMax = vec4<f32>(-FLT_MAX);
|
| 150 |
+
for (var k = lid.x; k < REDUCE_SIZE; k = k + WGS) {
|
| 151 |
+
let base = outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner0;
|
| 152 |
+
if (inner0 < INNER_SIZE) { localMax.x = max(localMax.x, x[base]); }
|
| 153 |
+
if (inner0 + 1u < INNER_SIZE) { localMax.y = max(localMax.y, x[base + 1u]); }
|
| 154 |
+
if (inner0 + 2u < INNER_SIZE) { localMax.z = max(localMax.z, x[base + 2u]); }
|
| 155 |
+
if (inner0 + 3u < INNER_SIZE) { localMax.w = max(localMax.w, x[base + 3u]); }
|
| 156 |
+
}
|
| 157 |
+
let maximum = reduce_max4(localMax, lid.x);
|
| 158 |
+
|
| 159 |
+
var localSum = vec4<f32>(0.0);
|
| 160 |
+
for (var k = lid.x; k < REDUCE_SIZE; k = k + WGS) {
|
| 161 |
+
let base = outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner0;
|
| 162 |
+
if (inner0 < INNER_SIZE) { localSum.x = localSum.x + stable_exp(x[base], maximum.x); }
|
| 163 |
+
if (inner0 + 1u < INNER_SIZE) { localSum.y = localSum.y + stable_exp(x[base + 1u], maximum.y); }
|
| 164 |
+
if (inner0 + 2u < INNER_SIZE) { localSum.z = localSum.z + stable_exp(x[base + 2u], maximum.z); }
|
| 165 |
+
if (inner0 + 3u < INNER_SIZE) { localSum.w = localSum.w + stable_exp(x[base + 3u], maximum.w); }
|
| 166 |
+
}
|
| 167 |
+
let sum = reduce_add4(localSum, lid.x);
|
| 168 |
+
|
| 169 |
+
for (var k = lid.x; k < REDUCE_SIZE; k = k + WGS) {
|
| 170 |
+
let base = outer * REDUCE_SIZE * INNER_SIZE + k * INNER_SIZE + inner0;
|
| 171 |
+
if (inner0 < INNER_SIZE) { y[base] = normalized(x[base], maximum.x, sum.x); }
|
| 172 |
+
if (inner0 + 1u < INNER_SIZE) { y[base + 1u] = normalized(x[base + 1u], maximum.y, sum.y); }
|
| 173 |
+
if (inner0 + 2u < INNER_SIZE) { y[base + 2u] = normalized(x[base + 2u], maximum.z, sum.z); }
|
| 174 |
+
if (inner0 + 3u < INNER_SIZE) { y[base + 3u] = normalized(x[base + 3u], maximum.w, sum.w); }
|
| 175 |
+
}
|
| 176 |
+
}
|
build/webgpu/test.json
ADDED
|
@@ -0,0 +1,720 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"op": "ai.onnx.LogSoftmax",
|
| 3 |
+
"fixtureArrays": {
|
| 4 |
+
"ort_seed_123_input_x": [1.0856307, 0.99734545, 0.2829785, 1.5062947, 0.5786002, 1.6514366, 2.4266791, 0.42891264, 1.2659363, 0.8667404, 0.6788862, 0.09470897, 1.4913896, 0.638902, 0.44398195, 0.43435127, 2.20593, 2.1867862, 1.004054, 0.3861864, 0.7373686, 1.4907321, 0.9358339, 1.175829, 1.2538806, 0.6377515, 0.9071052, 1.4286807, 0.14006872, 0.8617549, 0.25561938, 2.798589, 1.7715331, 0.69987726, 0.92746246, 0.17363568, 0.002845916, 0.6882227, 0.87953633, 0.28362733, 0.8053665, 1.7276695, 0.3908998, 0.57380587, 0.33858904, 0.011830495, 2.3923652, 0.41291216, 0.978736, 2.2381434, 1.2940853, 1.0387882, 1.7437122, 0.79806274, 0.02968323, 1.0693159, 0.8907064, 1.7548862, 1.4956441, 1.0693927],
|
| 5 |
+
"onnx_backend_logsoftmax_input_x": [1.764052391052246, 0.40015721321105957, 0.978738009929657, 2.2408931255340576, 1.8675580024719238, 0.9772778749465942, 0.9500884413719177, 0.15135720372200012, 0.10321885347366333, 0.4105985164642334, 0.14404356479644775, 1.4542734622955322, 0.7610377073287964, 0.12167501449584961, 0.44386324286460876, 0.3336743414402008, 1.4940791130065918, 0.2051582634449005, 0.3130677044391632, 0.8540957570075989, 2.5529897212982178, 0.653618574142456, 0.8644362092018127, 0.7421650290489197, 2.269754648208618, 1.4543657302856445, 0.04575851559638977, 0.18718385696411133, 1.5327792167663574, 1.4693588018417358, 0.154947429895401, 0.37816253304481506, 0.8877857327461243, 1.980796456336975, 0.34791216254234314, 0.15634897351264954, 1.2302906513214111, 1.202379822731018, 0.38732680678367615, 0.302302747964859, 1.0485529899597168, 1.420017957687378, 1.7062702178955078, 1.950775384902954, 0.5096521973609924, 0.4380742907524109, 1.2527953386306763, 0.7774903774261475, 1.6138978004455566, 0.21274028718471527, 0.8954665660858154, 0.38690251111984253, 0.5108051300048828, 1.18063223361969, 0.02818222902715206, 0.4283318817615509, 0.06651721894741058, 0.30247190594673157, 0.6343221068382263, 0.3627411723136902]
|
| 6 |
+
},
|
| 7 |
+
"cases": [
|
| 8 |
+
{
|
| 9 |
+
"name": "strided_packed4_tail_rank3_axis1_inner6",
|
| 10 |
+
"provenance": {
|
| 11 |
+
"notes": "Exercises the scalar-aligned packed-4 strided reducer with two valid lanes in the final inner group."
|
| 12 |
+
},
|
| 13 |
+
"attrs": { "axis": 1 },
|
| 14 |
+
"inputs": {
|
| 15 |
+
"x": {
|
| 16 |
+
"dtype": "float32",
|
| 17 |
+
"shape": [2, 17, 6],
|
| 18 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.07, "scale": 2.0 }
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 17, 6], "tolerance": 0.00001, "relTolerance": 0.00001 } }
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"name": "strided_capacity_large_axis_tail_boundary",
|
| 25 |
+
"provenance": {
|
| 26 |
+
"notes": "Compact correctness sibling for the large-inner selector crossover. The case-local threshold selects the capacity path while axis length 2048 and inner=6 retain the production gate and non-multiple-of-four tail."
|
| 27 |
+
},
|
| 28 |
+
"tunables": { "STRIDED_CAPACITY_MIN_ROWS": 1 },
|
| 29 |
+
"attrs": { "axis": 1 },
|
| 30 |
+
"inputs": {
|
| 31 |
+
"x": {
|
| 32 |
+
"dtype": "float32",
|
| 33 |
+
"shape": [1, 2048, 6],
|
| 34 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.007, "scale": 2.0 }
|
| 35 |
+
}
|
| 36 |
+
},
|
| 37 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 2048, 6], "tolerance": 0.00001, "relTolerance": 0.00001 } }
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"name": "dispatch_cliff_online_2dfold_65540x40",
|
| 41 |
+
"attrs": { "axis": -1 },
|
| 42 |
+
"inputs": {
|
| 43 |
+
"x": {
|
| 44 |
+
"dtype": "float32",
|
| 45 |
+
"shape": [65540, 40],
|
| 46 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.007, "scale": 2.0 }
|
| 47 |
+
}
|
| 48 |
+
},
|
| 49 |
+
"outputs": { "y": { "dtype": "float32", "shape": [65540, 40], "tolerance": 0.0001, "relTolerance": 0.0001 } }
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"name": "dispatch_cliff_stable3pass_513x32768",
|
| 53 |
+
"attrs": { "axis": -1 },
|
| 54 |
+
"inputs": {
|
| 55 |
+
"x": {
|
| 56 |
+
"dtype": "float32",
|
| 57 |
+
"shape": [513, 32768],
|
| 58 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.031, "scale": 3.0 }
|
| 59 |
+
}
|
| 60 |
+
},
|
| 61 |
+
"outputs": { "y": { "dtype": "float32", "shape": [513, 32768], "tolerance": 0.0001, "relTolerance": 0.00001 } }
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"name": "rank2_last_axis",
|
| 65 |
+
"inputs": {
|
| 66 |
+
"x": {
|
| 67 |
+
"dtype": "float32",
|
| 68 |
+
"shape": [2, 4],
|
| 69 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, -2.0, 0.0, 2.0, 4.0] }
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 4] } }
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"name": "packed_rows_tail_axis1_33x32",
|
| 76 |
+
"provenance": {
|
| 77 |
+
"notes": "One row beyond a full 32-row packed workgroup locks the shared portable multirow kernel's partial-workgroup barrier and output guards."
|
| 78 |
+
},
|
| 79 |
+
"attrs": { "axis": 1 },
|
| 80 |
+
"inputs": {
|
| 81 |
+
"x": {
|
| 82 |
+
"dtype": "float32",
|
| 83 |
+
"shape": [33, 32],
|
| 84 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.019, "scale": 2.0 }
|
| 85 |
+
}
|
| 86 |
+
},
|
| 87 |
+
"outputs": { "y": { "dtype": "float32", "shape": [33, 32], "tolerance": 0.00001, "relTolerance": 0.00001 } }
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"name": "many_short_rows_axis1_64x32",
|
| 91 |
+
"provenance": {
|
| 92 |
+
"notes": "Compact correctness sibling for many-short-row LogSoftmax benchmarking; keeps the reduced axis short without making the correctness suite benchmark-sized."
|
| 93 |
+
},
|
| 94 |
+
"attrs": { "axis": 1 },
|
| 95 |
+
"inputs": {
|
| 96 |
+
"x": {
|
| 97 |
+
"dtype": "float32",
|
| 98 |
+
"shape": [64, 32],
|
| 99 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.019, "scale": 2.0 }
|
| 100 |
+
}
|
| 101 |
+
},
|
| 102 |
+
"outputs": { "y": { "dtype": "float32", "shape": [64, 32], "tolerance": 0.00001, "relTolerance": 0.00001 } }
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"name": "ort_simple_axis1",
|
| 106 |
+
"provenance": {
|
| 107 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 108 |
+
"test": "LogSoftmaxOperator.Simple"
|
| 109 |
+
},
|
| 110 |
+
"attrs": { "axis": 1 },
|
| 111 |
+
"inputs": {
|
| 112 |
+
"x": { "dtype": "float32", "shape": [1, 3], "data": { "kind": "values", "values": [-1.0, 0.0, 1.0] } }
|
| 113 |
+
},
|
| 114 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 3], "tolerance": 0.000001 } }
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"name": "ort_large_number_axis1",
|
| 118 |
+
"provenance": {
|
| 119 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 120 |
+
"test": "LogSoftmaxOperator.LargeNumber"
|
| 121 |
+
},
|
| 122 |
+
"attrs": { "axis": 1 },
|
| 123 |
+
"inputs": {
|
| 124 |
+
"x": {
|
| 125 |
+
"dtype": "float32",
|
| 126 |
+
"shape": [2, 4],
|
| 127 |
+
"data": { "kind": "values", "values": [0.0, 1.0, 2.0, 3.0, 10000.0, 10001.0, 10002.0, 10003.0] }
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 4], "tolerance": 0.0005 } }
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"name": "rank3_last_axis_f16",
|
| 134 |
+
"attrs": { "axis": -1 },
|
| 135 |
+
"inputs": { "x": { "dtype": "float16", "shape": [2, 2, 3] } },
|
| 136 |
+
"outputs": { "y": { "dtype": "float16", "shape": [2, 2, 3] } },
|
| 137 |
+
"tolerance": 0.002
|
| 138 |
+
},
|
| 139 |
+
{
|
| 140 |
+
"name": "float32_min_uniform_regression",
|
| 141 |
+
"attrs": { "axis": -1 },
|
| 142 |
+
"inputs": {
|
| 143 |
+
"x": { "dtype": "float32", "shape": [1, 64], "data": { "kind": "constant", "value": -3.4028234663852886e+38 } }
|
| 144 |
+
},
|
| 145 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 64], "tolerance": 0.000001 } }
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"name": "longrow_split_axis1_1x65536",
|
| 149 |
+
"attrs": { "axis": 1 },
|
| 150 |
+
"inputs": {
|
| 151 |
+
"x": {
|
| 152 |
+
"dtype": "float32",
|
| 153 |
+
"shape": [1, 65536],
|
| 154 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.031, "scale": 3.0 }
|
| 155 |
+
}
|
| 156 |
+
},
|
| 157 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 65536], "tolerance": 0.0001, "relTolerance": 0.00001 } }
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"name": "longrow_split_float32_min_uniform",
|
| 161 |
+
"attrs": { "axis": -1 },
|
| 162 |
+
"inputs": {
|
| 163 |
+
"x": {
|
| 164 |
+
"dtype": "float32",
|
| 165 |
+
"shape": [1, 65536],
|
| 166 |
+
"data": { "kind": "constant", "value": -3.4028234663852886e+38 }
|
| 167 |
+
}
|
| 168 |
+
},
|
| 169 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 65536], "tolerance": 0.00001, "relTolerance": 0.00001 } }
|
| 170 |
+
},
|
| 171 |
+
{
|
| 172 |
+
"name": "positive_infinity_rows_return_nan",
|
| 173 |
+
"attrs": { "axis": -1 },
|
| 174 |
+
"inputs": {
|
| 175 |
+
"x": {
|
| 176 |
+
"dtype": "float32",
|
| 177 |
+
"shape": [2, 4],
|
| 178 |
+
"data": { "kind": "values", "values": ["Infinity", 1.0, 2.0, -3.0, "Infinity", "Infinity", 5.0, 5.0] }
|
| 179 |
+
}
|
| 180 |
+
},
|
| 181 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 4], "tolerance": 0.000001, "allowNaN": true } }
|
| 182 |
+
},
|
| 183 |
+
{
|
| 184 |
+
"name": "singleton_axis_mixed_finite_nonfinite_f16",
|
| 185 |
+
"attrs": { "axis": 0 },
|
| 186 |
+
"inputs": {
|
| 187 |
+
"x": {
|
| 188 |
+
"dtype": "float16",
|
| 189 |
+
"shape": [1, 4],
|
| 190 |
+
"data": { "kind": "values", "values": [0.25, "Infinity", "-Infinity", "NaN"] }
|
| 191 |
+
}
|
| 192 |
+
},
|
| 193 |
+
"outputs": { "y": { "dtype": "float16", "shape": [1, 4], "tolerance": 0, "allowNaN": true } }
|
| 194 |
+
},
|
| 195 |
+
{
|
| 196 |
+
"name": "empty_last_axis_dim_zero",
|
| 197 |
+
"provenance": {
|
| 198 |
+
"source": "onnxruntime/test/providers/cpu/math/softmax_test.cc",
|
| 199 |
+
"test": "SoftmaxOperator.DimWithZero",
|
| 200 |
+
"notes": "LogSoftmax analogue of ORT's zero-dimension Softmax coverage; the output is empty, so the framework plans the op as a no-op (zero dispatch) per ONNX zero-dimension semantics."
|
| 201 |
+
},
|
| 202 |
+
"attrs": { "axis": -1 },
|
| 203 |
+
"inputs": { "x": { "dtype": "float32", "shape": [1, 0], "data": { "kind": "values", "values": [] } } },
|
| 204 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 0], "tolerance": 0 } }
|
| 205 |
+
},
|
| 206 |
+
{
|
| 207 |
+
"name": "empty_axis0_dim_zero",
|
| 208 |
+
"provenance": {
|
| 209 |
+
"source": "onnxruntime/test/providers/cpu/math/softmax_test.cc",
|
| 210 |
+
"test": "SoftmaxOperator.DimWithZero",
|
| 211 |
+
"notes": "LogSoftmax leading-axis analogue of ORT's zero-dimension Softmax coverage; the reduced axis itself has size zero and the output is empty."
|
| 212 |
+
},
|
| 213 |
+
"attrs": { "axis": 0 },
|
| 214 |
+
"inputs": { "x": { "dtype": "float32", "shape": [0, 1], "data": { "kind": "values", "values": [] } } },
|
| 215 |
+
"outputs": { "y": { "dtype": "float32", "shape": [0, 1], "tolerance": 0 } }
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"name": "axis0_non_last_rank2",
|
| 219 |
+
"attrs": { "axis": 0 },
|
| 220 |
+
"inputs": {
|
| 221 |
+
"x": {
|
| 222 |
+
"dtype": "float32",
|
| 223 |
+
"shape": [3, 4],
|
| 224 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 2.0, 1.0, 4.0, 3.0, 3.0, 4.0, 1.0, 2.0] }
|
| 225 |
+
}
|
| 226 |
+
},
|
| 227 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4], "tolerance": 0.000001 } }
|
| 228 |
+
},
|
| 229 |
+
{
|
| 230 |
+
"name": "ort_opset13_rank3_axis0_large_offsets",
|
| 231 |
+
"provenance": {
|
| 232 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 233 |
+
"test": "LogSoftmaxOperator.ThreeDimsAxis0",
|
| 234 |
+
"notes": "Opset-13 axis=0 semantics reduce only the leading axis; large per-column offsets exercise stable strided-axis normalization."
|
| 235 |
+
},
|
| 236 |
+
"attrs": { "axis": 0 },
|
| 237 |
+
"inputs": {
|
| 238 |
+
"x": {
|
| 239 |
+
"dtype": "float32",
|
| 240 |
+
"shape": [3, 2, 2],
|
| 241 |
+
"data": {
|
| 242 |
+
"kind": "values",
|
| 243 |
+
"values": [10000.0, -10000.0, 0.0, 5.0, 10001.0, -9999.0, -1.0, 6.0, 9999.0, -10001.0, 2.0, 4.0]
|
| 244 |
+
}
|
| 245 |
+
}
|
| 246 |
+
},
|
| 247 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 2, 2], "tolerance": 0.000001 } }
|
| 248 |
+
},
|
| 249 |
+
{
|
| 250 |
+
"name": "axis0_positive_infinity_rows_return_nan",
|
| 251 |
+
"provenance": {
|
| 252 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 253 |
+
"test": "LogSoftmaxOperator.ThreeDimsAxis0",
|
| 254 |
+
"notes": "Positive infinity must poison the whole reduction group even when the reduced axis is strided instead of contiguous."
|
| 255 |
+
},
|
| 256 |
+
"attrs": { "axis": 0 },
|
| 257 |
+
"inputs": {
|
| 258 |
+
"x": {
|
| 259 |
+
"dtype": "float32",
|
| 260 |
+
"shape": [2, 2, 2],
|
| 261 |
+
"data": { "kind": "values", "values": ["Infinity", 1.0, 2.0, "Infinity", 3.0, "Infinity", "Infinity", 4.0] }
|
| 262 |
+
}
|
| 263 |
+
},
|
| 264 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 2, 2], "tolerance": 0, "allowNaN": true } }
|
| 265 |
+
},
|
| 266 |
+
{
|
| 267 |
+
"name": "rank6_last_axis",
|
| 268 |
+
"attrs": { "axis": -1 },
|
| 269 |
+
"inputs": {
|
| 270 |
+
"x": {
|
| 271 |
+
"dtype": "float32",
|
| 272 |
+
"shape": [1, 2, 1, 2, 1, 3],
|
| 273 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, -1.0, 0.0, 1.0, 4.0, 4.0, 5.0, -2.0, -3.0, -4.0] }
|
| 274 |
+
}
|
| 275 |
+
},
|
| 276 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 2, 1, 2, 1, 3], "tolerance": 0.000001 } }
|
| 277 |
+
},
|
| 278 |
+
{
|
| 279 |
+
"name": "rank3_axis1_middle_large_values",
|
| 280 |
+
"attrs": { "axis": 1 },
|
| 281 |
+
"inputs": {
|
| 282 |
+
"x": {
|
| 283 |
+
"dtype": "float32",
|
| 284 |
+
"shape": [2, 3, 2],
|
| 285 |
+
"data": {
|
| 286 |
+
"kind": "values",
|
| 287 |
+
"values": [1000.0, -1000.0, 1001.0, -1001.0, 999.0, -999.0, -50.0, 50.0, -51.0, 49.0, -52.0, 48.0]
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
},
|
| 291 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 3, 2], "tolerance": 0.000001 } }
|
| 292 |
+
},
|
| 293 |
+
{
|
| 294 |
+
"name": "ort_opset13_rank3_axis1_seed123",
|
| 295 |
+
"provenance": {
|
| 296 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 297 |
+
"test": "LogSoftmaxOperator.ThreeDimsAxis1_opset13"
|
| 298 |
+
},
|
| 299 |
+
"attrs": { "axis": 1 },
|
| 300 |
+
"inputs": {
|
| 301 |
+
"x": {
|
| 302 |
+
"dtype": "float32",
|
| 303 |
+
"shape": [3, 4, 5],
|
| 304 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_seed_123_input_x" } }
|
| 305 |
+
}
|
| 306 |
+
},
|
| 307 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.000001 } }
|
| 308 |
+
},
|
| 309 |
+
{
|
| 310 |
+
"name": "ort_opset13_rank3_axis2_seed123",
|
| 311 |
+
"provenance": {
|
| 312 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 313 |
+
"test": "LogSoftmaxOperator.ThreeDimsAxis2_opset13"
|
| 314 |
+
},
|
| 315 |
+
"attrs": { "axis": 2 },
|
| 316 |
+
"inputs": {
|
| 317 |
+
"x": {
|
| 318 |
+
"dtype": "float32",
|
| 319 |
+
"shape": [3, 4, 5],
|
| 320 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_seed_123_input_x" } }
|
| 321 |
+
}
|
| 322 |
+
},
|
| 323 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.000001 } }
|
| 324 |
+
},
|
| 325 |
+
{
|
| 326 |
+
"name": "rank3_axis_minus2_middle_strided",
|
| 327 |
+
"attrs": { "axis": -2 },
|
| 328 |
+
"inputs": {
|
| 329 |
+
"x": {
|
| 330 |
+
"dtype": "float32",
|
| 331 |
+
"shape": [1, 3, 3],
|
| 332 |
+
"data": { "kind": "values", "values": [1.0, 2.0, 3.0, 2.0, 3.0, 4.0, 3.0, 4.0, 5.0] }
|
| 333 |
+
}
|
| 334 |
+
},
|
| 335 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 3, 3], "tolerance": 0.000001 } }
|
| 336 |
+
},
|
| 337 |
+
{
|
| 338 |
+
"name": "ort_opset13_rank3_default_axis_seed123",
|
| 339 |
+
"provenance": {
|
| 340 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 341 |
+
"test": "LogSoftmaxOperator.ThreeDimsDefaultAxis_opset13"
|
| 342 |
+
},
|
| 343 |
+
"inputs": {
|
| 344 |
+
"x": {
|
| 345 |
+
"dtype": "float32",
|
| 346 |
+
"shape": [3, 4, 5],
|
| 347 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_seed_123_input_x" } }
|
| 348 |
+
}
|
| 349 |
+
},
|
| 350 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.000001 } }
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
"name": "ort_negative_axis_rank3_last_seed123",
|
| 354 |
+
"provenance": {
|
| 355 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 356 |
+
"test": "LogSoftmaxOperator.ThreeDimsNegativeAxis"
|
| 357 |
+
},
|
| 358 |
+
"attrs": { "axis": -1 },
|
| 359 |
+
"inputs": {
|
| 360 |
+
"x": {
|
| 361 |
+
"dtype": "float32",
|
| 362 |
+
"shape": [3, 4, 5],
|
| 363 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/ort_seed_123_input_x" } }
|
| 364 |
+
}
|
| 365 |
+
},
|
| 366 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.000001 } }
|
| 367 |
+
},
|
| 368 |
+
{
|
| 369 |
+
"name": "ort_axis1_large_dim_1025",
|
| 370 |
+
"provenance": {
|
| 371 |
+
"source": "onnxruntime/test/providers/cpu/math/logsoftmax_test.cc",
|
| 372 |
+
"test": "LogSoftmaxOperator.2DInputReduceOnAxis1WithLargeDim"
|
| 373 |
+
},
|
| 374 |
+
"attrs": { "axis": 1 },
|
| 375 |
+
"inputs": {
|
| 376 |
+
"x": {
|
| 377 |
+
"dtype": "float32",
|
| 378 |
+
"shape": [1, 1025],
|
| 379 |
+
"data": {
|
| 380 |
+
"kind": "values",
|
| 381 |
+
"values": [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.7, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.1, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.2, 1.21, 1.22, 1.23, 1.24, 1.25, 1.26, 1.27, 1.28, 1.29, 1.3, 1.31, 1.32, 1.33, 1.34, 1.35, 1.36, 1.37, 1.38, 1.39, 1.4, 1.41, 1.42, 1.43, 1.44, 1.45, 1.46, 1.47, 1.48, 1.49, 1.5, 1.51, 1.52, 1.53, 1.54, 1.55, 1.56, 1.57, 1.58, 1.59, 1.6, 1.61, 1.62, 1.63, 1.64, 1.65, 1.66, 1.67, 1.68, 1.69, 1.7, 1.71, 1.72, 1.73, 1.74, 1.75, 1.76, 1.77, 1.78, 1.79, 1.8, 1.81, 1.82, 1.83, 1.84, 1.85, 1.86, 1.87, 1.88, 1.89, 1.9, 1.91, 1.92, 1.93, 1.94, 1.95, 1.96, 1.97, 1.98, 1.99, 2.0, 2.01, 2.02, 2.03, 2.04, 2.05, 2.06, 2.07, 2.08, 2.09, 2.1, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18, 2.19, 2.2, 2.21, 2.22, 2.23, 2.24, 2.25, 2.26, 2.27, 2.28, 2.29, 2.3, 2.31, 2.32, 2.33, 2.34, 2.35, 2.36, 2.37, 2.38, 2.39, 2.4, 2.41, 2.42, 2.43, 2.44, 2.45, 2.46, 2.47, 2.48, 2.49, 2.5, 2.51, 2.52, 2.53, 2.54, 2.55, 2.56, 2.57, 2.58, 2.59, 2.6, 2.61, 2.62, 2.63, 2.64, 2.65, 2.66, 2.67, 2.68, 2.69, 2.7, 2.71, 2.72, 2.73, 2.74, 2.75, 2.76, 2.77, 2.78, 2.79, 2.8, 2.81, 2.82, 2.83, 2.84, 2.85, 2.86, 2.87, 2.88, 2.89, 2.9, 2.91, 2.92, 2.93, 2.94, 2.95, 2.96, 2.97, 2.98, 2.99, 3.0, 3.01, 3.02, 3.03, 3.04, 3.05, 3.06, 3.07, 3.08, 3.09, 3.1, 3.11, 3.12, 3.13, 3.14, 3.15, 3.16, 3.17, 3.18, 3.19, 3.2, 3.21, 3.22, 3.23, 3.24, 3.25, 3.26, 3.27, 3.28, 3.29, 3.3, 3.31, 3.32, 3.33, 3.34, 3.35, 3.36, 3.37, 3.38, 3.39, 3.4, 3.41, 3.42, 3.43, 3.44, 3.45, 3.46, 3.47, 3.48, 3.49, 3.5, 3.51, 3.52, 3.53, 3.54, 3.55, 3.56, 3.57, 3.58, 3.59, 3.6, 3.61, 3.62, 3.63, 3.64, 3.65, 3.66, 3.67, 3.68, 3.69, 3.7, 3.71, 3.72, 3.73, 3.74, 3.75, 3.76, 3.77, 3.78, 3.79, 3.8, 3.81, 3.82, 3.83, 3.84, 3.85, 3.86, 3.87, 3.88, 3.89, 3.9, 3.91, 3.92, 3.93, 3.94, 3.95, 3.96, 3.97, 3.98, 3.99, 4.0, 4.01, 4.02, 4.03, 4.04, 4.05, 4.06, 4.07, 4.08, 4.09, 4.1, 4.11, 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18, 4.19, 4.2, 4.21, 4.22, 4.23, 4.24, 4.25, 4.26, 4.27, 4.28, 4.29, 4.3, 4.31, 4.32, 4.33, 4.34, 4.35, 4.36, 4.37, 4.38, 4.39, 4.4, 4.41, 4.42, 4.43, 4.44, 4.45, 4.46, 4.47, 4.48, 4.49, 4.5, 4.51, 4.52, 4.53, 4.54, 4.55, 4.56, 4.57, 4.58, 4.59, 4.6, 4.61, 4.62, 4.63, 4.64, 4.65, 4.66, 4.67, 4.68, 4.69, 4.7, 4.71, 4.72, 4.73, 4.74, 4.75, 4.76, 4.77, 4.78, 4.79, 4.8, 4.81, 4.82, 4.83, 4.84, 4.85, 4.86, 4.87, 4.88, 4.89, 4.9, 4.91, 4.92, 4.93, 4.94, 4.95, 4.96, 4.97, 4.98, 4.99, 5.0, 5.01, 5.02, 5.03, 5.04, 5.05, 5.06, 5.07, 5.08, 5.09, 5.1, 5.11, 5.12, 5.13, 5.14, 5.15, 5.16, 5.17, 5.18, 5.19, 5.2, 5.21, 5.22, 5.23, 5.24, 5.25, 5.26, 5.27, 5.28, 5.29, 5.3, 5.31, 5.32, 5.33, 5.34, 5.35, 5.36, 5.37, 5.38, 5.39, 5.4, 5.41, 5.42, 5.43, 5.44, 5.45, 5.46, 5.47, 5.48, 5.49, 5.5, 5.51, 5.52, 5.53, 5.54, 5.55, 5.56, 5.57, 5.58, 5.59, 5.6, 5.61, 5.62, 5.63, 5.64, 5.65, 5.66, 5.67, 5.68, 5.69, 5.7, 5.71, 5.72, 5.73, 5.74, 5.75, 5.76, 5.77, 5.78, 5.79, 5.8, 5.81, 5.82, 5.83, 5.84, 5.85, 5.86, 5.87, 5.88, 5.89, 5.9, 5.91, 5.92, 5.93, 5.94, 5.95, 5.96, 5.97, 5.98, 5.99, 6.0, 6.01, 6.02, 6.03, 6.04, 6.05, 6.06, 6.07, 6.08, 6.09, 6.1, 6.11, 6.12, 6.13, 6.14, 6.15, 6.16, 6.17, 6.18, 6.19, 6.2, 6.21, 6.22, 6.23, 6.24, 6.25, 6.26, 6.27, 6.28, 6.29, 6.3, 6.31, 6.32, 6.33, 6.34, 6.35, 6.36, 6.37, 6.38, 6.39, 6.4, 6.41, 6.42, 6.43, 6.44, 6.45, 6.46, 6.47, 6.48, 6.49, 6.5, 6.51, 6.52, 6.53, 6.54, 6.55, 6.56, 6.57, 6.58, 6.59, 6.6, 6.61, 6.62, 6.63, 6.64, 6.65, 6.66, 6.67, 6.68, 6.69, 6.7, 6.71, 6.72, 6.73, 6.74, 6.75, 6.76, 6.77, 6.78, 6.79, 6.8, 6.81, 6.82, 6.83, 6.84, 6.85, 6.86, 6.87, 6.88, 6.89, 6.9, 6.91, 6.92, 6.93, 6.94, 6.95, 6.96, 6.97, 6.98, 6.99, 7.0, 7.01, 7.02, 7.03, 7.04, 7.05, 7.06, 7.07, 7.08, 7.09, 7.1, 7.11, 7.12, 7.13, 7.14, 7.15, 7.16, 7.17, 7.18, 7.19, 7.2, 7.21, 7.22, 7.23, 7.24, 7.25, 7.26, 7.27, 7.28, 7.29, 7.3, 7.31, 7.32, 7.33, 7.34, 7.35, 7.36, 7.37, 7.38, 7.39, 7.4, 7.41, 7.42, 7.43, 7.44, 7.45, 7.46, 7.47, 7.48, 7.49, 7.5, 7.51, 7.52, 7.53, 7.54, 7.55, 7.56, 7.57, 7.58, 7.59, 7.6, 7.61, 7.62, 7.63, 7.64, 7.65, 7.66, 7.67, 7.68, 7.69, 7.7, 7.71, 7.72, 7.73, 7.74, 7.75, 7.76, 7.77, 7.78, 7.79, 7.8, 7.81, 7.82, 7.83, 7.84, 7.85, 7.86, 7.87, 7.88, 7.89, 7.9, 7.91, 7.92, 7.93, 7.94, 7.95, 7.96, 7.97, 7.98, 7.99, 8.0, 8.01, 8.02, 8.03, 8.04, 8.05, 8.06, 8.07, 8.08, 8.09, 8.1, 8.11, 8.12, 8.13, 8.14, 8.15, 8.16, 8.17, 8.18, 8.19, 8.2, 8.21, 8.22, 8.23, 8.24, 8.25, 8.26, 8.27, 8.28, 8.29, 8.3, 8.31, 8.32, 8.33, 8.34, 8.35, 8.36, 8.37, 8.38, 8.39, 8.4, 8.41, 8.42, 8.43, 8.44, 8.45, 8.46, 8.47, 8.48, 8.49, 8.5, 8.51, 8.52, 8.53, 8.54, 8.55, 8.56, 8.57, 8.58, 8.59, 8.6, 8.61, 8.62, 8.63, 8.64, 8.65, 8.66, 8.67, 8.68, 8.69, 8.7, 8.71, 8.72, 8.73, 8.74, 8.75, 8.76, 8.77, 8.78, 8.79, 8.8, 8.81, 8.82, 8.83, 8.84, 8.85, 8.86, 8.87, 8.88, 8.89, 8.9, 8.91, 8.92, 8.93, 8.94, 8.95, 8.96, 8.97, 8.98, 8.99, 9.0, 9.01, 9.02, 9.03, 9.04, 9.05, 9.06, 9.07, 9.08, 9.09, 9.1, 9.11, 9.12, 9.13, 9.14, 9.15, 9.16, 9.17, 9.18, 9.19, 9.2, 9.21, 9.22, 9.23, 9.24, 9.25, 9.26, 9.27, 9.28, 9.29, 9.3, 9.31, 9.32, 9.33, 9.34, 9.35, 9.36, 9.37, 9.38, 9.39, 9.4, 9.41, 9.42, 9.43, 9.44, 9.45, 9.46, 9.47, 9.48, 9.49, 9.5, 9.51, 9.52, 9.53, 9.54, 9.55, 9.56, 9.57, 9.58, 9.59, 9.6, 9.61, 9.62, 9.63, 9.64, 9.65, 9.66, 9.67, 9.68, 9.69, 9.7, 9.71, 9.72, 9.73, 9.74, 9.75, 9.76, 9.77, 9.78, 9.79, 9.8, 9.81, 9.82, 9.83, 9.84, 9.85, 9.86, 9.87, 9.88, 9.89, 9.9, 9.91, 9.92, 9.93, 9.94, 9.95, 9.96, 9.97, 9.98, 9.99, 10.0, 10.01, 10.02, 10.03, 10.04, 10.05, 10.06, 10.07, 10.08, 10.09, 10.1, 10.11, 10.12, 10.13, 10.14, 10.15, 10.16, 10.17, 10.18, 10.19, 10.2, 10.21, 10.22, 10.23, 10.24, 10.25]
|
| 382 |
+
}
|
| 383 |
+
}
|
| 384 |
+
},
|
| 385 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 1025], "tolerance": 0.00001 } }
|
| 386 |
+
},
|
| 387 |
+
{
|
| 388 |
+
"name": "onnx_backend_logsoftmax_axis_0",
|
| 389 |
+
"provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_logsoftmax_axis_0" },
|
| 390 |
+
"attrs": { "axis": 0 },
|
| 391 |
+
"inputs": {
|
| 392 |
+
"x": {
|
| 393 |
+
"dtype": "float32",
|
| 394 |
+
"shape": [3, 4, 5],
|
| 395 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_logsoftmax_input_x" } }
|
| 396 |
+
}
|
| 397 |
+
},
|
| 398 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.00001 } }
|
| 399 |
+
},
|
| 400 |
+
{
|
| 401 |
+
"name": "onnx_backend_logsoftmax_axis_1",
|
| 402 |
+
"provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_logsoftmax_axis_1" },
|
| 403 |
+
"attrs": { "axis": 1 },
|
| 404 |
+
"inputs": {
|
| 405 |
+
"x": {
|
| 406 |
+
"dtype": "float32",
|
| 407 |
+
"shape": [3, 4, 5],
|
| 408 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_logsoftmax_input_x" } }
|
| 409 |
+
}
|
| 410 |
+
},
|
| 411 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.00001 } }
|
| 412 |
+
},
|
| 413 |
+
{
|
| 414 |
+
"name": "onnx_backend_logsoftmax_axis_2",
|
| 415 |
+
"provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_logsoftmax_axis_2" },
|
| 416 |
+
"attrs": { "axis": 2 },
|
| 417 |
+
"inputs": {
|
| 418 |
+
"x": {
|
| 419 |
+
"dtype": "float32",
|
| 420 |
+
"shape": [3, 4, 5],
|
| 421 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_logsoftmax_input_x" } }
|
| 422 |
+
}
|
| 423 |
+
},
|
| 424 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.00001 } }
|
| 425 |
+
},
|
| 426 |
+
{
|
| 427 |
+
"name": "onnx_backend_logsoftmax_default_axis",
|
| 428 |
+
"provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_logsoftmax_default_axis" },
|
| 429 |
+
"inputs": {
|
| 430 |
+
"x": {
|
| 431 |
+
"dtype": "float32",
|
| 432 |
+
"shape": [3, 4, 5],
|
| 433 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_logsoftmax_input_x" } }
|
| 434 |
+
}
|
| 435 |
+
},
|
| 436 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.00001 } }
|
| 437 |
+
},
|
| 438 |
+
{
|
| 439 |
+
"name": "onnx_backend_logsoftmax_example_1",
|
| 440 |
+
"provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_logsoftmax_example_1" },
|
| 441 |
+
"inputs": {
|
| 442 |
+
"x": { "dtype": "float32", "shape": [1, 3], "data": { "kind": "values", "values": [-1.0, 0.0, 1.0] } }
|
| 443 |
+
},
|
| 444 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 3], "tolerance": 0.00001 } }
|
| 445 |
+
},
|
| 446 |
+
{
|
| 447 |
+
"name": "onnx_backend_logsoftmax_large_number",
|
| 448 |
+
"provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_logsoftmax_large_number" },
|
| 449 |
+
"inputs": {
|
| 450 |
+
"x": {
|
| 451 |
+
"dtype": "float32",
|
| 452 |
+
"shape": [2, 4],
|
| 453 |
+
"data": { "kind": "values", "values": [0.0, 1.0, 2.0, 3.0, 10000.0, 10001.0, 10002.0, 10003.0] }
|
| 454 |
+
}
|
| 455 |
+
},
|
| 456 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 4], "tolerance": 0.00001 } }
|
| 457 |
+
},
|
| 458 |
+
{
|
| 459 |
+
"name": "onnx_backend_logsoftmax_negative_axis",
|
| 460 |
+
"provenance": { "source": "cmake/external/onnx/onnx/backend/test/data/node/test_logsoftmax_negative_axis" },
|
| 461 |
+
"attrs": { "axis": -1 },
|
| 462 |
+
"inputs": {
|
| 463 |
+
"x": {
|
| 464 |
+
"dtype": "float32",
|
| 465 |
+
"shape": [3, 4, 5],
|
| 466 |
+
"data": { "kind": "values", "values": { "$ref": "#/fixtureArrays/onnx_backend_logsoftmax_input_x" } }
|
| 467 |
+
}
|
| 468 |
+
},
|
| 469 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 4, 5], "tolerance": 0.00001 } }
|
| 470 |
+
},
|
| 471 |
+
{
|
| 472 |
+
"name": "online_wg_vec4_2x1024",
|
| 473 |
+
"attrs": { "axis": -1 },
|
| 474 |
+
"inputs": {
|
| 475 |
+
"x": {
|
| 476 |
+
"dtype": "float32",
|
| 477 |
+
"shape": [2, 1024],
|
| 478 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.07, "scale": 2.0 }
|
| 479 |
+
}
|
| 480 |
+
},
|
| 481 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 1024], "tolerance": 0.00001 } }
|
| 482 |
+
},
|
| 483 |
+
{
|
| 484 |
+
"name": "online_wg_scalar_odd_2x511",
|
| 485 |
+
"attrs": { "axis": -1 },
|
| 486 |
+
"inputs": {
|
| 487 |
+
"x": {
|
| 488 |
+
"dtype": "float32",
|
| 489 |
+
"shape": [2, 511],
|
| 490 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.11, "scale": 2.0 }
|
| 491 |
+
}
|
| 492 |
+
},
|
| 493 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 511], "tolerance": 0.00001 } }
|
| 494 |
+
},
|
| 495 |
+
{
|
| 496 |
+
"name": "online_wg_unaligned_even_2x510",
|
| 497 |
+
"attrs": { "axis": -1 },
|
| 498 |
+
"inputs": {
|
| 499 |
+
"x": {
|
| 500 |
+
"dtype": "float32",
|
| 501 |
+
"shape": [2, 510],
|
| 502 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.09, "scale": 2.0 }
|
| 503 |
+
}
|
| 504 |
+
},
|
| 505 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 510], "tolerance": 0.00001 } }
|
| 506 |
+
},
|
| 507 |
+
{
|
| 508 |
+
"name": "online_wg_batch_2048x64",
|
| 509 |
+
"attrs": { "axis": -1 },
|
| 510 |
+
"inputs": {
|
| 511 |
+
"x": {
|
| 512 |
+
"dtype": "float32",
|
| 513 |
+
"shape": [2048, 64],
|
| 514 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29, "scale": 2.0 }
|
| 515 |
+
}
|
| 516 |
+
},
|
| 517 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2048, 64], "tolerance": 0.00001 } }
|
| 518 |
+
},
|
| 519 |
+
{
|
| 520 |
+
"name": "online_wg_all_negative_infinity_2x64",
|
| 521 |
+
"attrs": { "axis": -1 },
|
| 522 |
+
"inputs": { "x": { "dtype": "float32", "shape": [2, 64], "data": { "kind": "negativeInfinity" } } },
|
| 523 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 64], "tolerance": 0.000001, "allowNaN": true } }
|
| 524 |
+
},
|
| 525 |
+
{
|
| 526 |
+
"name": "online_wg_neg_inf_padding_mixed_2x40",
|
| 527 |
+
"attrs": { "axis": -1 },
|
| 528 |
+
"inputs": {
|
| 529 |
+
"x": {
|
| 530 |
+
"dtype": "float32",
|
| 531 |
+
"shape": [2, 40],
|
| 532 |
+
"data": { "kind": "cycle", "values": ["-Infinity", 0.5, 1.25, "-Infinity", -2.5] }
|
| 533 |
+
}
|
| 534 |
+
},
|
| 535 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 40], "tolerance": 0.00001 } }
|
| 536 |
+
},
|
| 537 |
+
{
|
| 538 |
+
"name": "online_wg_f16_2x128",
|
| 539 |
+
"attrs": { "axis": -1 },
|
| 540 |
+
"inputs": {
|
| 541 |
+
"x": {
|
| 542 |
+
"dtype": "float16",
|
| 543 |
+
"shape": [2, 128],
|
| 544 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.09, "scale": 2.0 }
|
| 545 |
+
}
|
| 546 |
+
},
|
| 547 |
+
"outputs": { "y": { "dtype": "float16", "shape": [2, 128], "tolerance": 0.005 } }
|
| 548 |
+
},
|
| 549 |
+
{
|
| 550 |
+
"name": "dispatch_cliff_strided_rows_140000",
|
| 551 |
+
"provenance": {
|
| 552 |
+
"notes": "Non-last (strided) axis log-softmax over 140000 outer rows (>65535) forces the folded one-workgroup-per-row dispatch in the strided_3pass reduce_max/exp_sum passes (shared with Softmax). Validates the wg.x + wg.y*nwg.x reconstruction guarded by the compile-time STRIDED_ROWS const. Before the 2D fold the plan-time dispatch-limit guard throws."
|
| 553 |
+
},
|
| 554 |
+
"attrs": { "axis": 1 },
|
| 555 |
+
"inputs": {
|
| 556 |
+
"x": {
|
| 557 |
+
"dtype": "float32",
|
| 558 |
+
"shape": [70000, 4, 2],
|
| 559 |
+
"data": { "kind": "cycle", "values": [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] }
|
| 560 |
+
}
|
| 561 |
+
},
|
| 562 |
+
"outputs": { "y": { "dtype": "float32", "shape": [70000, 4, 2], "tolerance": 0.0001 } }
|
| 563 |
+
},
|
| 564 |
+
{
|
| 565 |
+
"name": "rank1_axis_minus1_f32",
|
| 566 |
+
"attrs": { "axis": -1 },
|
| 567 |
+
"inputs": {
|
| 568 |
+
"x": {
|
| 569 |
+
"dtype": "float32",
|
| 570 |
+
"shape": [128],
|
| 571 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.041, "cosStep": 0.017, "scale": 3.0 }
|
| 572 |
+
}
|
| 573 |
+
},
|
| 574 |
+
"outputs": { "y": { "dtype": "float32", "shape": [128], "tolerance": 0.000001 } }
|
| 575 |
+
},
|
| 576 |
+
{
|
| 577 |
+
"name": "rank1_axis0_f32",
|
| 578 |
+
"attrs": { "axis": 0 },
|
| 579 |
+
"inputs": {
|
| 580 |
+
"x": {
|
| 581 |
+
"dtype": "float32",
|
| 582 |
+
"shape": [128],
|
| 583 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.041, "cosStep": 0.017, "scale": 3.0 }
|
| 584 |
+
}
|
| 585 |
+
},
|
| 586 |
+
"outputs": { "y": { "dtype": "float32", "shape": [128], "tolerance": 0.000001 } }
|
| 587 |
+
},
|
| 588 |
+
{
|
| 589 |
+
"name": "f16_strided_axis1_rank3",
|
| 590 |
+
"attrs": { "axis": 1 },
|
| 591 |
+
"inputs": {
|
| 592 |
+
"x": {
|
| 593 |
+
"dtype": "float16",
|
| 594 |
+
"shape": [4, 8, 5],
|
| 595 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.07, "scale": 2.0 }
|
| 596 |
+
}
|
| 597 |
+
},
|
| 598 |
+
"outputs": { "y": { "dtype": "float16", "shape": [4, 8, 5], "tolerance": 0.005 } }
|
| 599 |
+
},
|
| 600 |
+
{
|
| 601 |
+
"name": "f16_longrow_split_1x65536",
|
| 602 |
+
"attrs": { "axis": -1 },
|
| 603 |
+
"inputs": {
|
| 604 |
+
"x": {
|
| 605 |
+
"dtype": "float16",
|
| 606 |
+
"shape": [1, 65536],
|
| 607 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.031, "scale": 2.0 }
|
| 608 |
+
}
|
| 609 |
+
},
|
| 610 |
+
"outputs": { "y": { "dtype": "float16", "shape": [1, 65536], "tolerance": 0.005 } }
|
| 611 |
+
},
|
| 612 |
+
{
|
| 613 |
+
"name": "nan_in_input_propagates_rank2",
|
| 614 |
+
"attrs": { "axis": -1 },
|
| 615 |
+
"inputs": {
|
| 616 |
+
"x": {
|
| 617 |
+
"dtype": "float32",
|
| 618 |
+
"shape": [2, 4],
|
| 619 |
+
"data": { "kind": "values", "values": ["NaN", 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 4.0] }
|
| 620 |
+
}
|
| 621 |
+
},
|
| 622 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 4], "tolerance": 0.000001, "allowNaN": true } }
|
| 623 |
+
},
|
| 624 |
+
{
|
| 625 |
+
"name": "longrow_split_positive_infinity_poisons_row_2x65536",
|
| 626 |
+
"attrs": { "axis": -1 },
|
| 627 |
+
"inputs": {
|
| 628 |
+
"x": {
|
| 629 |
+
"dtype": "float32",
|
| 630 |
+
"shape": [2, 65536],
|
| 631 |
+
"data": { "kind": "cycle", "values": ["Infinity", 0.5, 1.25, -2.5, 0.75, -1.0, 2.0] }
|
| 632 |
+
}
|
| 633 |
+
},
|
| 634 |
+
"outputs": {
|
| 635 |
+
"y": { "dtype": "float32", "shape": [2, 65536], "tolerance": 0.0001, "relTolerance": 0.0001, "allowNaN": true }
|
| 636 |
+
}
|
| 637 |
+
},
|
| 638 |
+
{
|
| 639 |
+
"name": "longrow_split_all_negative_infinity_row_nan_1x65536",
|
| 640 |
+
"attrs": { "axis": -1 },
|
| 641 |
+
"inputs": { "x": { "dtype": "float32", "shape": [1, 65536], "data": { "kind": "negativeInfinity" } } },
|
| 642 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 65536], "tolerance": 0.000001, "allowNaN": true } }
|
| 643 |
+
},
|
| 644 |
+
{
|
| 645 |
+
"name": "vec4_combine_positive_infinity_poisons_row_2x68",
|
| 646 |
+
"attrs": { "axis": -1 },
|
| 647 |
+
"inputs": {
|
| 648 |
+
"x": {
|
| 649 |
+
"dtype": "float32",
|
| 650 |
+
"shape": [2, 68],
|
| 651 |
+
"data": { "kind": "cycle", "values": ["Infinity", 0.25, -1.5, 2.0, 0.5, -0.75, 1.75, -2.25] }
|
| 652 |
+
}
|
| 653 |
+
},
|
| 654 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 68], "tolerance": 0.000001, "allowNaN": true } }
|
| 655 |
+
},
|
| 656 |
+
{
|
| 657 |
+
"name": "longrow_split_multirow_isolation_3x65536",
|
| 658 |
+
"attrs": { "axis": -1 },
|
| 659 |
+
"inputs": {
|
| 660 |
+
"x": {
|
| 661 |
+
"dtype": "float32",
|
| 662 |
+
"shape": [3, 65536],
|
| 663 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.023, "scale": 4.0, "offset": 100.0 }
|
| 664 |
+
}
|
| 665 |
+
},
|
| 666 |
+
"outputs": { "y": { "dtype": "float32", "shape": [3, 65536], "tolerance": 0.0001, "relTolerance": 0.00001 } }
|
| 667 |
+
},
|
| 668 |
+
{
|
| 669 |
+
"name": "f16_longrow_split_positive_infinity_poisons_row_1x65536",
|
| 670 |
+
"attrs": { "axis": -1 },
|
| 671 |
+
"inputs": {
|
| 672 |
+
"x": {
|
| 673 |
+
"dtype": "float16",
|
| 674 |
+
"shape": [1, 65536],
|
| 675 |
+
"data": { "kind": "cycle", "values": ["Infinity", 0.5, 1.0, -1.5, 2.0, -0.5] }
|
| 676 |
+
}
|
| 677 |
+
},
|
| 678 |
+
"outputs": { "y": { "dtype": "float16", "shape": [1, 65536], "tolerance": 0.005, "allowNaN": true } }
|
| 679 |
+
},
|
| 680 |
+
{
|
| 681 |
+
"name": "rank7_last_axis",
|
| 682 |
+
"attrs": { "axis": -1 },
|
| 683 |
+
"inputs": {
|
| 684 |
+
"x": {
|
| 685 |
+
"dtype": "float32",
|
| 686 |
+
"shape": [2, 1, 2, 1, 2, 1, 3],
|
| 687 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29, "scale": 2.0 }
|
| 688 |
+
}
|
| 689 |
+
},
|
| 690 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 1, 2, 1, 2, 1, 3], "tolerance": 0.000001 } }
|
| 691 |
+
},
|
| 692 |
+
{
|
| 693 |
+
"name": "strided_rowmax_scratch_over_128mib_f16_capacity_fallback",
|
| 694 |
+
"provenance": {
|
| 695 |
+
"notes": "Row-scratch capacity cliff: 33554433 strided rows need a 128 MiB+1 f32 rowMax buffer, so strided_3pass fails axisRowScratchFits and only the single-pass online fallback remains. f16 keeps the 64 MiB input inside the same spec-minimum storage-binding limit the f32 scratch overflows (and rejects the two f32-only strided routes), which is exactly the device situation the fallback exists for."
|
| 696 |
+
},
|
| 697 |
+
"attrs": { "axis": 0 },
|
| 698 |
+
"inputs": {
|
| 699 |
+
"x": {
|
| 700 |
+
"dtype": "float16",
|
| 701 |
+
"shape": [1, 33554433],
|
| 702 |
+
"data": { "kind": "cycle", "values": [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] }
|
| 703 |
+
}
|
| 704 |
+
},
|
| 705 |
+
"outputs": { "y": { "dtype": "float16", "shape": [1, 33554433], "tolerance": 0.002 } }
|
| 706 |
+
},
|
| 707 |
+
{
|
| 708 |
+
"name": "rank8_last_axis",
|
| 709 |
+
"attrs": { "axis": -1 },
|
| 710 |
+
"inputs": {
|
| 711 |
+
"x": {
|
| 712 |
+
"dtype": "float32",
|
| 713 |
+
"shape": [2, 1, 2, 1, 2, 1, 2, 3],
|
| 714 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.21 }
|
| 715 |
+
}
|
| 716 |
+
},
|
| 717 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 1, 2, 1, 2, 1, 2, 3], "tolerance": 0.000001 } }
|
| 718 |
+
}
|
| 719 |
+
]
|
| 720 |
+
}
|