Xenova HF Staff commited on
Commit
a0dbbbe
·
verified ·
1 Parent(s): b81405f

sync 6fdf6301e2bb

Browse files
README.md CHANGED
@@ -1,3 +1,92 @@
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
+ # com.microsoft.GatedRMSNorm
10
+
11
+ `com.microsoft` · ONNX Runtime contrib operator · contrib since_version 1
12
+
13
+ ## Description
14
+
15
+ Gated RMS normalization as used by Mamba2 and gated DeltaNet attention outputs: `Y = X * rsqrt(mean(X^2) + epsilon) * scale * SiLU(gate)`. The mean of squares is taken over each contiguous group of `C` elements, where `C` is the element count of `scale`, so a per-head norm runs on a packed `(..., H * C)` tensor with no surrounding reshape. Rows are counted flat as the element count of `X` divided by `C` and any rank of at least 1 is accepted. Every intermediate including SiLU is float32 whatever the tensor type; only the store narrows. Bfloat16 is not implemented.
16
+
17
+ See the [ONNX Runtime `GatedRMSNorm` contrib-operator spec](https://github.com/microsoft/onnxruntime/blob/main/docs/ContribOperators.md#com.microsoft.GatedRMSNorm) for the reference semantics.
18
+
19
+ ## Inputs
20
+
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- | --- |
23
+ | `xT` | `X` | `T` | — | — | Input activations with shape `(..., H * C)`. Any rank of at least 1 is accepted and the leading axes are folded into the row count. | required |
24
+ | `scaleT` | `scale` | `T` | — | — | Normalization weight. Its element count is the normalization span `C`; the standard spelling is `(C)`, and the reshaped `(1, C)` and `(C, 1)` spellings of the same vector are accepted because upstream measures the span as an element count. | required |
25
+ | `gateT` | `gate` | `T` | — | — | Gate activations with exactly the same shape as `X`; SiLU is applied to them internally. | required |
26
+
27
+ ## Outputs
28
+
29
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
30
+ | --- | --- | --- | --- | --- | --- | --- |
31
+ | `yT` | `Y` | `T` | same as `xT` | same as `xT` | Normalized, scaled and gated activations with the same shape and dtype as `X`. | required |
32
+
33
+ ## Attributes
34
+
35
+ Default values (overridable per request):
36
+
37
+ | Attribute | Default | Description |
38
+ | --- | --- | --- |
39
+ | `epsilon` | `0.00001` | Constant added to the mean of squares before the reciprocal square root. The standard default is `1e-5`; note that the Hugging Face gated-RMS archetype defaults to `1e-6` instead, so a value must be carried explicitly when porting between the two. |
40
+
41
+ ## Type constraints
42
+
43
+ | Variable | Allowed dtypes |
44
+ | --- | --- |
45
+ | `T` | `float32`, `float16` |
46
+
47
+ ## Implementation variants
48
+
49
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
50
+
51
+ - `subgroup_rows_vec4` — One aligned lane group per normalization row inside a subgroup, with the row held in registers. The fold is a segmented butterfly over the low lane bits, so several rows share a subgroup with no workgroup memory, no barrier and no idle lanes beyond the row's own power-of-two padding.
52
+ - `packed_rows_vec4` — Portable vec4 route: the workgroup is split into a power-of-two lane group per row and each group folds its own contiguous slice of the shared array, so a short row does not idle the workgroup and no subgroup support is required. It also carries rows too wide to stage in registers, which it walks twice.
53
+ - `subgroup_rows_scalar` — The barrier-free lane-group schedule for a normalization span that is not a multiple of four: the same segmented butterfly over scalar loads.
54
+ - `packed_rows_scalar` — Scalar fallback for a span that is not a multiple of four, and the route every device can select. It keeps the lane-group-per-row schedule so a narrow span still fills the workgroup.
55
+ - `subgroup_wide_rows_vec4` — Subgroup partials for a row spanning several whole subgroups; every lane reads the short row sum after one barrier.
56
+ - `subgroup_wide_rows_scalar` — Subgroup partials for a row spanning several whole subgroups; every lane reads the short row sum after one barrier.
57
+
58
+ ## Device requirements
59
+
60
+ Some implementation variants require `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
61
+
62
+ ## Files
63
+
64
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
65
+ - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
66
+ - [`test.json`](build/webgpu/test.json) — correctness cases
67
+ - [`bench.json`](build/webgpu/bench.json) — benchmark cases
68
+ - [`gated-rms-norm-rows.wgsl.jinja`](build/webgpu/gated-rms-norm-rows.wgsl.jinja)
69
+
70
+ ## Use with `@huggingface/kernels`
71
+
72
+ ```sh
73
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.3
74
+ ```
75
+
76
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
77
+
78
+ The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
79
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
80
+
81
+ Replace each `*Data` placeholder with a typed array containing the corresponding input data.
82
+
83
+ ```js
84
+ import { getKernel } from "@huggingface/kernels";
85
+
86
+ const kernel = await getKernel("webgpu-kernels/com.microsoft.GatedRMSNorm", { version: 1 });
87
+ const { yT } = await kernel({
88
+ xT: { data: xTData, shape: [3, 4] },
89
+ scaleT: { data: scaleTData, shape: [1] },
90
+ gateT: { data: gateTData, shape: [3, 4] },
91
+ });
92
+ ```
build/webgpu/bench.json ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256, 512], "LANE_WORDS": [1, 2, 4, 8] },
3
+ "cases": [
4
+ {
5
+ "name": "gated-rmsnorm-decode-h16-d128-f32",
6
+ "preset": "smoke",
7
+ "provenance": {
8
+ "model": "Qwen3.5 / Qwen3-Next gated linear attention output norm",
9
+ "notes": "One decoded token: 16 value heads of 128. The whole operator is 24 KB of traffic, so the measurement is launch latency rather than bandwidth."
10
+ },
11
+ "vars": { "dtype": "float32", "count": 2048, "span": 128 },
12
+ "inputs": {
13
+ "xT": { "shape": [1, 1, 2048], "dtype": "float32", "dist": "normal", "seed": 501, "scale": 1 },
14
+ "scaleT": { "shape": [128], "dtype": "float32", "dist": "uniform", "seed": 502, "min": 0.2, "max": 1.8 },
15
+ "gateT": { "shape": [1, 1, 2048], "dtype": "float32", "dist": "normal", "seed": 503, "scale": 2 }
16
+ },
17
+ "outputs": { "yT": { "shape": [1, 1, 2048], "dtype": "float32" } },
18
+ "bench": {
19
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
20
+ }
21
+ },
22
+ {
23
+ "name": "gated-rmsnorm-decode-h16-d128-f16",
24
+ "preset": "smoke",
25
+ "provenance": {
26
+ "model": "Qwen3.5 / Qwen3-Next gated linear attention output norm",
27
+ "notes": "The float16 decode step, the dtype the shipped model graphs use."
28
+ },
29
+ "vars": { "dtype": "float16", "count": 2048, "span": 128 },
30
+ "inputs": {
31
+ "xT": { "shape": [1, 1, 2048], "dtype": "float16", "dist": "normal", "seed": 501, "scale": 1 },
32
+ "scaleT": { "shape": [128], "dtype": "float16", "dist": "uniform", "seed": 502, "min": 0.2, "max": 1.8 },
33
+ "gateT": { "shape": [1, 1, 2048], "dtype": "float16", "dist": "normal", "seed": 503, "scale": 2 }
34
+ },
35
+ "outputs": { "yT": { "shape": [1, 1, 2048], "dtype": "float16" } },
36
+ "bench": {
37
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
38
+ }
39
+ },
40
+ {
41
+ "name": "gated-rmsnorm-prefill-s512-h16-d128-f32",
42
+ "preset": "smoke",
43
+ "provenance": {
44
+ "model": "Qwen3.5 / Qwen3-Next gated linear attention output norm",
45
+ "notes": "Measures gated RMS normalization over a 512-token prefill: 8192 rows (512 tokens x 16 heads) of 128 float32 channels each."
46
+ },
47
+ "vars": { "dtype": "float32", "count": 1048576, "span": 128 },
48
+ "inputs": {
49
+ "xT": { "shape": [1, 512, 2048], "dtype": "float32", "dist": "normal", "seed": 511, "scale": 1 },
50
+ "scaleT": { "shape": [128], "dtype": "float32", "dist": "uniform", "seed": 512, "min": 0.2, "max": 1.8 },
51
+ "gateT": { "shape": [1, 512, 2048], "dtype": "float32", "dist": "normal", "seed": 513, "scale": 2 }
52
+ },
53
+ "outputs": { "yT": { "shape": [1, 512, 2048], "dtype": "float32" } },
54
+ "bench": {
55
+ "primary": true,
56
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
57
+ }
58
+ },
59
+ {
60
+ "name": "gated-rmsnorm-prefill-s512-h16-d128-f16",
61
+ "preset": "smoke",
62
+ "provenance": {
63
+ "model": "Qwen3.5 / Qwen3-Next gated linear attention output norm",
64
+ "notes": "The float16 prefill of the same 8192 spans."
65
+ },
66
+ "vars": { "dtype": "float16", "count": 1048576, "span": 128 },
67
+ "inputs": {
68
+ "xT": { "shape": [1, 512, 2048], "dtype": "float16", "dist": "normal", "seed": 511, "scale": 1 },
69
+ "scaleT": { "shape": [128], "dtype": "float16", "dist": "uniform", "seed": 512, "min": 0.2, "max": 1.8 },
70
+ "gateT": { "shape": [1, 512, 2048], "dtype": "float16", "dist": "normal", "seed": 513, "scale": 2 }
71
+ },
72
+ "outputs": { "yT": { "shape": [1, 512, 2048], "dtype": "float16" } },
73
+ "bench": {
74
+ "primary": true,
75
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
76
+ }
77
+ },
78
+ {
79
+ "name": "gated-rmsnorm-single-group-d1536-f16",
80
+ "preset": "smoke",
81
+ "provenance": {
82
+ "notes": "Measures gated RMS normalization over 512 rows of 1536 float16 channels each, normalized as a single group (H=1) rather than split across multiple heads."
83
+ },
84
+ "vars": { "dtype": "float16", "count": 786432, "span": 1536 },
85
+ "inputs": {
86
+ "xT": { "shape": [1, 512, 1536], "dtype": "float16", "dist": "normal", "seed": 521, "scale": 1 },
87
+ "scaleT": { "shape": [1536], "dtype": "float16", "dist": "uniform", "seed": 522, "min": 0.2, "max": 1.8 },
88
+ "gateT": { "shape": [1, 512, 1536], "dtype": "float16", "dist": "normal", "seed": 523, "scale": 2 }
89
+ },
90
+ "outputs": { "yT": { "shape": [1, 512, 1536], "dtype": "float16" } },
91
+ "bench": {
92
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
93
+ }
94
+ },
95
+ {
96
+ "name": "gated-rmsnorm-single-group-d5120-f32",
97
+ "preset": "smoke",
98
+ "provenance": {
99
+ "notes": "A 5120-wide span: wide enough that the row no longer fits in registers at this workgroup width, so the statistic and the store each walk it."
100
+ },
101
+ "vars": { "dtype": "float32", "count": 1310720, "span": 5120 },
102
+ "inputs": {
103
+ "xT": { "shape": [1, 256, 5120], "dtype": "float32", "dist": "normal", "seed": 531, "scale": 1 },
104
+ "scaleT": { "shape": [5120], "dtype": "float32", "dist": "uniform", "seed": 532, "min": 0.2, "max": 1.8 },
105
+ "gateT": { "shape": [1, 256, 5120], "dtype": "float32", "dist": "normal", "seed": 533, "scale": 2 }
106
+ },
107
+ "outputs": { "yT": { "shape": [1, 256, 5120], "dtype": "float32" } },
108
+ "bench": {
109
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
110
+ }
111
+ },
112
+ {
113
+ "name": "gated-rmsnorm-small-span-d48-f16",
114
+ "preset": "smoke",
115
+ "provenance": {
116
+ "notes": "A 48-element span over the same tensor: 16384 very short rows, where a workgroup-per-row schedule would leave most of every workgroup idle."
117
+ },
118
+ "vars": { "dtype": "float16", "count": 786432, "span": 48 },
119
+ "inputs": {
120
+ "xT": { "shape": [1, 512, 1536], "dtype": "float16", "dist": "normal", "seed": 541, "scale": 1 },
121
+ "scaleT": { "shape": [48], "dtype": "float16", "dist": "uniform", "seed": 542, "min": 0.2, "max": 1.8 },
122
+ "gateT": { "shape": [1, 512, 1536], "dtype": "float16", "dist": "normal", "seed": 543, "scale": 2 }
123
+ },
124
+ "outputs": { "yT": { "shape": [1, 512, 1536], "dtype": "float16" } },
125
+ "bench": {
126
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
127
+ }
128
+ },
129
+ {
130
+ "name": "gated-rmsnorm-span-six-scalar-f32",
131
+ "preset": "smoke",
132
+ "provenance": { "notes": "A normalization span of six values leaves a two-element tail after four-wide groups." },
133
+ "vars": { "dtype": "float32", "count": 786432, "span": 6 },
134
+ "inputs": {
135
+ "xT": { "shape": [1, 512, 1536], "dtype": "float32", "dist": "normal", "seed": 551, "scale": 1 },
136
+ "scaleT": { "shape": [6], "dtype": "float32", "dist": "uniform", "seed": 552, "min": 0.2, "max": 1.8 },
137
+ "gateT": { "shape": [1, 512, 1536], "dtype": "float32", "dist": "normal", "seed": 553, "scale": 2 }
138
+ },
139
+ "outputs": { "yT": { "shape": [1, 512, 1536], "dtype": "float32" } },
140
+ "bench": {
141
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
142
+ }
143
+ },
144
+ {
145
+ "name": "gated-rmsnorm-prefill-s4096-h16-d128-f16",
146
+ "preset": "model",
147
+ "provenance": {
148
+ "model": "Qwen3.5 / Qwen3-Next gated linear attention output norm",
149
+ "notes": "A 4096-token prefill: 65536 spans, a working set far past cache so the bandwidth figure is a DRAM figure."
150
+ },
151
+ "vars": { "dtype": "float16", "count": 8388608, "span": 128 },
152
+ "inputs": {
153
+ "xT": { "shape": [1, 4096, 2048], "dtype": "float16", "dist": "normal", "seed": 561, "scale": 1 },
154
+ "scaleT": { "shape": [128], "dtype": "float16", "dist": "uniform", "seed": 562, "min": 0.2, "max": 1.8 },
155
+ "gateT": { "shape": [1, 4096, 2048], "dtype": "float16", "dist": "normal", "seed": 563, "scale": 2 }
156
+ },
157
+ "outputs": { "yT": { "shape": [1, 4096, 2048], "dtype": "float16" } },
158
+ "bench": {
159
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
160
+ }
161
+ },
162
+ {
163
+ "name": "gated-rmsnorm-prefill-s2048-h16-d128-f32",
164
+ "preset": "model",
165
+ "provenance": {
166
+ "model": "Qwen3.5 / Qwen3-Next gated linear attention output norm",
167
+ "notes": "The float32 counterpart at the same 48 MB working set."
168
+ },
169
+ "vars": { "dtype": "float32", "count": 4194304, "span": 128 },
170
+ "inputs": {
171
+ "xT": { "shape": [1, 2048, 2048], "dtype": "float32", "dist": "normal", "seed": 571, "scale": 1 },
172
+ "scaleT": { "shape": [128], "dtype": "float32", "dist": "uniform", "seed": 572, "min": 0.2, "max": 1.8 },
173
+ "gateT": { "shape": [1, 2048, 2048], "dtype": "float32", "dist": "normal", "seed": 573, "scale": 2 }
174
+ },
175
+ "outputs": { "yT": { "shape": [1, 2048, 2048], "dtype": "float32" } },
176
+ "bench": {
177
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
178
+ }
179
+ },
180
+ {
181
+ "name": "gated-rmsnorm-short-c5-r8193-float32",
182
+ "preset": "smoke",
183
+ "provenance": {
184
+ "notes": "Measures gated RMS normalization over 8193 rows of 5 channels in float32, one row past a full 256-row group. Its smaller row count sets an overhead baseline against the float32 131,073-row sibling."
185
+ },
186
+ "vars": { "dtype": "float32", "count": 40965, "span": 5 },
187
+ "inputs": {
188
+ "xT": { "shape": [1, 8193, 5], "dtype": "float32", "dist": "normal", "seed": 551, "scale": 1 },
189
+ "scaleT": { "shape": [5], "dtype": "float32", "dist": "uniform", "seed": 552, "min": 0.2, "max": 1.8 },
190
+ "gateT": { "shape": [1, 8193, 5], "dtype": "float32", "dist": "normal", "seed": 553, "scale": 2 }
191
+ },
192
+ "outputs": { "yT": { "shape": [1, 8193, 5], "dtype": "float32" } },
193
+ "bench": {
194
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
195
+ }
196
+ },
197
+ {
198
+ "name": "gated-rmsnorm-short-c5-r131073-float32",
199
+ "preset": "smoke",
200
+ "provenance": {
201
+ "notes": "Measures gated RMS normalization over 131,073 rows of 5 channels in float32, one row past a full 256-row group. Its larger row count isolates per-row cost from the 8193-row float32 sibling's per-dispatch overhead."
202
+ },
203
+ "vars": { "dtype": "float32", "count": 655365, "span": 5 },
204
+ "inputs": {
205
+ "xT": { "shape": [1, 131073, 5], "dtype": "float32", "dist": "normal", "seed": 551, "scale": 1 },
206
+ "scaleT": { "shape": [5], "dtype": "float32", "dist": "uniform", "seed": 552, "min": 0.2, "max": 1.8 },
207
+ "gateT": { "shape": [1, 131073, 5], "dtype": "float32", "dist": "normal", "seed": 553, "scale": 2 }
208
+ },
209
+ "outputs": { "yT": { "shape": [1, 131073, 5], "dtype": "float32" } },
210
+ "bench": {
211
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
212
+ }
213
+ },
214
+ {
215
+ "name": "gated-rmsnorm-short-c5-r131073-float16",
216
+ "preset": "smoke",
217
+ "provenance": {
218
+ "notes": "Measures gated RMS normalization over 131,073 rows of 5 channels in float16, one row past a full 256-row group; compares against its float32 counterpart at the same shape."
219
+ },
220
+ "vars": { "dtype": "float16", "count": 655365, "span": 5 },
221
+ "inputs": {
222
+ "xT": { "shape": [1, 131073, 5], "dtype": "float16", "dist": "normal", "seed": 551, "scale": 1 },
223
+ "scaleT": { "shape": [5], "dtype": "float16", "dist": "uniform", "seed": 552, "min": 0.2, "max": 1.8 },
224
+ "gateT": { "shape": [1, 131073, 5], "dtype": "float16", "dist": "normal", "seed": 553, "scale": 2 }
225
+ },
226
+ "outputs": { "yT": { "shape": [1, 131073, 5], "dtype": "float16" } },
227
+ "bench": {
228
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
229
+ }
230
+ },
231
+ {
232
+ "name": "gated-rmsnorm-short-c6-r131073-float16",
233
+ "preset": "smoke",
234
+ "provenance": {
235
+ "notes": "Measures gated RMS normalization over 131,073 rows of 6 channels in float16, one row past a full 256-row group."
236
+ },
237
+ "vars": { "dtype": "float16", "count": 786438, "span": 6 },
238
+ "inputs": {
239
+ "xT": { "shape": [1, 131073, 6], "dtype": "float16", "dist": "normal", "seed": 551, "scale": 1 },
240
+ "scaleT": { "shape": [6], "dtype": "float16", "dist": "uniform", "seed": 552, "min": 0.2, "max": 1.8 },
241
+ "gateT": { "shape": [1, 131073, 6], "dtype": "float16", "dist": "normal", "seed": 553, "scale": 2 }
242
+ },
243
+ "outputs": { "yT": { "shape": [1, 131073, 6], "dtype": "float16" } },
244
+ "bench": {
245
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
246
+ }
247
+ },
248
+ {
249
+ "name": "gated-rmsnorm-short-c7-r131073-float32",
250
+ "preset": "smoke",
251
+ "provenance": {
252
+ "notes": "Measures gated RMS normalization over 131,073 rows of 7 channels in float32, one row past a full 256-row group; compares against its float16 counterpart at the same shape."
253
+ },
254
+ "vars": { "dtype": "float32", "count": 917511, "span": 7 },
255
+ "inputs": {
256
+ "xT": { "shape": [1, 131073, 7], "dtype": "float32", "dist": "normal", "seed": 551, "scale": 1 },
257
+ "scaleT": { "shape": [7], "dtype": "float32", "dist": "uniform", "seed": 552, "min": 0.2, "max": 1.8 },
258
+ "gateT": { "shape": [1, 131073, 7], "dtype": "float32", "dist": "normal", "seed": 553, "scale": 2 }
259
+ },
260
+ "outputs": { "yT": { "shape": [1, 131073, 7], "dtype": "float32" } },
261
+ "bench": {
262
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
263
+ }
264
+ },
265
+ {
266
+ "name": "gated-rmsnorm-short-c7-r131073-float16",
267
+ "preset": "smoke",
268
+ "provenance": {
269
+ "notes": "Measures gated RMS normalization over 131,073 rows of 7 channels in float16, one row past a full 256-row group; compares against its float32 counterpart at the same shape."
270
+ },
271
+ "vars": { "dtype": "float16", "count": 917511, "span": 7 },
272
+ "inputs": {
273
+ "xT": { "shape": [1, 131073, 7], "dtype": "float16", "dist": "normal", "seed": 551, "scale": 1 },
274
+ "scaleT": { "shape": [7], "dtype": "float16", "dist": "uniform", "seed": 552, "min": 0.2, "max": 1.8 },
275
+ "gateT": { "shape": [1, 131073, 7], "dtype": "float16", "dist": "normal", "seed": 553, "scale": 2 }
276
+ },
277
+ "outputs": { "yT": { "shape": [1, 131073, 7], "dtype": "float16" } },
278
+ "bench": {
279
+ "metrics": [{ "type": "bandwidth", "value": "(args.count * 3 + args.span) * dtypeBytes(args.dtype)" }]
280
+ }
281
+ }
282
+ ]
283
+ }
build/webgpu/gated-rms-norm-rows.wgsl.jinja ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% if foldKind == "subgroup_full" or foldKind == "subgroup_butterfly" or foldKind == "subgroup_tree" %}
2
+ enable subgroups;
3
+ {% endif %}
4
+ {{ env.wgsl.resourceDeclarations }}
5
+
6
+ // Gated RMS normalization over each contiguous group of C elements of a row:
7
+ // Y = X * inverseSqrt(mean(X^2) + epsilon) * scale * SiLU(gate)
8
+ // C is the scale length, so a per-head norm runs on a packed (..., H * C) tensor
9
+ // with no surrounding reshape. Every intermediate is float32 and only the store
10
+ // narrows, so an f16 tensor keeps an f32 statistic.
11
+ //
12
+ // Each normalization group owns a fixed LANE GROUP rather than a whole workgroup:
13
+ // a 128-element group would otherwise leave most of a workgroup idle and still pay
14
+ // a full-width fold. LANES_PER_ROW is the smallest power of two that covers the
15
+ // row, capped by the workgroup (and, on the subgroup routes, by the subgroup) so
16
+ // several rows are resident at once.
17
+ const ROW_WORDS: u32 = {{ rowWords }}u;
18
+ const NORM_SIZE: f32 = {{ normSize }}.0;
19
+ const EPS: f32 = {{ epsilon }};
20
+ const WG: u32 = {{ workgroupSize }}u;
21
+ const LANES_PER_ROW: u32 = {{ lanesPerRow }}u;
22
+ const ROWS_PER_GROUP: u32 = {{ rowsPerGroup }}u;
23
+ {% if foldKind == "subgroup_tree" %}
24
+ var<workgroup> partials: array<f32, {{ (workgroupSize / subgroupWidth) | int }}>;
25
+ {% endif %}
26
+ {% if foldKind == "workgroup_tree" %}
27
+
28
+ // One contiguous LANES_PER_ROW slice per row. The fold never crosses a slice
29
+ // boundary, so the rows sharing this array do not interfere.
30
+ var<workgroup> partials: array<f32, WG>;
31
+ {% endif %}
32
+ {% if useVec4 %}
33
+
34
+ fn silu4(v: vec4<f32>) -> vec4<f32> {
35
+ return v / (vec4<f32>(1.0) + exp(-v));
36
+ }
37
+ {% else %}
38
+
39
+ fn silu(x: f32) -> f32 {
40
+ return x / (1.0 + exp(-x));
41
+ }
42
+ {% endif %}
43
+
44
+ @compute @workgroup_size(WG)
45
+ {% if foldKind == "subgroup_tree" %}
46
+ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(subgroup_invocation_id) sg_lane: u32, @builtin(subgroup_id) sg_id: u32) {
47
+ let tid = sg_id * {{ subgroupWidth }}u + sg_lane;
48
+ {% else %}
49
+ fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
50
+ {% endif %}
51
+ let lane = {{ "tid" if foldKind == "subgroup_tree" else "lid.x" }} % LANES_PER_ROW;
52
+ let localRow = {{ "tid" if foldKind == "subgroup_tree" else "lid.x" }} / LANES_PER_ROW;
53
+ // 2D-folded row index: wg.y carries the high bits past the dispatch's per-axis
54
+ // workgroup fold width.
55
+ let row = (wg.x + wg.y * {{ DISPATCH_FOLD_WIDTH }}u) * ROWS_PER_GROUP + localRow;
56
+ let rowValid = row < params.rows;
57
+ // A lane group past the last row folds an empty row and stores nothing, so the
58
+ // collectives and barriers below stay in uniform control flow.
59
+ let limit = select(0u, ROW_WORDS, rowValid);
60
+ let base = row * ROW_WORDS;
61
+
62
+ var acc = 0.0;
63
+ {% if stageRow %}
64
+ {% for i in range(wordsPerLane) %}
65
+ {% if useVec4 %}
66
+ var xv{{ i }} = vec4<f32>(0.0);
67
+ var gv{{ i }} = vec4<f32>(0.0);
68
+ {% else %}
69
+ var xv{{ i }} = 0.0;
70
+ var gv{{ i }} = 0.0;
71
+ {% endif %}
72
+ let c{{ i }} = lane + {{ i }}u * LANES_PER_ROW;
73
+ if (c{{ i }} < limit) {
74
+ {% if useVec4 %}
75
+ xv{{ i }} = vec4<f32>(x[base + c{{ i }}]);
76
+ gv{{ i }} = vec4<f32>(gate[base + c{{ i }}]);
77
+ acc = acc + dot(xv{{ i }}, xv{{ i }});
78
+ {% else %}
79
+ xv{{ i }} = f32(x[base + c{{ i }}]);
80
+ gv{{ i }} = f32(gate[base + c{{ i }}]);
81
+ acc = fma(xv{{ i }}, xv{{ i }}, acc);
82
+ {% endif %}
83
+ }
84
+ {% endfor %}
85
+ {% else %}
86
+ // The row does not fit in registers at this width, so the statistic pass and the
87
+ // store pass each walk it.
88
+ for (var c = lane; c < limit; c = c + LANES_PER_ROW) {
89
+ {% if useVec4 %}
90
+ let v = vec4<f32>(x[base + c]);
91
+ acc = acc + dot(v, v);
92
+ {% else %}
93
+ let v = f32(x[base + c]);
94
+ acc = fma(v, v, acc);
95
+ {% endif %}
96
+ }
97
+ {% endif %}
98
+ {% if foldKind == "subgroup_full" %}
99
+ let sumSq = subgroupAdd(acc);
100
+ {% elif foldKind == "subgroup_butterfly" %}
101
+ // Segmented butterfly: only the low bits of the lane index are exchanged, so each
102
+ // aligned group of LANES_PER_ROW lanes folds its own row and one subgroup carries
103
+ // several rows without a barrier or any workgroup memory.
104
+ var folded = acc;
105
+ {% for step in range(foldSteps) %}
106
+ folded = folded + subgroupShuffleXor(folded, 1u << {{ step }}u);
107
+ {% endfor %}
108
+ let sumSq = folded;
109
+ {% elif foldKind == "subgroup_tree" %}
110
+ // Subgroup IDs define the row coordinates independently of local invocation
111
+ // ordering. Only subgroup leaders publish; the row's short final sum is read
112
+ // by every lane, avoiding a second broadcast barrier.
113
+ let subgroupPartial = subgroupAdd(acc);
114
+ if (sg_lane == 0u) {
115
+ partials[sg_id] = subgroupPartial;
116
+ }
117
+ workgroupBarrier();
118
+ let sumSq = {% for i in range(subgroupsPerRow) %}{{ "+ " if not loop.first else "" }}partials[localRow * {{ subgroupsPerRow }}u + {{ i }}u]{% endfor %};
119
+ {% elif foldKind == "workgroup_tree" %}
120
+ partials[lid.x] = acc;
121
+ workgroupBarrier();
122
+ // LANES_PER_ROW is a power of two, so the halving stride is uniform across the
123
+ // whole workgroup and every lane reaches every barrier.
124
+ var stride = LANES_PER_ROW / 2u;
125
+ loop {
126
+ if (stride == 0u) {
127
+ break;
128
+ }
129
+ if (lane < stride) {
130
+ partials[lid.x] = partials[lid.x] + partials[lid.x + stride];
131
+ }
132
+ workgroupBarrier();
133
+ stride = stride / 2u;
134
+ }
135
+ let sumSq = partials[localRow * LANES_PER_ROW];
136
+ {% else %}
137
+ // One lane owns the whole row, so the accumulator is already the row statistic.
138
+ let sumSq = acc;
139
+ {% endif %}
140
+ let inv = inverseSqrt(sumSq / NORM_SIZE + EPS);
141
+ {% if stageRow %}
142
+ {% for i in range(wordsPerLane) %}
143
+ if (c{{ i }} < limit) {
144
+ {% if useVec4 %}
145
+ y[base + c{{ i }}] = {{ vectorScalar }}(xv{{ i }} * inv * vec4<f32>(scale[c{{ i }}]) * silu4(gv{{ i }}));
146
+ {% else %}
147
+ y[base + c{{ i }}] = {{ scalar }}(xv{{ i }} * inv * f32(scale[c{{ i }}]) * silu(gv{{ i }}));
148
+ {% endif %}
149
+ }
150
+ {% endfor %}
151
+ {% else %}
152
+ for (var c = lane; c < limit; c = c + LANES_PER_ROW) {
153
+ let idx = base + c;
154
+ {% if useVec4 %}
155
+ y[idx] = {{ vectorScalar }}(vec4<f32>(x[idx]) * inv * vec4<f32>(scale[c]) * silu4(vec4<f32>(gate[idx])));
156
+ {% else %}
157
+ y[idx] = {{ scalar }}(f32(x[idx]) * inv * f32(scale[c]) * silu(f32(gate[idx])));
158
+ {% endif %}
159
+ }
160
+ {% endif %}
161
+ }
build/webgpu/manifest.json ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "domain": "com.microsoft",
3
+ "name": "GatedRMSNorm",
4
+ "sinceVersion": 1,
5
+ "inputs": {
6
+ "xT": { "onnx": "X", "dtype": "T" },
7
+ "scaleT": { "onnx": "scale", "dtype": "T" },
8
+ "gateT": { "onnx": "gate", "dtype": "T" }
9
+ },
10
+ "outputs": { "yT": { "onnx": "Y", "dtype": "T", "rank": "ranks.xT", "shape": "shapes.xT" } },
11
+ "attributes": { "epsilon": { "default": 0.00001 } },
12
+ "typeConstraints": { "T": ["float32", "float16"] },
13
+ "tunables": { "WORKGROUP_SIZE": { "default": 256 }, "LANE_WORDS": { "default": 2 } },
14
+ "derive": {
15
+ "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
16
+ "foldedDispatchCapacity": "min(device.limits.maxComputeWorkgroupsPerDimension, 65535) * min(device.limits.maxComputeWorkgroupsPerDimension, 65535)",
17
+ "normSize": "numel(shapes.scaleT)",
18
+ "lastDim": "dim(shapes.xT, ranks.xT - 1)",
19
+ "elementCount": "numel(shapes.xT)",
20
+ "rowCount": "elementCount / max(1, normSize)",
21
+ "dtypeOk": "tensorDtypes.xT == tensorDtypes.scaleT and tensorDtypes.xT == tensorDtypes.gateT and tensorDtypes.xT == tensorDtypes.yT and f16Ok(dtypes.T)",
22
+ "shapeOk": "ranks.xT >= 1 and ranks.scaleT >= 1 and normSize > 0 and lastDim % normSize == 0 and sameShape(shapes.gateT, shapes.xT) and sameShape(shapes.yT, shapes.xT)",
23
+ "contractOk": "dtypeOk and shapeOk",
24
+ "workgroupOk": "tunables.WORKGROUP_SIZE > 0 and tunables.WORKGROUP_SIZE <= deviceWorkgroupCap and pow2ceil(tunables.WORKGROUP_SIZE) == tunables.WORKGROUP_SIZE and tunables.WORKGROUP_SIZE * 4 <= device.limits.maxComputeWorkgroupStorageSize and tunables.LANE_WORDS > 0",
25
+ "vec4Ok": "normSize % 4 == 0",
26
+ "vecWords": "normSize / 4",
27
+ "vecPreferredLanes": "max(1, min(tunables.WORKGROUP_SIZE / 2, pow2ceil(max(1, ceilDiv(vecWords, max(1, tunables.LANE_WORDS))))))",
28
+ "vecWideLanes": "max(1, min(tunables.WORKGROUP_SIZE, pow2ceil(max(1, ceilDiv(vecWords, max(1, tunables.LANE_WORDS))))))",
29
+ "vecLanesPerRow": "pick([[ceilDiv(vecWords, vecPreferredLanes) <= 8, vecPreferredLanes]], vecWideLanes)",
30
+ "vecRowsPerGroup": "tunables.WORKGROUP_SIZE / max(1, vecLanesPerRow)",
31
+ "vecWordsPerLane": "ceilDiv(vecWords, max(1, vecLanesPerRow))",
32
+ "vecRowGroups": "ceilDiv(rowCount, max(1, vecRowsPerGroup))",
33
+ "scalarPreferredLanes": "1 if normSize <= 8 else (max(1, min(tunables.WORKGROUP_SIZE / 2, pow2ceil(max(1, ceilDiv(normSize, max(1, tunables.LANE_WORDS)))))))",
34
+ "scalarWideLanes": "max(1, min(tunables.WORKGROUP_SIZE, pow2ceil(max(1, ceilDiv(normSize, max(1, tunables.LANE_WORDS))))))",
35
+ "scalarLanesPerRow": "pick([[ceilDiv(normSize, scalarPreferredLanes) <= 8, scalarPreferredLanes]], scalarWideLanes)",
36
+ "scalarRowsPerGroup": "tunables.WORKGROUP_SIZE / max(1, scalarLanesPerRow)",
37
+ "scalarWordsPerLane": "ceilDiv(normSize, max(1, scalarLanesPerRow))",
38
+ "scalarRowGroups": "ceilDiv(rowCount, max(1, scalarRowsPerGroup))",
39
+ "subgroupShapeKnown": "device.features.has(\"subgroups\") and has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize > 0 and device.adapterInfo.subgroupMinSize <= device.adapterInfo.subgroupMaxSize and device.adapterInfo.subgroupMaxSize <= tunables.WORKGROUP_SIZE and tunables.WORKGROUP_SIZE % device.adapterInfo.subgroupMaxSize == 0",
40
+ "sgMin": "device.adapterInfo.subgroupMinSize if subgroupShapeKnown else 0",
41
+ "sgFullWidth": "subgroupShapeKnown and device.adapterInfo.subgroupMinSize == device.adapterInfo.subgroupMaxSize",
42
+ "sgVecLanesPerRow": "min(sgMin, vecLanesPerRow) if subgroupShapeKnown else 1",
43
+ "sgVecRowsPerGroup": "tunables.WORKGROUP_SIZE / max(1, sgVecLanesPerRow)",
44
+ "sgVecWordsPerLane": "ceilDiv(vecWords, max(1, sgVecLanesPerRow))",
45
+ "sgVecRowGroups": "ceilDiv(rowCount, max(1, sgVecRowsPerGroup))",
46
+ "sgScalarLanesPerRow": "min(sgMin, scalarLanesPerRow) if subgroupShapeKnown else 1",
47
+ "sgScalarRowsPerGroup": "tunables.WORKGROUP_SIZE / max(1, sgScalarLanesPerRow)",
48
+ "sgScalarWordsPerLane": "ceilDiv(normSize, max(1, sgScalarLanesPerRow))",
49
+ "sgScalarRowGroups": "ceilDiv(rowCount, max(1, sgScalarRowsPerGroup))",
50
+ "vectorElement": "\"vec4<\" ~ dtypes.T ~ \">\"",
51
+ "workgroupSize": "tunables.WORKGROUP_SIZE",
52
+ "epsilon": "attrs.epsilon",
53
+ "scalar": "dtypes.T",
54
+ "vectorScalar": "vectorElement"
55
+ },
56
+ "when": ["contractOk", "workgroupOk"],
57
+ "bindings": {
58
+ "x": { "arg": "xT", "elementType": "$vectorElement" },
59
+ "scale": { "arg": "scaleT", "elementType": "$vectorElement" },
60
+ "gate": { "arg": "gateT", "elementType": "$vectorElement" },
61
+ "y": { "arg": "yT", "elementType": "$vectorElement" },
62
+ "x_x_t": { "arg": "xT", "name": "x", "elementType": "$scalar" },
63
+ "scale_main": { "arg": "scaleT", "name": "scale", "elementType": "$scalar" },
64
+ "gate_main": { "arg": "gateT", "name": "gate", "elementType": "$scalar" },
65
+ "y_y_t": { "arg": "yT", "name": "y", "elementType": "$scalar" },
66
+ "params": { "struct": [{ "name": "rows", "type": "u32", "value": "rowCount" }] }
67
+ },
68
+ "variants": [
69
+ {
70
+ "id": "subgroup_rows_vec4",
71
+ "priority": 30,
72
+ "when": ["vec4Ok", "subgroupShapeKnown", "sgVecWordsPerLane >= 1", "sgVecWordsPerLane <= 8", "sgVecRowGroups <= foldedDispatchCapacity"],
73
+ "requires": { "features": ["subgroups"] },
74
+ "derive": {
75
+ "foldKind": "\"none\" if sgVecLanesPerRow <= 1 else (\"subgroup_full\" if (sgFullWidth and sgVecLanesPerRow == sgMin) else \"subgroup_butterfly\")",
76
+ "foldSteps": "log2ceil(sgVecLanesPerRow)",
77
+ "useVec4": "true",
78
+ "stageRow": "true",
79
+ "rowWords": "vecWords",
80
+ "wordsPerLane": "sgVecWordsPerLane",
81
+ "lanesPerRow": "sgVecLanesPerRow",
82
+ "rowsPerGroup": "sgVecRowsPerGroup"
83
+ },
84
+ "passes": [
85
+ {
86
+ "id": "main",
87
+ "name": "GatedRMSNorm.SubgroupRowsVec4",
88
+ "shader": "gated-rms-norm-rows.wgsl.jinja",
89
+ "bindings": ["x", "scale", "gate", "y", "params"],
90
+ "dispatch": {
91
+ "x": "min(sgVecRowGroups, DISPATCH_FOLD_WIDTH)",
92
+ "y": "ceilDiv(sgVecRowGroups, DISPATCH_FOLD_WIDTH)"
93
+ }
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "id": "packed_rows_vec4",
99
+ "priority": 20,
100
+ "when": ["vec4Ok", "vecRowGroups <= foldedDispatchCapacity"],
101
+ "derive": {
102
+ "foldKind": "\"none\" if vecLanesPerRow <= 1 else \"workgroup_tree\"",
103
+ "foldSteps": "log2ceil(vecLanesPerRow)",
104
+ "useVec4": "true",
105
+ "stageRow": "vecWordsPerLane <= 8",
106
+ "rowWords": "vecWords",
107
+ "wordsPerLane": "vecWordsPerLane",
108
+ "lanesPerRow": "vecLanesPerRow",
109
+ "rowsPerGroup": "vecRowsPerGroup"
110
+ },
111
+ "passes": [
112
+ {
113
+ "id": "main",
114
+ "name": "GatedRMSNorm.PackedRowsVec4",
115
+ "shader": "gated-rms-norm-rows.wgsl.jinja",
116
+ "bindings": ["x", "scale", "gate", "y", "params"],
117
+ "dispatch": {
118
+ "x": "min(vecRowGroups, DISPATCH_FOLD_WIDTH)",
119
+ "y": "ceilDiv(vecRowGroups, DISPATCH_FOLD_WIDTH)"
120
+ }
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ "id": "subgroup_rows_scalar",
126
+ "priority": 10,
127
+ "when": ["subgroupShapeKnown", "sgScalarWordsPerLane >= 1", "sgScalarWordsPerLane <= 8", "sgScalarRowGroups <= foldedDispatchCapacity"],
128
+ "requires": { "features": ["subgroups"] },
129
+ "derive": {
130
+ "foldKind": "\"none\" if sgScalarLanesPerRow <= 1 else (\"subgroup_full\" if (sgFullWidth and sgScalarLanesPerRow == sgMin) else \"subgroup_butterfly\")",
131
+ "foldSteps": "log2ceil(sgScalarLanesPerRow)",
132
+ "useVec4": "false",
133
+ "stageRow": "true",
134
+ "rowWords": "normSize",
135
+ "wordsPerLane": "sgScalarWordsPerLane",
136
+ "lanesPerRow": "sgScalarLanesPerRow",
137
+ "rowsPerGroup": "sgScalarRowsPerGroup"
138
+ },
139
+ "passes": [
140
+ {
141
+ "id": "main",
142
+ "name": "GatedRMSNorm.SubgroupRowsScalar",
143
+ "shader": "gated-rms-norm-rows.wgsl.jinja",
144
+ "bindings": ["x_x_t", "scale_main", "gate_main", "y_y_t", "params"],
145
+ "dispatch": {
146
+ "x": "min(sgScalarRowGroups, DISPATCH_FOLD_WIDTH)",
147
+ "y": "ceilDiv(sgScalarRowGroups, DISPATCH_FOLD_WIDTH)"
148
+ }
149
+ }
150
+ ]
151
+ },
152
+ {
153
+ "id": "packed_rows_scalar",
154
+ "priority": 0,
155
+ "when": ["scalarRowGroups <= foldedDispatchCapacity"],
156
+ "derive": {
157
+ "foldKind": "\"none\" if scalarLanesPerRow <= 1 else \"workgroup_tree\"",
158
+ "foldSteps": "log2ceil(scalarLanesPerRow)",
159
+ "useVec4": "false",
160
+ "stageRow": "scalarWordsPerLane <= 8",
161
+ "rowWords": "normSize",
162
+ "wordsPerLane": "scalarWordsPerLane",
163
+ "lanesPerRow": "scalarLanesPerRow",
164
+ "rowsPerGroup": "scalarRowsPerGroup"
165
+ },
166
+ "passes": [
167
+ {
168
+ "id": "main",
169
+ "name": "GatedRMSNorm.PackedRowsScalar",
170
+ "shader": "gated-rms-norm-rows.wgsl.jinja",
171
+ "bindings": ["x_x_t", "scale_main", "gate_main", "y_y_t", "params"],
172
+ "dispatch": {
173
+ "x": "min(scalarRowGroups, DISPATCH_FOLD_WIDTH)",
174
+ "y": "ceilDiv(scalarRowGroups, DISPATCH_FOLD_WIDTH)"
175
+ }
176
+ }
177
+ ]
178
+ },
179
+ {
180
+ "id": "subgroup_wide_rows_vec4",
181
+ "priority": 25,
182
+ "when": ["vec4Ok", "vecRowGroups <= foldedDispatchCapacity", "dtypes.T == \"f32\"", "sgFullWidth", "device.wgslLanguageFeatures.has(\"subgroup_id\")", "vecLanesPerRow > sgMin", "vecLanesPerRow % max(1, sgMin) == 0"],
183
+ "requires": { "features": ["subgroups"] },
184
+ "derive": {
185
+ "foldKind": "\"subgroup_tree\"",
186
+ "useVec4": "true",
187
+ "stageRow": "vecWordsPerLane <= 8",
188
+ "rowWords": "vecWords",
189
+ "wordsPerLane": "vecWordsPerLane",
190
+ "lanesPerRow": "vecLanesPerRow",
191
+ "rowsPerGroup": "vecRowsPerGroup",
192
+ "subgroupWidth": "sgMin",
193
+ "subgroupsPerRow": "vecLanesPerRow / max(1, sgMin)"
194
+ },
195
+ "passes": [
196
+ {
197
+ "id": "main",
198
+ "name": "GatedRMSNorm.SubgroupWideRowsVec4",
199
+ "shader": "gated-rms-norm-rows.wgsl.jinja",
200
+ "bindings": ["x", "scale", "gate", "y", "params"],
201
+ "dispatch": {
202
+ "x": "min(vecRowGroups, DISPATCH_FOLD_WIDTH)",
203
+ "y": "ceilDiv(vecRowGroups, DISPATCH_FOLD_WIDTH)"
204
+ }
205
+ }
206
+ ]
207
+ },
208
+ {
209
+ "id": "subgroup_wide_rows_scalar",
210
+ "priority": 5,
211
+ "when": ["scalarRowGroups <= foldedDispatchCapacity", "dtypes.T == \"f32\"", "sgFullWidth", "device.wgslLanguageFeatures.has(\"subgroup_id\")", "scalarLanesPerRow > sgMin", "scalarLanesPerRow % max(1, sgMin) == 0"],
212
+ "requires": { "features": ["subgroups"] },
213
+ "derive": {
214
+ "foldKind": "\"subgroup_tree\"",
215
+ "useVec4": "false",
216
+ "stageRow": "scalarWordsPerLane <= 8",
217
+ "rowWords": "normSize",
218
+ "wordsPerLane": "scalarWordsPerLane",
219
+ "lanesPerRow": "scalarLanesPerRow",
220
+ "rowsPerGroup": "scalarRowsPerGroup",
221
+ "subgroupWidth": "sgMin",
222
+ "subgroupsPerRow": "scalarLanesPerRow / max(1, sgMin)"
223
+ },
224
+ "passes": [
225
+ {
226
+ "id": "main",
227
+ "name": "GatedRMSNorm.SubgroupWideRowsScalar",
228
+ "shader": "gated-rms-norm-rows.wgsl.jinja",
229
+ "bindings": ["x_x_t", "scale_main", "gate_main", "y_y_t", "params"],
230
+ "dispatch": {
231
+ "x": "min(scalarRowGroups, DISPATCH_FOLD_WIDTH)",
232
+ "y": "ceilDiv(scalarRowGroups, DISPATCH_FOLD_WIDTH)"
233
+ }
234
+ }
235
+ ]
236
+ }
237
+ ]
238
+ }
build/webgpu/metadata.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "com.microsoft.GatedRMSNorm",
3
+ "id": "_com_microsoft_gatedrmsnorm_webgpu_3fd919f",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "backend": { "type": "webgpu" },
7
+ "digest": {
8
+ "algorithm": "sha256",
9
+ "files": {
10
+ "bench.json": "E5LQpx8I5PNcqbNTX4KyCxcXvNNp3cHMKCbDTk1JlFI=",
11
+ "gated-rms-norm-rows.wgsl.jinja": "FNlhlwGawl2E/vwWNng/ZX+vThDezoBS43E/hxT1/9k=",
12
+ "manifest.json": "/o1FvJuHt1tdlx9h75CH1UA2wgYdt880zc3UQmntRrg=",
13
+ "test.json": "o0Og3R+dIxLG/ZwxG7CgIS7tbiRjxtApNu9xFzFxcZg="
14
+ }
15
+ },
16
+ "provenance": { "kernel": { "sha": "6fdf6301e2bbcc2f03bf1eaf493b7ad55ef33afc", "dirty": false } },
17
+ "webgpu": {
18
+ "manifestSpec": "2.1",
19
+ "variants": {
20
+ "subgroup_rows_vec4": ["gated-rms-norm-rows.wgsl.jinja"],
21
+ "packed_rows_vec4": ["gated-rms-norm-rows.wgsl.jinja"],
22
+ "subgroup_rows_scalar": ["gated-rms-norm-rows.wgsl.jinja"],
23
+ "packed_rows_scalar": ["gated-rms-norm-rows.wgsl.jinja"],
24
+ "subgroup_wide_rows_vec4": ["gated-rms-norm-rows.wgsl.jinja"],
25
+ "subgroup_wide_rows_scalar": ["gated-rms-norm-rows.wgsl.jinja"]
26
+ }
27
+ }
28
+ }
build/webgpu/test.json ADDED
@@ -0,0 +1,881 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cases": [
3
+ {
4
+ "name": "ort_float_per_head",
5
+ "provenance": {
6
+ "source": "ORT: onnxruntime/test/contrib_ops/linear_attention_gates_op_test.cc",
7
+ "test": "ContribOpGatedRMSNormTest.Float_PerHead",
8
+ "notes": "Upstream draws its tensors from a seeded mt19937 and recomputes the expectation in the test body, so the geometry and epsilon transfer but the values do not. Batch 1, sequence 4, 32 heads of 128."
9
+ },
10
+ "attrs": { "epsilon": 0.000001 },
11
+ "inputs": {
12
+ "xT": {
13
+ "dtype": "float32",
14
+ "shape": [1, 4, 4096],
15
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29 }
16
+ },
17
+ "scaleT": {
18
+ "dtype": "float32",
19
+ "shape": [128],
20
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 0.5, "offset": 1.0 }
21
+ },
22
+ "gateT": {
23
+ "dtype": "float32",
24
+ "shape": [1, 4, 4096],
25
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.19, "scale": 2.0 }
26
+ }
27
+ },
28
+ "outputs": { "yT": { "dtype": "float32", "shape": [1, 4, 4096], "tolerance": 0.000002, "relTolerance": 0.00002 } }
29
+ },
30
+ {
31
+ "name": "ort_float_single_group",
32
+ "provenance": {
33
+ "source": "ORT: onnxruntime/test/contrib_ops/linear_attention_gates_op_test.cc",
34
+ "test": "ContribOpGatedRMSNormTest.Float_SingleGroup",
35
+ "notes": "One normalization group per row (H = 1), the case where a per-channel and a shared-group weight index agree, so it cannot distinguish them on its own."
36
+ },
37
+ "attrs": { "epsilon": 0.00001 },
38
+ "inputs": {
39
+ "xT": {
40
+ "dtype": "float32",
41
+ "shape": [2, 3, 512],
42
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.41 }
43
+ },
44
+ "scaleT": {
45
+ "dtype": "float32",
46
+ "shape": [512],
47
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.5, "offset": 1.0 }
48
+ },
49
+ "gateT": {
50
+ "dtype": "float32",
51
+ "shape": [2, 3, 512],
52
+ "data": { "kind": "fillFloat32", "sinStep": 0.05, "cosStep": 0.43, "scale": 2.0 }
53
+ }
54
+ },
55
+ "outputs": { "yT": { "dtype": "float32", "shape": [2, 3, 512], "tolerance": 0.000002, "relTolerance": 0.00002 } }
56
+ },
57
+ {
58
+ "name": "ort_float16_per_head",
59
+ "provenance": {
60
+ "source": "ORT: onnxruntime/test/contrib_ops/linear_attention_gates_op_test.cc",
61
+ "test": "ContribOpGatedRMSNormTest.Float16_PerHead"
62
+ },
63
+ "attrs": { "epsilon": 0.000001 },
64
+ "inputs": {
65
+ "xT": {
66
+ "dtype": "float16",
67
+ "shape": [1, 4, 4096],
68
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.29 }
69
+ },
70
+ "scaleT": {
71
+ "dtype": "float16",
72
+ "shape": [128],
73
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 0.5, "offset": 1.0 }
74
+ },
75
+ "gateT": {
76
+ "dtype": "float16",
77
+ "shape": [1, 4, 4096],
78
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.19, "scale": 2.0 }
79
+ }
80
+ },
81
+ "outputs": { "yT": { "dtype": "float16", "shape": [1, 4, 4096], "tolerance": 0.0005, "relTolerance": 0.002 } }
82
+ },
83
+ {
84
+ "name": "ort_float16_prefill",
85
+ "provenance": {
86
+ "source": "ORT: onnxruntime/test/contrib_ops/linear_attention_gates_op_test.cc",
87
+ "test": "ContribOpGatedRMSNormTest.Float16_Prefill",
88
+ "notes": "Batch 2, sequence 17: an odd token count so the row count is not a multiple of the rows a workgroup carries."
89
+ },
90
+ "attrs": { "epsilon": 0.000001 },
91
+ "inputs": {
92
+ "xT": {
93
+ "dtype": "float16",
94
+ "shape": [2, 17, 4096],
95
+ "data": { "kind": "fillFloat32", "sinStep": 0.061, "cosStep": 0.257 }
96
+ },
97
+ "scaleT": {
98
+ "dtype": "float16",
99
+ "shape": [128],
100
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 0.5, "offset": 1.0 }
101
+ },
102
+ "gateT": {
103
+ "dtype": "float16",
104
+ "shape": [2, 17, 4096],
105
+ "data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.23, "scale": 2.0 }
106
+ }
107
+ },
108
+ "outputs": { "yT": { "dtype": "float16", "shape": [2, 17, 4096], "tolerance": 0.0005, "relTolerance": 0.002 } }
109
+ },
110
+ {
111
+ "name": "ort_float16_small_norm_size",
112
+ "provenance": {
113
+ "source": "ORT: onnxruntime/test/contrib_ops/linear_attention_gates_op_test.cc",
114
+ "test": "ContribOpGatedRMSNormTest.Float16_SmallNormSize",
115
+ "notes": "A 48-element span: a multiple of four but not of the lane group a wide row would use, so the trailing lanes of the group fold nothing."
116
+ },
117
+ "attrs": { "epsilon": 0.000001 },
118
+ "inputs": {
119
+ "xT": {
120
+ "dtype": "float16",
121
+ "shape": [1, 4, 384],
122
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.33 }
123
+ },
124
+ "scaleT": {
125
+ "dtype": "float16",
126
+ "shape": [48],
127
+ "data": { "kind": "fillFloat32", "sinStep": 0.41, "cosStep": 0.07, "scale": 0.5, "offset": 1.0 }
128
+ },
129
+ "gateT": {
130
+ "dtype": "float16",
131
+ "shape": [1, 4, 384],
132
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.27, "scale": 2.0 }
133
+ }
134
+ },
135
+ "outputs": { "yT": { "dtype": "float16", "shape": [1, 4, 384], "tolerance": 0.0005, "relTolerance": 0.002 } }
136
+ },
137
+ {
138
+ "name": "ort_float16_large_norm_size",
139
+ "provenance": {
140
+ "source": "ORT: onnxruntime/test/contrib_ops/linear_attention_gates_op_test.cc",
141
+ "test": "ContribOpGatedRMSNormTest.Float16_LargeNormSize"
142
+ },
143
+ "attrs": { "epsilon": 0.000001 },
144
+ "inputs": {
145
+ "xT": {
146
+ "dtype": "float16",
147
+ "shape": [1, 2, 3072],
148
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.053 }
149
+ },
150
+ "scaleT": {
151
+ "dtype": "float16",
152
+ "shape": [1536],
153
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.013, "scale": 0.5, "offset": 1.0 }
154
+ },
155
+ "gateT": {
156
+ "dtype": "float16",
157
+ "shape": [1, 2, 3072],
158
+ "data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.071, "scale": 2.0 }
159
+ }
160
+ },
161
+ "outputs": { "yT": { "dtype": "float16", "shape": [1, 2, 3072], "tolerance": 0.0008, "relTolerance": 0.003 } }
162
+ },
163
+ {
164
+ "name": "rank1_decode_row",
165
+ "inputs": {
166
+ "xT": {
167
+ "dtype": "float32",
168
+ "shape": [128],
169
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.39 }
170
+ },
171
+ "scaleT": {
172
+ "dtype": "float32",
173
+ "shape": [128],
174
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 0.5, "offset": 1.0 }
175
+ },
176
+ "gateT": {
177
+ "dtype": "float32",
178
+ "shape": [128],
179
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.47, "scale": 2.0 }
180
+ }
181
+ },
182
+ "outputs": { "yT": { "dtype": "float32", "shape": [128], "tolerance": 0.000002, "relTolerance": 0.00002 } }
183
+ },
184
+ {
185
+ "name": "rank2_many_rows",
186
+ "inputs": {
187
+ "xT": {
188
+ "dtype": "float32",
189
+ "shape": [512, 256],
190
+ "data": { "kind": "fillFloat32", "sinStep": 0.043, "cosStep": 0.121 }
191
+ },
192
+ "scaleT": {
193
+ "dtype": "float32",
194
+ "shape": [128],
195
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 0.5, "offset": 1.0 }
196
+ },
197
+ "gateT": {
198
+ "dtype": "float32",
199
+ "shape": [512, 256],
200
+ "data": { "kind": "fillFloat32", "sinStep": 0.083, "cosStep": 0.151, "scale": 2.0 }
201
+ }
202
+ },
203
+ "outputs": { "yT": { "dtype": "float32", "shape": [512, 256], "tolerance": 0.000002, "relTolerance": 0.00002 } }
204
+ },
205
+ {
206
+ "name": "rank4_batched_multi_group",
207
+ "inputs": {
208
+ "xT": {
209
+ "dtype": "float16",
210
+ "shape": [2, 3, 4, 256],
211
+ "data": { "kind": "fillFloat32", "sinStep": 0.067, "cosStep": 0.139 }
212
+ },
213
+ "scaleT": {
214
+ "dtype": "float16",
215
+ "shape": [64],
216
+ "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.17, "scale": 0.5, "offset": 1.0 }
217
+ },
218
+ "gateT": {
219
+ "dtype": "float16",
220
+ "shape": [2, 3, 4, 256],
221
+ "data": { "kind": "fillFloat32", "sinStep": 0.101, "cosStep": 0.211, "scale": 2.0 }
222
+ }
223
+ },
224
+ "outputs": { "yT": { "dtype": "float16", "shape": [2, 3, 4, 256], "tolerance": 0.0005, "relTolerance": 0.002 } }
225
+ },
226
+ {
227
+ "name": "scale_spelled_leading_one",
228
+ "inputs": {
229
+ "xT": {
230
+ "dtype": "float32",
231
+ "shape": [8, 128],
232
+ "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.09 }
233
+ },
234
+ "scaleT": {
235
+ "dtype": "float32",
236
+ "shape": [1, 128],
237
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 0.5, "offset": 1.0 }
238
+ },
239
+ "gateT": {
240
+ "dtype": "float32",
241
+ "shape": [8, 128],
242
+ "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.53, "scale": 2.0 }
243
+ }
244
+ },
245
+ "outputs": { "yT": { "dtype": "float32", "shape": [8, 128], "tolerance": 0.000002, "relTolerance": 0.00002 } }
246
+ },
247
+ {
248
+ "name": "scale_spelled_trailing_one",
249
+ "inputs": {
250
+ "xT": {
251
+ "dtype": "float32",
252
+ "shape": [8, 128],
253
+ "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.09 }
254
+ },
255
+ "scaleT": {
256
+ "dtype": "float32",
257
+ "shape": [128, 1],
258
+ "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 0.5, "offset": 1.0 }
259
+ },
260
+ "gateT": {
261
+ "dtype": "float32",
262
+ "shape": [8, 128],
263
+ "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.53, "scale": 2.0 }
264
+ }
265
+ },
266
+ "outputs": { "yT": { "dtype": "float32", "shape": [8, 128], "tolerance": 0.000002, "relTolerance": 0.00002 } }
267
+ },
268
+ {
269
+ "name": "span_six_scalar_route",
270
+ "inputs": {
271
+ "xT": {
272
+ "dtype": "float32",
273
+ "shape": [5, 12],
274
+ "data": { "kind": "fillFloat32", "sinStep": 0.27, "cosStep": 0.61 }
275
+ },
276
+ "scaleT": {
277
+ "dtype": "float32",
278
+ "shape": [6],
279
+ "data": { "kind": "fillFloat32", "sinStep": 0.44, "cosStep": 0.22, "scale": 0.5, "offset": 1.0 }
280
+ },
281
+ "gateT": {
282
+ "dtype": "float32",
283
+ "shape": [5, 12],
284
+ "data": { "kind": "fillFloat32", "sinStep": 0.35, "cosStep": 0.14, "scale": 2.0 }
285
+ }
286
+ },
287
+ "outputs": { "yT": { "dtype": "float32", "shape": [5, 12], "tolerance": 0.000002, "relTolerance": 0.00002 } }
288
+ },
289
+ {
290
+ "name": "span_one_single_lane_fold",
291
+ "inputs": {
292
+ "xT": {
293
+ "dtype": "float32",
294
+ "shape": [3, 4],
295
+ "data": { "kind": "fillFloat32", "sinStep": 0.51, "cosStep": 0.13 }
296
+ },
297
+ "scaleT": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [1.75] } },
298
+ "gateT": {
299
+ "dtype": "float32",
300
+ "shape": [3, 4],
301
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.63, "scale": 2.0 }
302
+ }
303
+ },
304
+ "outputs": { "yT": { "dtype": "float32", "shape": [3, 4], "tolerance": 0.000002, "relTolerance": 0.00002 } }
305
+ },
306
+ {
307
+ "name": "span_four_single_word",
308
+ "inputs": {
309
+ "xT": {
310
+ "dtype": "float32",
311
+ "shape": [16, 4],
312
+ "data": { "kind": "fillFloat32", "sinStep": 0.47, "cosStep": 0.23 }
313
+ },
314
+ "scaleT": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [0.5, 1.25, -0.75, 2.0] } },
315
+ "gateT": {
316
+ "dtype": "float32",
317
+ "shape": [16, 4],
318
+ "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.71, "scale": 2.0 }
319
+ }
320
+ },
321
+ "outputs": { "yT": { "dtype": "float32", "shape": [16, 4], "tolerance": 0.000002, "relTolerance": 0.00002 } }
322
+ },
323
+ {
324
+ "name": "span_four_single_word_f16",
325
+ "inputs": {
326
+ "xT": {
327
+ "dtype": "float16",
328
+ "shape": [16, 4],
329
+ "data": { "kind": "fillFloat32", "sinStep": 0.47, "cosStep": 0.23 }
330
+ },
331
+ "scaleT": { "dtype": "float16", "shape": [4], "data": { "kind": "values", "values": [0.5, 1.25, -0.75, 2.0] } },
332
+ "gateT": {
333
+ "dtype": "float16",
334
+ "shape": [16, 4],
335
+ "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.71, "scale": 2.0 }
336
+ }
337
+ },
338
+ "outputs": { "yT": { "dtype": "float16", "shape": [16, 4], "tolerance": 0.0005, "relTolerance": 0.002 } }
339
+ },
340
+ {
341
+ "name": "epsilon_dominates_statistic",
342
+ "attrs": { "epsilon": 100 },
343
+ "inputs": {
344
+ "xT": {
345
+ "dtype": "float32",
346
+ "shape": [4, 16],
347
+ "data": { "kind": "fillFloat32", "sinStep": 0.33, "cosStep": 0.77 }
348
+ },
349
+ "scaleT": {
350
+ "dtype": "float32",
351
+ "shape": [16],
352
+ "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.49, "scale": 0.5, "offset": 1.0 }
353
+ },
354
+ "gateT": {
355
+ "dtype": "float32",
356
+ "shape": [4, 16],
357
+ "data": { "kind": "fillFloat32", "sinStep": 0.57, "cosStep": 0.17, "scale": 2.0 }
358
+ }
359
+ },
360
+ "outputs": { "yT": { "dtype": "float32", "shape": [4, 16], "tolerance": 0.000002, "relTolerance": 0.00002 } }
361
+ },
362
+ {
363
+ "name": "gate_saturating_tails",
364
+ "inputs": {
365
+ "xT": {
366
+ "dtype": "float32",
367
+ "shape": [4, 64],
368
+ "data": { "kind": "fillFloat32", "sinStep": 0.25, "cosStep": 0.55 }
369
+ },
370
+ "scaleT": {
371
+ "dtype": "float32",
372
+ "shape": [64],
373
+ "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.35, "scale": 0.5, "offset": 1.0 }
374
+ },
375
+ "gateT": {
376
+ "dtype": "float32",
377
+ "shape": [4, 64],
378
+ "data": { "kind": "fillFloat32", "sinStep": 0.45, "cosStep": 0.85, "scale": 12.0 }
379
+ }
380
+ },
381
+ "outputs": { "yT": { "dtype": "float32", "shape": [4, 64], "tolerance": 0.000002, "relTolerance": 0.00002 } }
382
+ },
383
+ {
384
+ "name": "span_2048_two_words_per_lane",
385
+ "inputs": {
386
+ "xT": {
387
+ "dtype": "float32",
388
+ "shape": [4, 2048],
389
+ "data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.047 }
390
+ },
391
+ "scaleT": {
392
+ "dtype": "float32",
393
+ "shape": [2048],
394
+ "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.007, "scale": 0.5, "offset": 1.0 }
395
+ },
396
+ "gateT": {
397
+ "dtype": "float32",
398
+ "shape": [4, 2048],
399
+ "data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.059, "scale": 2.0 }
400
+ }
401
+ },
402
+ "outputs": { "yT": { "dtype": "float32", "shape": [4, 2048], "tolerance": 0.000004, "relTolerance": 0.00004 } }
403
+ },
404
+ {
405
+ "name": "span_16384_streaming_route",
406
+ "inputs": {
407
+ "xT": {
408
+ "dtype": "float32",
409
+ "shape": [2, 16384],
410
+ "data": { "kind": "fillFloat32", "sinStep": 0.0031, "cosStep": 0.0079 }
411
+ },
412
+ "scaleT": {
413
+ "dtype": "float32",
414
+ "shape": [16384],
415
+ "data": { "kind": "fillFloat32", "sinStep": 0.0011, "cosStep": 0.0023, "scale": 0.5, "offset": 1.0 }
416
+ },
417
+ "gateT": {
418
+ "dtype": "float32",
419
+ "shape": [2, 16384],
420
+ "data": { "kind": "fillFloat32", "sinStep": 0.0043, "cosStep": 0.0017, "scale": 2.0 }
421
+ }
422
+ },
423
+ "outputs": { "yT": { "dtype": "float32", "shape": [2, 16384], "tolerance": 0.00001, "relTolerance": 0.0001 } }
424
+ },
425
+ {
426
+ "name": "span_16384_streaming_route_f16",
427
+ "inputs": {
428
+ "xT": {
429
+ "dtype": "float16",
430
+ "shape": [2, 16384],
431
+ "data": { "kind": "fillFloat32", "sinStep": 0.0031, "cosStep": 0.0079 }
432
+ },
433
+ "scaleT": {
434
+ "dtype": "float16",
435
+ "shape": [16384],
436
+ "data": { "kind": "fillFloat32", "sinStep": 0.0011, "cosStep": 0.0023, "scale": 0.5, "offset": 1.0 }
437
+ },
438
+ "gateT": {
439
+ "dtype": "float16",
440
+ "shape": [2, 16384],
441
+ "data": { "kind": "fillFloat32", "sinStep": 0.0043, "cosStep": 0.0017, "scale": 2.0 }
442
+ }
443
+ },
444
+ "outputs": { "yT": { "dtype": "float16", "shape": [2, 16384], "tolerance": 0.0008, "relTolerance": 0.003 } }
445
+ },
446
+ {
447
+ "name": "wide_subgroup_span_1026_rows_3_wg_256",
448
+ "inputs": {
449
+ "xT": {
450
+ "dtype": "float32",
451
+ "shape": [3, 1026],
452
+ "data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.047 }
453
+ },
454
+ "scaleT": {
455
+ "dtype": "float32",
456
+ "shape": [1026],
457
+ "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.007, "scale": 0.5, "offset": 1.0 }
458
+ },
459
+ "gateT": {
460
+ "dtype": "float32",
461
+ "shape": [3, 1026],
462
+ "data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.059, "scale": 2.0 }
463
+ }
464
+ },
465
+ "outputs": { "yT": { "dtype": "float32", "shape": [3, 1026], "tolerance": 0.000004, "relTolerance": 0.00004 } },
466
+ "tunables": { "WORKGROUP_SIZE": 256 }
467
+ },
468
+ {
469
+ "name": "wide_subgroup_span_1028_rows_3_wg_256",
470
+ "inputs": {
471
+ "xT": {
472
+ "dtype": "float32",
473
+ "shape": [3, 1028],
474
+ "data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.047 }
475
+ },
476
+ "scaleT": {
477
+ "dtype": "float32",
478
+ "shape": [1028],
479
+ "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.007, "scale": 0.5, "offset": 1.0 }
480
+ },
481
+ "gateT": {
482
+ "dtype": "float32",
483
+ "shape": [3, 1028],
484
+ "data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.059, "scale": 2.0 }
485
+ }
486
+ },
487
+ "outputs": { "yT": { "dtype": "float32", "shape": [3, 1028], "tolerance": 0.000004, "relTolerance": 0.00004 } },
488
+ "tunables": { "WORKGROUP_SIZE": 256 }
489
+ },
490
+ {
491
+ "name": "wide_subgroup_span_2050_rows_3_wg_64",
492
+ "inputs": {
493
+ "xT": {
494
+ "dtype": "float32",
495
+ "shape": [3, 2050],
496
+ "data": { "kind": "fillFloat32", "sinStep": 0.011, "cosStep": 0.047 }
497
+ },
498
+ "scaleT": {
499
+ "dtype": "float32",
500
+ "shape": [2050],
501
+ "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.007, "scale": 0.5, "offset": 1.0 }
502
+ },
503
+ "gateT": {
504
+ "dtype": "float32",
505
+ "shape": [3, 2050],
506
+ "data": { "kind": "fillFloat32", "sinStep": 0.023, "cosStep": 0.059, "scale": 2.0 }
507
+ }
508
+ },
509
+ "outputs": { "yT": { "dtype": "float32", "shape": [3, 2050], "tolerance": 0.000004, "relTolerance": 0.00004 } },
510
+ "tunables": { "WORKGROUP_SIZE": 64 }
511
+ },
512
+ {
513
+ "name": "short_scalar_float32_c1_r257",
514
+ "inputs": {
515
+ "xT": {
516
+ "dtype": "float32",
517
+ "shape": [257, 1],
518
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
519
+ },
520
+ "scaleT": {
521
+ "dtype": "float32",
522
+ "shape": [1],
523
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
524
+ },
525
+ "gateT": {
526
+ "dtype": "float32",
527
+ "shape": [257, 1],
528
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
529
+ }
530
+ },
531
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 1], "tolerance": 0.000002, "relTolerance": 0.00002 } },
532
+ "provenance": {
533
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
534
+ }
535
+ },
536
+ {
537
+ "name": "short_scalar_float32_c2_r257",
538
+ "inputs": {
539
+ "xT": {
540
+ "dtype": "float32",
541
+ "shape": [257, 2],
542
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
543
+ },
544
+ "scaleT": {
545
+ "dtype": "float32",
546
+ "shape": [2],
547
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
548
+ },
549
+ "gateT": {
550
+ "dtype": "float32",
551
+ "shape": [257, 2],
552
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
553
+ }
554
+ },
555
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 2], "tolerance": 0.000002, "relTolerance": 0.00002 } },
556
+ "provenance": {
557
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
558
+ }
559
+ },
560
+ {
561
+ "name": "short_scalar_float32_c3_r257",
562
+ "inputs": {
563
+ "xT": {
564
+ "dtype": "float32",
565
+ "shape": [257, 3],
566
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
567
+ },
568
+ "scaleT": {
569
+ "dtype": "float32",
570
+ "shape": [3],
571
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
572
+ },
573
+ "gateT": {
574
+ "dtype": "float32",
575
+ "shape": [257, 3],
576
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
577
+ }
578
+ },
579
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 3], "tolerance": 0.000002, "relTolerance": 0.00002 } },
580
+ "provenance": {
581
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
582
+ }
583
+ },
584
+ {
585
+ "name": "short_scalar_float32_c5_r257",
586
+ "inputs": {
587
+ "xT": {
588
+ "dtype": "float32",
589
+ "shape": [257, 5],
590
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
591
+ },
592
+ "scaleT": {
593
+ "dtype": "float32",
594
+ "shape": [5],
595
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
596
+ },
597
+ "gateT": {
598
+ "dtype": "float32",
599
+ "shape": [257, 5],
600
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
601
+ }
602
+ },
603
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 5], "tolerance": 0.000002, "relTolerance": 0.00002 } },
604
+ "provenance": {
605
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
606
+ }
607
+ },
608
+ {
609
+ "name": "short_scalar_float32_c6_r257",
610
+ "inputs": {
611
+ "xT": {
612
+ "dtype": "float32",
613
+ "shape": [257, 6],
614
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
615
+ },
616
+ "scaleT": {
617
+ "dtype": "float32",
618
+ "shape": [6],
619
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
620
+ },
621
+ "gateT": {
622
+ "dtype": "float32",
623
+ "shape": [257, 6],
624
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
625
+ }
626
+ },
627
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 6], "tolerance": 0.000002, "relTolerance": 0.00002 } },
628
+ "provenance": {
629
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
630
+ }
631
+ },
632
+ {
633
+ "name": "short_scalar_float32_c7_r257",
634
+ "inputs": {
635
+ "xT": {
636
+ "dtype": "float32",
637
+ "shape": [257, 7],
638
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
639
+ },
640
+ "scaleT": {
641
+ "dtype": "float32",
642
+ "shape": [7],
643
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
644
+ },
645
+ "gateT": {
646
+ "dtype": "float32",
647
+ "shape": [257, 7],
648
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
649
+ }
650
+ },
651
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 7], "tolerance": 0.000002, "relTolerance": 0.00002 } },
652
+ "provenance": {
653
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
654
+ }
655
+ },
656
+ {
657
+ "name": "short_scalar_float32_c8_r257",
658
+ "inputs": {
659
+ "xT": {
660
+ "dtype": "float32",
661
+ "shape": [257, 8],
662
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
663
+ },
664
+ "scaleT": {
665
+ "dtype": "float32",
666
+ "shape": [8],
667
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
668
+ },
669
+ "gateT": {
670
+ "dtype": "float32",
671
+ "shape": [257, 8],
672
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
673
+ }
674
+ },
675
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 8], "tolerance": 0.000002, "relTolerance": 0.00002 } },
676
+ "provenance": {
677
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
678
+ }
679
+ },
680
+ {
681
+ "name": "short_scalar_float32_c9_r257",
682
+ "inputs": {
683
+ "xT": {
684
+ "dtype": "float32",
685
+ "shape": [257, 9],
686
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
687
+ },
688
+ "scaleT": {
689
+ "dtype": "float32",
690
+ "shape": [9],
691
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
692
+ },
693
+ "gateT": {
694
+ "dtype": "float32",
695
+ "shape": [257, 9],
696
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
697
+ }
698
+ },
699
+ "outputs": { "yT": { "dtype": "float32", "shape": [257, 9], "tolerance": 0.000002, "relTolerance": 0.00002 } },
700
+ "provenance": {
701
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
702
+ }
703
+ },
704
+ {
705
+ "name": "short_scalar_float16_c3_r257",
706
+ "inputs": {
707
+ "xT": {
708
+ "dtype": "float16",
709
+ "shape": [257, 3],
710
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
711
+ },
712
+ "scaleT": {
713
+ "dtype": "float16",
714
+ "shape": [3],
715
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
716
+ },
717
+ "gateT": {
718
+ "dtype": "float16",
719
+ "shape": [257, 3],
720
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
721
+ }
722
+ },
723
+ "outputs": { "yT": { "dtype": "float16", "shape": [257, 3], "tolerance": 0.0005, "relTolerance": 0.002 } },
724
+ "provenance": {
725
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
726
+ }
727
+ },
728
+ {
729
+ "name": "short_scalar_float16_c5_r257",
730
+ "inputs": {
731
+ "xT": {
732
+ "dtype": "float16",
733
+ "shape": [257, 5],
734
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
735
+ },
736
+ "scaleT": {
737
+ "dtype": "float16",
738
+ "shape": [5],
739
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
740
+ },
741
+ "gateT": {
742
+ "dtype": "float16",
743
+ "shape": [257, 5],
744
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
745
+ }
746
+ },
747
+ "outputs": { "yT": { "dtype": "float16", "shape": [257, 5], "tolerance": 0.0005, "relTolerance": 0.002 } },
748
+ "provenance": {
749
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
750
+ }
751
+ },
752
+ {
753
+ "name": "short_scalar_float16_c6_r257",
754
+ "inputs": {
755
+ "xT": {
756
+ "dtype": "float16",
757
+ "shape": [257, 6],
758
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
759
+ },
760
+ "scaleT": {
761
+ "dtype": "float16",
762
+ "shape": [6],
763
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
764
+ },
765
+ "gateT": {
766
+ "dtype": "float16",
767
+ "shape": [257, 6],
768
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
769
+ }
770
+ },
771
+ "outputs": { "yT": { "dtype": "float16", "shape": [257, 6], "tolerance": 0.0005, "relTolerance": 0.002 } },
772
+ "provenance": {
773
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
774
+ }
775
+ },
776
+ {
777
+ "name": "short_scalar_float16_c7_r257",
778
+ "inputs": {
779
+ "xT": {
780
+ "dtype": "float16",
781
+ "shape": [257, 7],
782
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
783
+ },
784
+ "scaleT": {
785
+ "dtype": "float16",
786
+ "shape": [7],
787
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
788
+ },
789
+ "gateT": {
790
+ "dtype": "float16",
791
+ "shape": [257, 7],
792
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
793
+ }
794
+ },
795
+ "outputs": { "yT": { "dtype": "float16", "shape": [257, 7], "tolerance": 0.0005, "relTolerance": 0.002 } },
796
+ "provenance": {
797
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
798
+ }
799
+ },
800
+ {
801
+ "name": "short_scalar_float32_c7_r65_wg64_words4",
802
+ "inputs": {
803
+ "xT": {
804
+ "dtype": "float32",
805
+ "shape": [65, 7],
806
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
807
+ },
808
+ "scaleT": {
809
+ "dtype": "float32",
810
+ "shape": [7],
811
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
812
+ },
813
+ "gateT": {
814
+ "dtype": "float32",
815
+ "shape": [65, 7],
816
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
817
+ }
818
+ },
819
+ "outputs": { "yT": { "dtype": "float32", "shape": [65, 7], "tolerance": 0.000002, "relTolerance": 0.00002 } },
820
+ "tunables": { "WORKGROUP_SIZE": 64, "LANE_WORDS": 4 },
821
+ "provenance": {
822
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
823
+ }
824
+ },
825
+ {
826
+ "name": "short_scalar_float16_c7_r65_wg64_words4",
827
+ "inputs": {
828
+ "xT": {
829
+ "dtype": "float16",
830
+ "shape": [65, 7],
831
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
832
+ },
833
+ "scaleT": {
834
+ "dtype": "float16",
835
+ "shape": [7],
836
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
837
+ },
838
+ "gateT": {
839
+ "dtype": "float16",
840
+ "shape": [65, 7],
841
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
842
+ }
843
+ },
844
+ "outputs": { "yT": { "dtype": "float16", "shape": [65, 7], "tolerance": 0.0005, "relTolerance": 0.002 } },
845
+ "tunables": { "WORKGROUP_SIZE": 64, "LANE_WORDS": 4 },
846
+ "provenance": {
847
+ "notes": "Short scalar row schedule: mixed magnitudes, signed and zero scale, SiLU tails, and rows on either side of a workgroup boundary, with float32 intermediates at the default dtype tolerances; spans eight/nine cover the staging boundary."
848
+ }
849
+ },
850
+ {
851
+ "name": "short_scalar_float32_c6_r3_wg1_words1",
852
+ "inputs": {
853
+ "xT": {
854
+ "dtype": "float32",
855
+ "shape": [3, 6],
856
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_x" } }
857
+ },
858
+ "scaleT": {
859
+ "dtype": "float32",
860
+ "shape": [6],
861
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_scale" } }
862
+ },
863
+ "gateT": {
864
+ "dtype": "float32",
865
+ "shape": [3, 6],
866
+ "data": { "kind": "cycle", "values": { "$ref": "#/fixtureArrays/short_scalar_gate" } }
867
+ }
868
+ },
869
+ "outputs": { "yT": { "dtype": "float32", "shape": [3, 6], "tolerance": 0.000002, "relTolerance": 0.00002 } },
870
+ "tunables": { "WORKGROUP_SIZE": 1, "LANE_WORDS": 1 },
871
+ "provenance": {
872
+ "notes": "Three rows of 6 float32 channels each, processed one lane per row; checks correctness at the smallest possible per-row lane allocation, below the width where multiple lanes would cooperate on one row."
873
+ }
874
+ }
875
+ ],
876
+ "fixtureArrays": {
877
+ "short_scalar_x": [-0.0009765625, 0.5, -1, 2, 8, -16, 64],
878
+ "short_scalar_scale": [-1, 0, 0.125, 1.5, -2],
879
+ "short_scalar_gate": [-80, -4, -1, 0, 1, 4, 80]
880
+ }
881
+ }