Xenova HF Staff commited on
Commit
f841d79
·
verified ·
1 Parent(s): 270ae6e

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,17 +18,17 @@ See the [ONNX Runtime `FusedGemm` contrib-operator spec](https://github.com/micr
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
- | --- | --- | --- | --- | --- | --- | --- |
23
- | `A` | `A` | `T` | `2` | — | Left operand, `(M, K)` when `transA` is 0 and `(K, M)` otherwise. | required |
24
- | `B` | `B` | `T` | `2` | — | Right operand, `(K, N)` when `transB` is 0 and `(N, K)` otherwise. | required |
25
- | `C` | `C` | `T` | — | — | Optional additive term, unidirectionally broadcastable to `(M, N)`: a scalar, a row `(N)`, a column `(M, 1)`, or the full matrix. | optional |
26
 
27
  ## Outputs
28
 
29
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
30
- | --- | --- | --- | --- | --- | --- | --- |
31
- | `Y` | `Y` | `T` | `2` | derived; see description | `act(alpha * A' * B' + beta * C)` with shape `(M, N)`. | required |
32
 
33
  ## Attributes
34
 
@@ -36,13 +36,13 @@ Attributes and default values (overridable per request):
36
 
37
  | Attribute | Default | Description |
38
  | --- | --- | --- |
 
 
 
39
  | `alpha` | `1` | Scalar multiplier for the product `A' * B'`; the standard default is 1. |
40
  | `beta` | `1` | Scalar multiplier for `C`; the standard default is 1. |
41
  | `transA` | `0` | Whether `A` is stored transposed. The standard default is 0. |
42
  | `transB` | `0` | Whether `B` is stored transposed. The standard default is 0. |
43
- | `activation` | — | Optional fused activation name. Supported modes are `Relu`, `LeakyRelu`, `Sigmoid`, `Tanh` and `HardSigmoid`; omission applies none. |
44
- | `activation_alpha` | — | First activation parameter: the slope for `LeakyRelu` or `alpha` for `HardSigmoid`. |
45
- | `activation_beta` | — | Second activation parameter: `beta` for `HardSigmoid`. |
46
 
47
  ## Type constraints
48
 
@@ -50,25 +50,45 @@ Attributes and default values (overridable per request):
50
  | --- | --- |
51
  | `T` | `float32`, `float16` |
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  ## Device requirements
54
 
55
  Some implementation variants require `subgroup-matrix` and `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
56
 
57
  ## Files
58
 
59
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
60
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
61
  - [`test.json`](build/webgpu/test.json) — correctness cases
62
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
63
  - [`fused-gemm.wgsl.jinja`](build/webgpu/fused-gemm.wgsl.jinja)
64
  - [`gemm-subgroup-matrix.wgsl.jinja`](build/webgpu/gemm-subgroup-matrix.wgsl.jinja)
 
65
 
66
  ## Use with `@huggingface/kernels`
67
 
68
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
69
- It then allocates the result tensors automatically.
 
 
 
70
 
71
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
72
 
73
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
74
 
@@ -76,7 +96,8 @@ Replace each `*Data` placeholder with a typed array containing the corresponding
76
  import { getKernel } from "@huggingface/kernels";
77
 
78
  const kernel = await getKernel("webgpu-kernels/com.microsoft.FusedGemm", { version: 1 });
79
- const { Y } = await kernel({ A: { data: AData, shape: [7, 13] }, B: { data: BData, shape: [13, 11] } }, {
80
- attrs: { activation: "Relu" },
 
81
  });
82
  ```
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- |
23
+ | `A` | `T` | `2` | — | Left operand, `(M, K)` when `transA` is 0 and `(K, M)` otherwise. | required |
24
+ | `B` | `T` | `2` | — | Right operand, `(K, N)` when `transB` is 0 and `(N, K)` otherwise. | required |
25
+ | `C` | `T` | — | — | Optional additive term, unidirectionally broadcastable to `(M, N)`: a scalar, a row `(N)`, a column `(M, 1)`, or the full matrix. | optional |
26
 
27
  ## Outputs
28
 
29
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
30
+ | --- | --- | --- | --- | --- | --- |
31
+ | `Y` | `T` | `2` | derived | `act(alpha * A' * B' + beta * C)` with shape `(M, N)`. | required |
32
 
33
  ## Attributes
34
 
 
36
 
37
  | Attribute | Default | Description |
38
  | --- | --- | --- |
39
+ | `activation` | — | Optional fused activation name. Supported modes are `Relu`, `LeakyRelu`, `Sigmoid`, `Tanh` and `HardSigmoid`; omission applies none. |
40
+ | `activation_alpha` | — | First activation parameter: the slope for `LeakyRelu` or `alpha` for `HardSigmoid`. |
41
+ | `activation_beta` | — | Second activation parameter: `beta` for `HardSigmoid`. |
42
  | `alpha` | `1` | Scalar multiplier for the product `A' * B'`; the standard default is 1. |
43
  | `beta` | `1` | Scalar multiplier for `C`; the standard default is 1. |
44
  | `transA` | `0` | Whether `A` is stored transposed. The standard default is 0. |
45
  | `transB` | `0` | Whether `B` is stored transposed. The standard default is 0. |
 
 
 
46
 
47
  ## Type constraints
48
 
 
50
  | --- | --- |
51
  | `T` | `float32`, `float16` |
52
 
53
+ ## Implementation variants
54
+
55
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
56
+
57
+ - `gemv_plain` — Reduces contiguous weight rows for a single output row in f32, applies alpha and activation, and casts once. Four aligned columns share the input vector; other widths use one column. The reduction width is bounded by K and device workgroup limits. Either storage orientation of A is supported.
58
+ - `gemv_bias` — Reduces contiguous weight rows for a single output row in f32, applies alpha, broadcast beta-scaled bias and activation, and casts once. Four aligned columns share the input vector; other widths use one column. The reduction width is bounded by K and device workgroup limits. Either storage orientation of A is supported.
59
+ - `notrans_sgmat_bias` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
60
+ - `notrans_sgmat` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
61
+ - `transA_sgmat_bias` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
62
+ - `transA_sgmat` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
63
+ - `transB_sgmat_bias` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
64
+ - `transB_sgmat` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
65
+ - `transAB_sgmat_bias` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
66
+ - `transAB_sgmat` — Uses supported subgroup matrices with f32 accumulation and a fused epilogue. Complete non-transposed f32 A tiles load directly from storage; long reductions on that path merge two-tile sums with f32 compensation. Staged A clamps row tails. Workgroup memory is checked against device limits.
67
+
68
  ## Device requirements
69
 
70
  Some implementation variants require `subgroup-matrix` and `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
71
 
72
  ## Files
73
 
74
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
75
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
76
  - [`test.json`](build/webgpu/test.json) — correctness cases
77
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
78
  - [`fused-gemm.wgsl.jinja`](build/webgpu/fused-gemm.wgsl.jinja)
79
  - [`gemm-subgroup-matrix.wgsl.jinja`](build/webgpu/gemm-subgroup-matrix.wgsl.jinja)
80
+ - [`gemv-contiguous.wgsl.jinja`](build/webgpu/gemv-contiguous.wgsl.jinja)
81
 
82
  ## Use with `@huggingface/kernels`
83
 
84
+ ```sh
85
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
86
+ ```
87
+
88
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
89
 
90
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
91
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
92
 
93
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
94
 
 
96
  import { getKernel } from "@huggingface/kernels";
97
 
98
  const kernel = await getKernel("webgpu-kernels/com.microsoft.FusedGemm", { version: 1 });
99
+ const { Y } = await kernel({
100
+ A: { data: AData, shape: [32, 224] },
101
+ B: { data: BData, shape: [224, 64] },
102
  });
103
  ```
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "com.microsoft.FusedGemm",
3
  "cases": [
4
  {
5
  "name": "fusedgemm-relu-m3072-k768-n3072",
@@ -64,6 +63,389 @@
64
  },
65
  "outputs": { "Y": { "shape": [2048, 2048], "dtype": "float32" } },
66
  "bench": { "metrics": [{ "type": "gflops", "value": "2 * 2048 * 1024 * 2048" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
68
  ]
69
  }
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "fusedgemm-relu-m3072-k768-n3072",
 
63
  },
64
  "outputs": { "Y": { "shape": [2048, 2048], "dtype": "float32" } },
65
  "bench": { "metrics": [{ "type": "gflops", "value": "2 * 2048 * 1024 * 2048" }] }
66
+ },
67
+ {
68
+ "name": "fusedgemm-f16-transb-relu-m1-k4096-n4096-sgmat-floor-pathology",
69
+ "preset": "model",
70
+ "vars": { "dtype": "float16" },
71
+ "attrs": { "transB": 1, "activation": "Relu" },
72
+ "inputs": {
73
+ "A": { "shape": [1, 4096], "dtype": "float16", "dist": "normal", "seed": 5350, "scale": 1 },
74
+ "B": { "shape": [4096, 4096], "dtype": "float16", "dist": "normal", "seed": 5351, "scale": 1 },
75
+ "C": { "shape": [4096], "dtype": "float16", "dist": "normal", "seed": 5352, "scale": 1 }
76
+ },
77
+ "outputs": { "Y": { "shape": [1, 4096], "dtype": "float16" } },
78
+ "bench": {
79
+ "metrics": [
80
+ { "type": "gflops", "value": "2 * 1 * 4096 * 4096" },
81
+ {
82
+ "type": "bandwidth",
83
+ "name": "logical input/output bytes",
84
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y) + numel(shapes.C))",
85
+ "provenance": "logical"
86
+ }
87
+ ]
88
+ }
89
+ },
90
+ {
91
+ "name": "fusedgemm-f16-transb-relu-m64-k4096-n4096-control",
92
+ "preset": "model",
93
+ "vars": { "dtype": "float16" },
94
+ "attrs": { "transB": 1, "activation": "Relu" },
95
+ "inputs": {
96
+ "A": { "shape": [64, 4096], "dtype": "float16", "dist": "normal", "seed": 5380, "scale": 1 },
97
+ "B": { "shape": [4096, 4096], "dtype": "float16", "dist": "normal", "seed": 5381, "scale": 1 },
98
+ "C": { "shape": [4096], "dtype": "float16", "dist": "normal", "seed": 5382, "scale": 1 }
99
+ },
100
+ "outputs": { "Y": { "shape": [64, 4096], "dtype": "float16" } },
101
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 64 * 4096 * 4096" }] }
102
+ },
103
+ {
104
+ "name": "fusedgemm-f16-single-row-k1024-n64-transb1-leakyrelu",
105
+ "preset": "model",
106
+ "vars": { "M": 1, "K": 1024, "N": 64, "dtype": "float16" },
107
+ "attrs": { "transB": 1, "activation": "LeakyRelu" },
108
+ "inputs": {
109
+ "A": { "shape": [1, 1024], "dtype": "float16", "dist": "normal", "seed": 5350, "scale": 1 },
110
+ "B": { "shape": [64, 1024], "dtype": "float16", "dist": "normal", "seed": 5351, "scale": 1 },
111
+ "C": { "shape": [64], "dtype": "float16", "dist": "normal", "seed": 5352, "scale": 1 }
112
+ },
113
+ "outputs": { "Y": { "shape": [1, 64], "dtype": "float16" } },
114
+ "bench": {
115
+ "metrics": [
116
+ { "type": "gflops", "value": "2 * args.M * args.K * args.N" },
117
+ {
118
+ "type": "bandwidth",
119
+ "name": "logical input/output bytes",
120
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y) + numel(shapes.C))",
121
+ "provenance": "logical"
122
+ }
123
+ ]
124
+ },
125
+ "provenance": {
126
+ "source": "synthetic benchmark",
127
+ "notes": "Single-row fused projection spanning reduction length, output width, weight orientation and activation, with f32 accumulation and f16 output."
128
+ }
129
+ },
130
+ {
131
+ "name": "fusedgemm-f16-single-row-k128-n4096-transb1-hardsigmoid",
132
+ "preset": "model",
133
+ "vars": { "M": 1, "K": 128, "N": 4096, "dtype": "float16" },
134
+ "attrs": { "transB": 1, "activation": "HardSigmoid" },
135
+ "inputs": {
136
+ "A": { "shape": [1, 128], "dtype": "float16", "dist": "normal", "seed": 5350, "scale": 1 },
137
+ "B": { "shape": [4096, 128], "dtype": "float16", "dist": "normal", "seed": 5351, "scale": 1 }
138
+ },
139
+ "outputs": { "Y": { "shape": [1, 4096], "dtype": "float16" } },
140
+ "bench": {
141
+ "metrics": [
142
+ { "type": "gflops", "value": "2 * args.M * args.K * args.N" },
143
+ {
144
+ "type": "bandwidth",
145
+ "name": "logical input/output bytes",
146
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y))",
147
+ "provenance": "logical"
148
+ }
149
+ ]
150
+ },
151
+ "provenance": {
152
+ "source": "synthetic benchmark",
153
+ "notes": "Single-row fused projection spanning reduction length, output width, weight orientation and activation, with f32 accumulation and f16 output."
154
+ }
155
+ },
156
+ {
157
+ "name": "fusedgemm-f16-single-row-k32-n256-transb1-tanh",
158
+ "preset": "model",
159
+ "vars": { "M": 1, "K": 32, "N": 256, "dtype": "float16" },
160
+ "attrs": { "transB": 1, "activation": "Tanh" },
161
+ "inputs": {
162
+ "A": { "shape": [1, 32], "dtype": "float16", "dist": "normal", "seed": 5350, "scale": 1 },
163
+ "B": { "shape": [256, 32], "dtype": "float16", "dist": "normal", "seed": 5351, "scale": 1 }
164
+ },
165
+ "outputs": { "Y": { "shape": [1, 256], "dtype": "float16" } },
166
+ "bench": {
167
+ "metrics": [
168
+ { "type": "gflops", "value": "2 * args.M * args.K * args.N" },
169
+ {
170
+ "type": "bandwidth",
171
+ "name": "logical input/output bytes",
172
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y))",
173
+ "provenance": "logical"
174
+ }
175
+ ]
176
+ },
177
+ "provenance": {
178
+ "source": "synthetic benchmark",
179
+ "notes": "Single-row fused projection spanning reduction length, output width, weight orientation and activation, with f32 accumulation and f16 output."
180
+ }
181
+ },
182
+ {
183
+ "name": "fusedgemm-f16-single-row-k1024-n64-transb0-relu",
184
+ "preset": "model",
185
+ "vars": { "M": 1, "K": 1024, "N": 64, "dtype": "float16" },
186
+ "attrs": { "transB": 0, "activation": "Relu" },
187
+ "inputs": {
188
+ "A": { "shape": [1, 1024], "dtype": "float16", "dist": "normal", "seed": 5350, "scale": 1 },
189
+ "B": { "shape": [1024, 64], "dtype": "float16", "dist": "normal", "seed": 5351, "scale": 1 },
190
+ "C": { "shape": [64], "dtype": "float16", "dist": "normal", "seed": 5352, "scale": 1 }
191
+ },
192
+ "outputs": { "Y": { "shape": [1, 64], "dtype": "float16" } },
193
+ "bench": {
194
+ "metrics": [
195
+ { "type": "gflops", "value": "2 * args.M * args.K * args.N" },
196
+ {
197
+ "type": "bandwidth",
198
+ "name": "logical input/output bytes",
199
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y) + numel(shapes.C))",
200
+ "provenance": "logical"
201
+ }
202
+ ]
203
+ },
204
+ "provenance": {
205
+ "source": "synthetic benchmark",
206
+ "notes": "Single-row fused projection spanning reduction length, output width, weight orientation and activation, with f32 accumulation and f16 output."
207
+ }
208
+ },
209
+ {
210
+ "name": "gemv-float32-k128-n4096-ta0-none-plain",
211
+ "preset": "model",
212
+ "vars": { "dtype": "float32" },
213
+ "attrs": { "transA": 0, "transB": 1 },
214
+ "inputs": {
215
+ "A": { "shape": [1, 128], "dtype": "float32", "dist": "normal", "seed": 5350, "scale": 1 },
216
+ "B": { "shape": [4096, 128], "dtype": "float32", "dist": "normal", "seed": 5351, "scale": 1 }
217
+ },
218
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 4096] } },
219
+ "bench": {
220
+ "metrics": [
221
+ { "type": "gflops", "value": 1048576 },
222
+ {
223
+ "type": "bandwidth",
224
+ "name": "logical input/output bytes",
225
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y))",
226
+ "provenance": "logical"
227
+ }
228
+ ]
229
+ },
230
+ "provenance": {
231
+ "source": "synthetic benchmark",
232
+ "notes": "Single-row fused projection covers dtype, stored A orientation, output alignment, broadcast bias and activation."
233
+ }
234
+ },
235
+ {
236
+ "name": "gemv-float32-k4096-n4096-ta1-none-Sigmoid",
237
+ "preset": "model",
238
+ "vars": { "dtype": "float32" },
239
+ "attrs": { "transA": 1, "transB": 1, "activation": "Sigmoid" },
240
+ "inputs": {
241
+ "A": { "shape": [4096, 1], "dtype": "float32", "dist": "normal", "seed": 5350, "scale": 1 },
242
+ "B": { "shape": [4096, 4096], "dtype": "float32", "dist": "normal", "seed": 5351, "scale": 1 }
243
+ },
244
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 4096] } },
245
+ "bench": {
246
+ "metrics": [
247
+ { "type": "gflops", "value": 33554432 },
248
+ {
249
+ "type": "bandwidth",
250
+ "name": "logical input/output bytes",
251
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y))",
252
+ "provenance": "logical"
253
+ }
254
+ ]
255
+ },
256
+ "provenance": {
257
+ "source": "synthetic benchmark",
258
+ "notes": "Single-row fused projection covers dtype, stored A orientation, output alignment, broadcast bias and activation."
259
+ }
260
+ },
261
+ {
262
+ "name": "gemv-float16-k1023-n257-ta0-scalar-LeakyRelu",
263
+ "preset": "model",
264
+ "vars": { "dtype": "float16" },
265
+ "attrs": {
266
+ "transA": 0,
267
+ "transB": 1,
268
+ "activation": "LeakyRelu",
269
+ "alpha": 0.75,
270
+ "beta": -0.25,
271
+ "activation_alpha": 0.125,
272
+ "activation_beta": 0.375
273
+ },
274
+ "inputs": {
275
+ "A": { "shape": [1, 1023], "dtype": "float16", "dist": "normal", "seed": 5350, "scale": 1 },
276
+ "B": { "shape": [257, 1023], "dtype": "float16", "dist": "normal", "seed": 5351, "scale": 1 },
277
+ "C": { "shape": [], "dtype": "float16", "dist": "normal", "seed": 5352, "scale": 1 }
278
+ },
279
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 257] } },
280
+ "bench": {
281
+ "metrics": [
282
+ { "type": "gflops", "value": 525822 },
283
+ {
284
+ "type": "bandwidth",
285
+ "name": "logical input/output bytes",
286
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y) + numel(shapes.C))",
287
+ "provenance": "logical"
288
+ }
289
+ ]
290
+ },
291
+ "provenance": {
292
+ "source": "synthetic benchmark",
293
+ "notes": "Single-row fused projection covers dtype, stored A orientation, output alignment, broadcast bias and activation."
294
+ }
295
+ },
296
+ {
297
+ "name": "gemv-float16-k4096-n4096-ta1-scalar-HardSigmoid",
298
+ "preset": "model",
299
+ "vars": { "dtype": "float16" },
300
+ "attrs": { "transA": 1, "transB": 1, "activation": "HardSigmoid" },
301
+ "inputs": {
302
+ "A": { "shape": [4096, 1], "dtype": "float16", "dist": "normal", "seed": 5350, "scale": 1 },
303
+ "B": { "shape": [4096, 4096], "dtype": "float16", "dist": "normal", "seed": 5351, "scale": 1 },
304
+ "C": { "shape": [], "dtype": "float16", "dist": "normal", "seed": 5352, "scale": 1 }
305
+ },
306
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 4096] } },
307
+ "bench": {
308
+ "metrics": [
309
+ { "type": "gflops", "value": 33554432 },
310
+ {
311
+ "type": "bandwidth",
312
+ "name": "logical input/output bytes",
313
+ "value": "dtypeBytes(args.dtype) * (numel(shapes.A) + numel(shapes.B) + numel(shapes.Y) + numel(shapes.C))",
314
+ "provenance": "logical"
315
+ }
316
+ ]
317
+ },
318
+ "provenance": {
319
+ "source": "synthetic benchmark",
320
+ "notes": "Single-row fused projection covers dtype, stored A orientation, output alignment, broadcast bias and activation."
321
+ }
322
+ },
323
+ {
324
+ "name": "direct_a_m31_k96_n128_tb1",
325
+ "attrs": { "transB": 1, "activation": "Relu" },
326
+ "inputs": {
327
+ "A": { "shape": [31, 96], "dtype": "float32", "dist": "normal", "seed": 5400, "scale": 0.125 },
328
+ "B": { "shape": [128, 96], "dtype": "float32", "dist": "normal", "seed": 5500, "scale": 0.125 },
329
+ "C": { "shape": [128], "dtype": "float32", "dist": "normal", "seed": 5600, "scale": 0.125 }
330
+ },
331
+ "outputs": { "Y": { "shape": [31, 128], "dtype": "float32" } },
332
+ "bench": { "metrics": [{ "type": "gflops", "value": "761856" }] }
333
+ },
334
+ {
335
+ "name": "direct_a_m32_k96_n128_tb1",
336
+ "attrs": { "transB": 1, "activation": "Relu" },
337
+ "inputs": {
338
+ "A": { "shape": [32, 96], "dtype": "float32", "dist": "normal", "seed": 5401, "scale": 0.125 },
339
+ "B": { "shape": [128, 96], "dtype": "float32", "dist": "normal", "seed": 5501, "scale": 0.125 },
340
+ "C": { "shape": [128], "dtype": "float32", "dist": "normal", "seed": 5601, "scale": 0.125 }
341
+ },
342
+ "outputs": { "Y": { "shape": [32, 128], "dtype": "float32" } },
343
+ "bench": { "metrics": [{ "type": "gflops", "value": "786432" }] }
344
+ },
345
+ {
346
+ "name": "direct_a_m33_k96_n128_tb1",
347
+ "attrs": { "transB": 1, "activation": "Relu" },
348
+ "inputs": {
349
+ "A": { "shape": [33, 96], "dtype": "float32", "dist": "normal", "seed": 5402, "scale": 0.125 },
350
+ "B": { "shape": [128, 96], "dtype": "float32", "dist": "normal", "seed": 5502, "scale": 0.125 },
351
+ "C": { "shape": [128], "dtype": "float32", "dist": "normal", "seed": 5602, "scale": 0.125 }
352
+ },
353
+ "outputs": { "Y": { "shape": [33, 128], "dtype": "float32" } },
354
+ "bench": { "metrics": [{ "type": "gflops", "value": "811008" }] }
355
+ },
356
+ {
357
+ "name": "direct_a_m63_k96_n128_tb1",
358
+ "attrs": { "transB": 1, "activation": "Relu" },
359
+ "inputs": {
360
+ "A": { "shape": [63, 96], "dtype": "float32", "dist": "normal", "seed": 5403, "scale": 0.125 },
361
+ "B": { "shape": [128, 96], "dtype": "float32", "dist": "normal", "seed": 5503, "scale": 0.125 },
362
+ "C": { "shape": [128], "dtype": "float32", "dist": "normal", "seed": 5603, "scale": 0.125 }
363
+ },
364
+ "outputs": { "Y": { "shape": [63, 128], "dtype": "float32" } },
365
+ "bench": { "metrics": [{ "type": "gflops", "value": "1548288" }] }
366
+ },
367
+ {
368
+ "name": "direct_a_m64_k96_n128_tb1",
369
+ "attrs": { "transB": 1, "activation": "Relu" },
370
+ "inputs": {
371
+ "A": { "shape": [64, 96], "dtype": "float32", "dist": "normal", "seed": 5404, "scale": 0.125 },
372
+ "B": { "shape": [128, 96], "dtype": "float32", "dist": "normal", "seed": 5504, "scale": 0.125 },
373
+ "C": { "shape": [128], "dtype": "float32", "dist": "normal", "seed": 5604, "scale": 0.125 }
374
+ },
375
+ "outputs": { "Y": { "shape": [64, 128], "dtype": "float32" } },
376
+ "bench": { "metrics": [{ "type": "gflops", "value": "1572864" }] }
377
+ },
378
+ {
379
+ "name": "direct_a_m65_k96_n128_tb1",
380
+ "attrs": { "transB": 1, "activation": "Relu" },
381
+ "inputs": {
382
+ "A": { "shape": [65, 96], "dtype": "float32", "dist": "normal", "seed": 5405, "scale": 0.125 },
383
+ "B": { "shape": [128, 96], "dtype": "float32", "dist": "normal", "seed": 5505, "scale": 0.125 },
384
+ "C": { "shape": [128], "dtype": "float32", "dist": "normal", "seed": 5605, "scale": 0.125 }
385
+ },
386
+ "outputs": { "Y": { "shape": [65, 128], "dtype": "float32" } },
387
+ "bench": { "metrics": [{ "type": "gflops", "value": "1597440" }] }
388
+ },
389
+ {
390
+ "name": "direct_a_m64_k32_n64_tb0",
391
+ "attrs": { "transB": 0, "activation": "Tanh" },
392
+ "inputs": {
393
+ "A": { "shape": [64, 32], "dtype": "float32", "dist": "normal", "seed": 5406, "scale": 0.125 },
394
+ "B": { "shape": [32, 64], "dtype": "float32", "dist": "normal", "seed": 5506, "scale": 0.125 }
395
+ },
396
+ "outputs": { "Y": { "shape": [64, 64], "dtype": "float32" } },
397
+ "bench": { "metrics": [{ "type": "gflops", "value": "262144" }] }
398
+ },
399
+ {
400
+ "name": "direct_a_m32_k224_n64_tb0",
401
+ "attrs": { "transB": 0 },
402
+ "inputs": {
403
+ "A": { "shape": [32, 224], "dtype": "float32", "dist": "normal", "seed": 5408, "scale": 0.125 },
404
+ "B": { "shape": [224, 64], "dtype": "float32", "dist": "normal", "seed": 5508, "scale": 0.125 }
405
+ },
406
+ "outputs": { "Y": { "shape": [32, 64], "dtype": "float32" } },
407
+ "bench": { "metrics": [{ "type": "gflops", "value": "917504" }] }
408
+ },
409
+ {
410
+ "name": "direct_a_m32_k256_n64_tb1",
411
+ "attrs": { "transB": 1 },
412
+ "inputs": {
413
+ "A": { "shape": [32, 256], "dtype": "float32", "dist": "normal", "seed": 5409, "scale": 0.125 },
414
+ "B": { "shape": [64, 256], "dtype": "float32", "dist": "normal", "seed": 5509, "scale": 0.125 }
415
+ },
416
+ "outputs": { "Y": { "shape": [32, 64], "dtype": "float32" } },
417
+ "bench": { "metrics": [{ "type": "gflops", "value": "1048576" }] }
418
+ },
419
+ {
420
+ "name": "direct_a_m32_k288_n64_tb0",
421
+ "attrs": { "transB": 0 },
422
+ "inputs": {
423
+ "A": { "shape": [32, 288], "dtype": "float32", "dist": "normal", "seed": 5410, "scale": 0.125 },
424
+ "B": { "shape": [288, 64], "dtype": "float32", "dist": "normal", "seed": 5510, "scale": 0.125 }
425
+ },
426
+ "outputs": { "Y": { "shape": [32, 64], "dtype": "float32" } },
427
+ "bench": { "metrics": [{ "type": "gflops", "value": "1179648" }] }
428
+ },
429
+ {
430
+ "name": "direct_a_m32_k768_n128_tb0",
431
+ "attrs": { "transB": 0, "activation": "Relu" },
432
+ "inputs": {
433
+ "A": { "shape": [32, 768], "dtype": "float32", "dist": "normal", "seed": 5411, "scale": 0.125 },
434
+ "B": { "shape": [768, 128], "dtype": "float32", "dist": "normal", "seed": 5511, "scale": 0.125 },
435
+ "C": { "shape": [128], "dtype": "float32", "dist": "normal", "seed": 5611, "scale": 0.125 }
436
+ },
437
+ "outputs": { "Y": { "shape": [32, 128], "dtype": "float32" } },
438
+ "bench": { "metrics": [{ "type": "gflops", "value": "6291456" }] }
439
+ },
440
+ {
441
+ "name": "direct_a_m64_k1024_n128_tb0",
442
+ "attrs": { "transB": 0, "activation": "Tanh" },
443
+ "inputs": {
444
+ "A": { "shape": [64, 1024], "dtype": "float32", "dist": "normal", "seed": 5412, "scale": 0.125 },
445
+ "B": { "shape": [1024, 128], "dtype": "float32", "dist": "normal", "seed": 5512, "scale": 0.125 }
446
+ },
447
+ "outputs": { "Y": { "shape": [64, 128], "dtype": "float32" } },
448
+ "bench": { "metrics": [{ "type": "gflops", "value": "16777216" }] }
449
  }
450
  ]
451
  }
build/webgpu/fused-gemm.wgsl.jinja CHANGED
@@ -24,26 +24,27 @@ fn gelu_tanh(v: f32) -> f32 {
24
  {% set actMode = gemmActivation | default("") %}
25
  {% set actA = gemmActAlpha | default(0.0) %}
26
  {% set actB = gemmActBeta | default(0.0) %}
27
- fn fused_act(v: f32) -> f32 {
28
- {% if actMode == "Relu" %}
29
  return max(v, 0.0);
30
- {% endif %}
31
- {% if actMode == "LeakyRelu" %}
32
- return select(v * f32({{ actA }}), v, v >= 0.0);
33
- {% endif %}
34
- {% if actMode == "Sigmoid" %}
35
  return 1.0 / (1.0 + exp(-v));
36
- {% endif %}
37
- {% if actMode == "Tanh" %}
38
- // Clamp first: the hardware tanh evaluates (e^2v - 1)/(e^2v + 1), which is
39
- // Inf/Inf = NaN once e^2v overflows (v > ~44). A GEMM accumulator reaches that
40
- // easily because it grows with K. tanh is already +/-1 to full f32 precision by
41
- // |v| ~ 9, so the clamp changes no representable result.
42
  return tanh(clamp(v, -10.0, 10.0));
 
 
 
 
43
  {% endif %}
44
- {% if actMode == "HardSigmoid" %}
45
- return clamp(f32({{ actA }}) * v + f32({{ actB }}), 0.0, 1.0);
46
- {% endif %}
47
  }
48
 
49
  {% endif %}
@@ -141,19 +142,22 @@ fn main(
141
  tileB[bc][br4] = bWord;
142
  }
143
  workgroupBarrier();
144
- {% set regIndent = " " %}{% set regT = "f32" %}{% set regCast = 1 if tileT != "f32" else 0 %}{{ regIndent }}let aRow = lid.y * TM;
145
- {{ regIndent }}let bCol = lid.x * TN;
146
- {{ regIndent }}for (var kv: u32 = 0u; kv < BK / 4u; kv = kv + 1u) {
147
- {{ regIndent }} var av: array<vec4<{{ regT }}>, TM>;
148
- {{ regIndent }} var bv: array<vec4<{{ regT }}>, TN>;
149
- {{ regIndent }} for (var i: u32 = 0u; i < TM; i = i + 1u) { av[i] = {% if regCast %}vec4<{{ regT }}>(tileA[aRow + i][kv]){% else %}tileA[aRow + i][kv]{% endif %}; }
150
- {{ regIndent }} for (var j: u32 = 0u; j < TN; j = j + 1u) { bv[j] = {% if regCast %}vec4<{{ regT }}>(tileB[bCol + j][kv]){% else %}tileB[bCol + j][kv]{% endif %}; }
151
- {{ regIndent }} for (var i: u32 = 0u; i < TM; i = i + 1u) {
152
- {{ regIndent }} for (var j: u32 = 0u; j < TN; j = j + 1u) {
153
- {{ regIndent }} acc[i * TN + j] = acc[i * TN + j] + dot(av[i], bv[j]);
154
- {{ regIndent }} }
155
- {{ regIndent }} }
156
- {{ regIndent }}}
 
 
 
157
  workgroupBarrier();
158
  }
159
 
 
24
  {% set actMode = gemmActivation | default("") %}
25
  {% set actA = gemmActAlpha | default(0.0) %}
26
  {% set actB = gemmActBeta | default(0.0) %}
27
+ {% macro fused_act_return(mode, alpha, beta) -%}
28
+ {% if mode == "Relu" %}
29
  return max(v, 0.0);
30
+ {% elif mode == "Clip" %}
31
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
32
+ {% elif mode == "LeakyRelu" %}
33
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
34
+ {% elif mode == "Sigmoid" %}
35
  return 1.0 / (1.0 + exp(-v));
36
+ {% elif mode == "Tanh" %}
37
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
38
+ // the builtin preserves that saturated result for larger accumulators.
 
 
 
39
  return tanh(clamp(v, -10.0, 10.0));
40
+ {% elif mode == "HardSigmoid" %}
41
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
42
+ {% else %}
43
+ return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
44
  {% endif %}
45
+ {%- endmacro -%}
46
+ fn fused_act(v: f32) -> f32 {
47
+ {{ fused_act_return(actMode, actA, actB) -}}
48
  }
49
 
50
  {% endif %}
 
142
  tileB[bc][br4] = bWord;
143
  }
144
  workgroupBarrier();
145
+ {% set regT = "f32" %}{% set regCast = 1 if tileT != "f32" else 0 %}
146
+ {% filter indent(8, true) %}
147
+ let aRow = lid.y * TM;
148
+ let bCol = lid.x * TN;
149
+ for (var kv: u32 = 0u; kv < BK / 4u; kv = kv + 1u) {
150
+ var av: array<vec4<{{ regT }}>, TM>;
151
+ var bv: array<vec4<{{ regT }}>, TN>;
152
+ for (var i: u32 = 0u; i < TM; i = i + 1u) { av[i] = {% if regCast %}vec4<{{ regT }}>(tileA[aRow + i][kv]){% else %}tileA[aRow + i][kv]{% endif %}; }
153
+ for (var j: u32 = 0u; j < TN; j = j + 1u) { bv[j] = {% if regCast %}vec4<{{ regT }}>(tileB[bCol + j][kv]){% else %}tileB[bCol + j][kv]{% endif %}; }
154
+ for (var i: u32 = 0u; i < TM; i = i + 1u) {
155
+ for (var j: u32 = 0u; j < TN; j = j + 1u) {
156
+ acc[i * TN + j] = acc[i * TN + j] + dot(av[i], bv[j]);
157
+ }
158
+ }
159
+ }
160
+ {% endfilter %}
161
  workgroupBarrier();
162
  }
163
 
build/webgpu/gemm-subgroup-matrix.wgsl.jinja CHANGED
@@ -1,6 +1,3 @@
1
- {% if usesF16 %}
2
- enable f16;
3
- {% endif %}
4
  enable subgroups;
5
  {% if pinSubgroupSize32 %}
6
  enable subgroup_size_control;
@@ -8,10 +5,12 @@ enable subgroup_size_control;
8
  enable chromium_experimental_subgroup_matrix;
9
  diagnostic(off, chromium.subgroup_matrix_uniformity);
10
 
 
11
  {{ env.wgsl.resourceDeclarations }}
12
 
13
  {% set operandScalar = mmaScalar %}
14
  {% set accScalar = "f32" %}
 
15
  {% set OUT = outputBuffer if outputBuffer is defined else "y" %}
16
  {% set OUT_SCALAR = outScalar if outScalar is defined else yScalar %}
17
  {% set tileMValue = tileM if tileM is defined else 32 %}
@@ -20,19 +19,46 @@ diagnostic(off, chromium.subgroup_matrix_uniformity);
20
  {% set gemmEpi = gemmEpilogue if gemmEpilogue is defined else "none" %}
21
  {% set epiOpen = "gelu_tanh(" if gemmEpi == "fastgelu" else ("fused_act(" if gemmEpi == "activation" else "") %}
22
  {% set epiClose = ")" if gemmEpi != "none" else "" %}
 
 
23
  {% set useDoubleBufferedB = doubleBufferedB is defined and doubleBufferedB %}
 
 
 
24
  {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
25
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
26
  {% set columnTileBaseValue = columnTileBase if columnTileBase is defined else 0 %}
27
  {% set rowTileBaseValue = rowTileBase if rowTileBase is defined else 0 %}
28
  // Four subgroups cover 32xN as a 2x2 grid, or taller tiles as four row bands.
 
 
 
 
29
  {% set subtileCols = (tileNValue / 2)|int %}
30
  {% set subtileRows = 16 %}
 
31
  {% set aTilesPerSubgroup = (subtileRows / 8)|int %}
32
  {% set bTilesPerSubgroup = (subtileCols / 8)|int %}
33
  {% set subgroupCount = 4 %}
34
  {% set scratchBanks = subgroupCount * aTilesPerSubgroup * bTilesPerSubgroup %}
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  {% if gemmEpi == "fastgelu" %}
37
  fn tanh_safe(x: f32) -> f32 {
38
  if (x > 10.0) { return 1.0; }
@@ -47,26 +73,27 @@ fn gelu_tanh(v: f32) -> f32 {
47
  {% set actMode = gemmActivation | default("") %}
48
  {% set actA = gemmActAlpha | default(0.0) %}
49
  {% set actB = gemmActBeta | default(0.0) %}
50
- fn fused_act(v: f32) -> f32 {
51
- {% if actMode == "Relu" %}
52
  return max(v, 0.0);
53
- {% endif %}
54
- {% if actMode == "LeakyRelu" %}
55
- return select(v * f32({{ actA }}), v, v >= 0.0);
56
- {% endif %}
57
- {% if actMode == "Sigmoid" %}
58
  return 1.0 / (1.0 + exp(-v));
59
- {% endif %}
60
- {% if actMode == "Tanh" %}
61
- // Clamp first: the hardware tanh evaluates (e^2v - 1)/(e^2v + 1), which is
62
- // Inf/Inf = NaN once e^2v overflows (v > ~44). A GEMM accumulator reaches that
63
- // easily because it grows with K. tanh is already +/-1 to full f32 precision by
64
- // |v| ~ 9, so the clamp changes no representable result.
65
  return tanh(clamp(v, -10.0, 10.0));
 
 
 
 
66
  {% endif %}
67
- {% if actMode == "HardSigmoid" %}
68
- return clamp(f32({{ actA }}) * v + f32({{ actB }}), 0.0, 1.0);
69
- {% endif %}
70
  }
71
 
72
  {% endif %}
@@ -80,13 +107,16 @@ const SUB_COLS: u32 = {{ subtileCols }}u;
80
  const SUB_ROWS: u32 = {{ subtileRows }}u;
81
  const COLUMN_TILE_BASE: u32 = {{ columnTileBaseValue }}u;
82
  const ROW_TILE_BASE: u32 = {{ rowTileBaseValue }}u;
 
83
 
84
  var<workgroup> tile_A: array<{{ operandScalar }}, {{ tileMValue }} * {{ tileKValue }}>;
 
85
  var<workgroup> tile_B: array<{{ operandScalar }}, {{ (2 if useDoubleBufferedB else 1) * tileNValue }} * {{ tileKValue }}>;
86
  // Distinct readback banks for every result matrix, so one barrier can publish
87
  // the full subtile without write-after-read reuse.
88
  var<workgroup> scratch: array<array<{{ accScalar }}, 64>, {{ scratchBanks }}>;
89
 
 
90
  fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
91
  {% if aTransposed %}
92
  // Generic transA stores A as [K,M]. Keep both the global and workgroup writes
@@ -123,6 +153,7 @@ fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
123
  {% endif %}
124
  }
125
 
 
126
  fn loadSHMB(tile_base: u32, k_idx: u32, row: u32, c_idx: u32, bank_offset: u32) {
127
  {% if bTransposed %}
128
  // Generic transB=0 stores B as [K,N]. Stage that native orientation and load
@@ -161,11 +192,13 @@ fn loadSHMB(tile_base: u32, k_idx: u32, row: u32, c_idx: u32, bank_offset: u32)
161
  {% set needsColBase = hasBias or (tailSafe is defined and tailSafe) %}
162
  {% set BIAS_1 = (" + " ~ accScalar ~ "(bias[col_base + col])") if hasBias else "" %}
163
  {% set BIAS_2 = (" + " ~ accScalar ~ "(bias[col_base + col2])") if hasBias else "" %}
 
 
164
  fn storeOutput(offset: u32{% if needsColBase %}, col_base: u32{% endif %}, row: u32, col: u32, src_slot: u32, row_limit: i32) {
165
  if (row_limit > 0 && row < u32(row_limit)) {
166
  let col2: u32 = col + 1u;
167
- {{ OUT }}[offset + row * OUT_F + col] = {{ OUT_SCALAR }}({{ epiOpen }}scratch[src_slot][row * 8u + col]{{ BIAS_1 }}{{ epiClose }});
168
- {{ OUT }}[offset + row * OUT_F + col2] = {{ OUT_SCALAR }}({{ epiOpen }}scratch[src_slot][row * 8u + col2]{{ BIAS_2 }}{{ epiClose }});
169
  }
170
  }
171
 
@@ -185,34 +218,59 @@ fn main(
185
  let w_global_base: u32 = (COLUMN_TILE_BASE + workgroup_id.x) * TILE_COLS;
186
 
187
  let subtile_id: u32 = local_idx / sg_size;
 
 
 
 
188
  let subtile_idx: u32 = subtile_id / 2u;
189
  let subtile_idy: u32 = subtile_id % 2u;
 
190
  let base_A: u32 = subtile_idy * SUB_ROWS;
191
  let base_B: u32 = subtile_idx * SUB_COLS;
192
 
193
- {% for m in range(aTilesPerSubgroup) %}
194
- {% for n in range(bTilesPerSubgroup) %}
195
- var matC{{ m }}{{ n }}: subgroup_matrix_result<{{ accScalar }}, 8, 8>;
196
- {% endfor %}
197
- {% endfor %}
198
-
 
 
 
 
 
 
 
199
  for (var kidx: u32 = 0u; kidx < {{ kLoopEndValue }}; kidx += TILE_K) {
 
 
200
  loadSHMA(a_global_base, kidx, local_idx / {{ (tileKValue / 8)|int }}u, local_idx % {{ (tileKValue / 8)|int }}u);
 
201
  loadSHMB(w_global_base, kidx, local_idx / {{ (tileKValue / 16)|int }}u, local_idx % {{ (tileKValue / 16)|int }}u, 0u);
202
  workgroupBarrier();
203
 
204
  for (var step: u32 = 0u; step < TILE_K; step += 8u) {
205
  {% set dynamicATiles = aTilesPerSubgroup if aTilesPerSubgroup is defined else 2 %}
 
 
 
206
  {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
207
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
 
208
  {% set B_BANK = "b_bank_offset + " if doubleBufferedB is defined and doubleBufferedB else "" %}
209
- {% if aTransposed %}
210
- let matrix_a_offset = step * TILE_ROWS + subtile_idy * SUB_ROWS;
 
 
 
 
211
  {% else %}
212
- let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + step;
213
  {% endif %}
214
  {% for m in range(dynamicATiles) %}
215
- {% if aTransposed %}
 
 
216
  var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, col_major>(&tile_A, matrix_a_offset + {{ m * 8 }}u, TILE_ROWS);
217
  {% else %}
218
  var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * TILE_K{% endif %}, TILE_K);
@@ -220,9 +278,9 @@ fn main(
220
  {% endfor %}
221
 
222
  {% if bTransposed %}
223
- let matrix_b_offset = {{ B_BANK }}step * TILE_COLS + subtile_idx * SUB_COLS;
224
  {% else %}
225
- let matrix_b_offset = {{ B_BANK }}subtile_idx * SUB_COLS * TILE_K + step;
226
  {% endif %}
227
  {% for n in range(bTilesPerSubgroup) %}
228
  {% if bTransposed %}
@@ -234,15 +292,43 @@ fn main(
234
 
235
  {% for m in range(dynamicATiles) %}
236
  {% for n in range(bTilesPerSubgroup) %}
237
- matC{{ m }}{{ n }} = subgroupMatrixMultiplyAccumulate(matA{{ m }}, matB{{ n }}, matC{{ m }}{{ n }});
238
  {% endfor %}
239
  {% endfor %}
240
 
241
  }
242
  workgroupBarrier();
243
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
  let matrix_c_offset: u32 = (a_global_base + base_A) * OUT_F + w_global_base + base_B;
 
 
 
 
 
 
 
 
 
246
  let row: u32 = sg_id / 4u;
247
  let col: u32 = (sg_id % 4u) * 2u;
248
  // Stage every result into a distinct bank before one barrier. Tail/bias paths
@@ -250,13 +336,10 @@ fn main(
250
  // across lanes, so its cross-lane readback must be published before partial-M
251
  // guards diverge. The epilogue also handles output conversion and bias.
252
  let bank: u32 = subtile_id * {{ aTilesPerSubgroup * bTilesPerSubgroup }}u;
253
- {% for m in range(aTilesPerSubgroup) %}
254
- {% for n in range(bTilesPerSubgroup) %}
255
- subgroupMatrixStore<row_major>(&scratch[bank + {{ m * bTilesPerSubgroup + n }}u], 0u, matC{{ m }}{{ n }}, 8u);
256
- {% endfor %}
257
- {% endfor %}
258
  workgroupBarrier();
259
 
 
260
  {% for m in range(aTilesPerSubgroup) %}
261
  let row_limit_{{ m }}: i32 = i32(params.M) - i32(a_global_base + base_A + {{ m * 8 }}u);
262
  {% for n in range(bTilesPerSubgroup) %}
@@ -268,7 +351,7 @@ fn main(
268
  row,
269
  col,
270
  bank + {{ m * bTilesPerSubgroup + n }}u,
271
- row_limit_{{ m }}
272
  );
273
  {% endfor %}
274
  {% endfor %}
 
 
 
 
1
  enable subgroups;
2
  {% if pinSubgroupSize32 %}
3
  enable subgroup_size_control;
 
5
  enable chromium_experimental_subgroup_matrix;
6
  diagnostic(off, chromium.subgroup_matrix_uniformity);
7
 
8
+
9
  {{ env.wgsl.resourceDeclarations }}
10
 
11
  {% set operandScalar = mmaScalar %}
12
  {% set accScalar = "f32" %}
13
+ {% set SPLIT_K = splitK if splitK is defined else 1 %}
14
  {% set OUT = outputBuffer if outputBuffer is defined else "y" %}
15
  {% set OUT_SCALAR = outScalar if outScalar is defined else yScalar %}
16
  {% set tileMValue = tileM if tileM is defined else 32 %}
 
19
  {% set gemmEpi = gemmEpilogue if gemmEpilogue is defined else "none" %}
20
  {% set epiOpen = "gelu_tanh(" if gemmEpi == "fastgelu" else ("fused_act(" if gemmEpi == "activation" else "") %}
21
  {% set epiClose = ")" if gemmEpi != "none" else "" %}
22
+ {% set useDirectStore = directStore is defined and directStore and gemmEpi == "none" %}
23
+ {% set useDirectAStorage = directAStorage is defined and directAStorage %}
24
  {% set useDoubleBufferedB = doubleBufferedB is defined and doubleBufferedB %}
25
+ {% set useBalancedAccumulation = balancedMatrixAccumulation is defined and balancedMatrixAccumulation and useDoubleBufferedB and not useDirectStore and operandScalar == "f32" %}
26
+ {% set matrixChunkTiles = compensatedMatrixChunkTiles if compensatedMatrixChunkTiles is defined else 0 %}
27
+ {% set useChunkedAccumulation = matrixChunkTiles > 0 and useDirectAStorage and not useDoubleBufferedB and not useDirectStore and operandScalar == "f32" and SPLIT_K == 1 %}
28
  {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
29
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
30
  {% set columnTileBaseValue = columnTileBase if columnTileBase is defined else 0 %}
31
  {% set rowTileBaseValue = rowTileBase if rowTileBase is defined else 0 %}
32
  // Four subgroups cover 32xN as a 2x2 grid, or taller tiles as four row bands.
33
+ {% if tileMValue > 32 %}
34
+ {% set subtileCols = tileNValue %}
35
+ {% set subtileRows = (tileMValue / 4)|int %}
36
+ {% else %}
37
  {% set subtileCols = (tileNValue / 2)|int %}
38
  {% set subtileRows = 16 %}
39
+ {% endif %}
40
  {% set aTilesPerSubgroup = (subtileRows / 8)|int %}
41
  {% set bTilesPerSubgroup = (subtileCols / 8)|int %}
42
  {% set subgroupCount = 4 %}
43
  {% set scratchBanks = subgroupCount * aTilesPerSubgroup * bTilesPerSubgroup %}
44
 
45
+ {% macro matrixAccumulators() %}
46
+ {% for m in range(aTilesPerSubgroup) %}
47
+ {% for n in range(bTilesPerSubgroup) %}
48
+ var matC{{ m }}{{ n }}: subgroup_matrix_result<{{ accScalar }}, 8, 8>;
49
+ {% endfor %}
50
+ {% endfor %}
51
+
52
+ {% endmacro %}
53
+
54
+ {% macro storeMatrixAccumulators() %}
55
+ {% for m in range(aTilesPerSubgroup) %}
56
+ {% for n in range(bTilesPerSubgroup) %}
57
+ subgroupMatrixStore<row_major>(&scratch[bank + {{ m * bTilesPerSubgroup + n }}u], 0u, matC{{ m }}{{ n }}, 8u);
58
+ {% endfor %}
59
+ {% endfor %}
60
+ {% endmacro %}
61
+
62
  {% if gemmEpi == "fastgelu" %}
63
  fn tanh_safe(x: f32) -> f32 {
64
  if (x > 10.0) { return 1.0; }
 
73
  {% set actMode = gemmActivation | default("") %}
74
  {% set actA = gemmActAlpha | default(0.0) %}
75
  {% set actB = gemmActBeta | default(0.0) %}
76
+ {% macro fused_act_return(mode, alpha, beta) -%}
77
+ {% if mode == "Relu" %}
78
  return max(v, 0.0);
79
+ {% elif mode == "Clip" %}
80
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
81
+ {% elif mode == "LeakyRelu" %}
82
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
83
+ {% elif mode == "Sigmoid" %}
84
  return 1.0 / (1.0 + exp(-v));
85
+ {% elif mode == "Tanh" %}
86
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
87
+ // the builtin preserves that saturated result for larger accumulators.
 
 
 
88
  return tanh(clamp(v, -10.0, 10.0));
89
+ {% elif mode == "HardSigmoid" %}
90
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
91
+ {% else %}
92
+ return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
93
  {% endif %}
94
+ {%- endmacro -%}
95
+ fn fused_act(v: f32) -> f32 {
96
+ {{ fused_act_return(actMode, actA, actB) -}}
97
  }
98
 
99
  {% endif %}
 
107
  const SUB_ROWS: u32 = {{ subtileRows }}u;
108
  const COLUMN_TILE_BASE: u32 = {{ columnTileBaseValue }}u;
109
  const ROW_TILE_BASE: u32 = {{ rowTileBaseValue }}u;
110
+ {% if not useDirectAStorage %}
111
 
112
  var<workgroup> tile_A: array<{{ operandScalar }}, {{ tileMValue }} * {{ tileKValue }}>;
113
+ {% endif %}
114
  var<workgroup> tile_B: array<{{ operandScalar }}, {{ (2 if useDoubleBufferedB else 1) * tileNValue }} * {{ tileKValue }}>;
115
  // Distinct readback banks for every result matrix, so one barrier can publish
116
  // the full subtile without write-after-read reuse.
117
  var<workgroup> scratch: array<array<{{ accScalar }}, 64>, {{ scratchBanks }}>;
118
 
119
+ {% if not useDirectAStorage %}
120
  fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
121
  {% if aTransposed %}
122
  // Generic transA stores A as [K,M]. Keep both the global and workgroup writes
 
153
  {% endif %}
154
  }
155
 
156
+ {% endif %}
157
  fn loadSHMB(tile_base: u32, k_idx: u32, row: u32, c_idx: u32, bank_offset: u32) {
158
  {% if bTransposed %}
159
  // Generic transB=0 stores B as [K,N]. Stage that native orientation and load
 
192
  {% set needsColBase = hasBias or (tailSafe is defined and tailSafe) %}
193
  {% set BIAS_1 = (" + " ~ accScalar ~ "(bias[col_base + col])") if hasBias else "" %}
194
  {% set BIAS_2 = (" + " ~ accScalar ~ "(bias[col_base + col2])") if hasBias else "" %}
195
+ {% set ACC_1 = "(prior.x + (scratch[src_slot][row * 8u + col] + tile_B[src_slot * 64u + row * 8u + col]))" if useBalancedAccumulation else "scratch[src_slot][row * 8u + col]" %}
196
+ {% set ACC_2 = "(prior.y + (scratch[src_slot][row * 8u + col2] + tile_B[src_slot * 64u + row * 8u + col2]))" if useBalancedAccumulation else "scratch[src_slot][row * 8u + col2]" %}
197
  fn storeOutput(offset: u32{% if needsColBase %}, col_base: u32{% endif %}, row: u32, col: u32, src_slot: u32, row_limit: i32) {
198
  if (row_limit > 0 && row < u32(row_limit)) {
199
  let col2: u32 = col + 1u;
200
+ {{ OUT }}[offset + row * OUT_F + col] = {{ OUT_SCALAR }}({{ epiOpen }}{{ ACC_1 }}{{ BIAS_1 }}{{ epiClose }});
201
+ {{ OUT }}[offset + row * OUT_F + col2] = {{ OUT_SCALAR }}({{ epiOpen }}{{ ACC_2 }}{{ BIAS_2 }}{{ epiClose }});
202
  }
203
  }
204
 
 
218
  let w_global_base: u32 = (COLUMN_TILE_BASE + workgroup_id.x) * TILE_COLS;
219
 
220
  let subtile_id: u32 = local_idx / sg_size;
221
+ {% if tileMValue > 32 %}
222
+ let subtile_idx: u32 = 0u;
223
+ let subtile_idy: u32 = subtile_id;
224
+ {% else %}
225
  let subtile_idx: u32 = subtile_id / 2u;
226
  let subtile_idy: u32 = subtile_id % 2u;
227
+ {% endif %}
228
  let base_A: u32 = subtile_idy * SUB_ROWS;
229
  let base_B: u32 = subtile_idx * SUB_COLS;
230
 
231
+ {% if not useChunkedAccumulation %}
232
+ {{ matrixAccumulators() }}
233
+ {% endif %}
234
+ {% if useChunkedAccumulation %}
235
+ let row = sg_id / 4u;
236
+ let col = (sg_id % 4u) * 2u;
237
+ let bank = subtile_id * {{ aTilesPerSubgroup * bTilesPerSubgroup }}u;
238
+ var chunkSums: array<vec2<f32>, {{ aTilesPerSubgroup * bTilesPerSubgroup }}>;
239
+ var chunkErrors: array<vec2<f32>, {{ aTilesPerSubgroup * bTilesPerSubgroup }}>;
240
+ for (var chunk_base = 0u; chunk_base < IN_F; chunk_base += TILE_K * {{ matrixChunkTiles }}u) {
241
+ {{ matrixAccumulators() }}
242
+ for (var kidx = chunk_base; kidx < min(chunk_base + TILE_K * {{ matrixChunkTiles }}u, IN_F); kidx += TILE_K) {
243
+ {% else %}
244
  for (var kidx: u32 = 0u; kidx < {{ kLoopEndValue }}; kidx += TILE_K) {
245
+ {% endif %}
246
+ {% if not useDirectAStorage %}
247
  loadSHMA(a_global_base, kidx, local_idx / {{ (tileKValue / 8)|int }}u, local_idx % {{ (tileKValue / 8)|int }}u);
248
+ {% endif %}
249
  loadSHMB(w_global_base, kidx, local_idx / {{ (tileKValue / 16)|int }}u, local_idx % {{ (tileKValue / 16)|int }}u, 0u);
250
  workgroupBarrier();
251
 
252
  for (var step: u32 = 0u; step < TILE_K; step += 8u) {
253
  {% set dynamicATiles = aTilesPerSubgroup if aTilesPerSubgroup is defined else 2 %}
254
+ {% set matrixAccumulator = matrixAccumulator | default("matC") %}
255
+ {% set matrixStepOffset = matrixStepOffset | default(0) %}
256
+ {% set matrixStep = "step" if matrixStepOffset == 0 else "(step + " ~ matrixStepOffset ~ "u)" %}
257
  {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
258
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
259
+ {% set directA = directAStorage is defined and directAStorage %}
260
  {% set B_BANK = "b_bank_offset + " if doubleBufferedB is defined and doubleBufferedB else "" %}
261
+ {% if directA %}
262
+ // Direct storage guarantees a full aligned M/K tile, so subgroup-matrix
263
+ // loads need no guarded workgroup staging.
264
+ let matrix_a_offset = (a_global_base + base_A) * IN_F + kidx + {{ matrixStep }};
265
+ {% elif aTransposed %}
266
+ let matrix_a_offset = {{ matrixStep }} * TILE_ROWS + subtile_idy * SUB_ROWS;
267
  {% else %}
268
+ let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + {{ matrixStep }};
269
  {% endif %}
270
  {% for m in range(dynamicATiles) %}
271
+ {% if directA %}
272
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&x, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * IN_F{% endif %}, IN_F);
273
+ {% elif aTransposed %}
274
  var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, col_major>(&tile_A, matrix_a_offset + {{ m * 8 }}u, TILE_ROWS);
275
  {% else %}
276
  var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * TILE_K{% endif %}, TILE_K);
 
278
  {% endfor %}
279
 
280
  {% if bTransposed %}
281
+ let matrix_b_offset = {{ B_BANK }}{{ matrixStep }} * TILE_COLS + subtile_idx * SUB_COLS;
282
  {% else %}
283
+ let matrix_b_offset = {{ B_BANK }}subtile_idx * SUB_COLS * TILE_K + {{ matrixStep }};
284
  {% endif %}
285
  {% for n in range(bTilesPerSubgroup) %}
286
  {% if bTransposed %}
 
292
 
293
  {% for m in range(dynamicATiles) %}
294
  {% for n in range(bTilesPerSubgroup) %}
295
+ {{ matrixAccumulator }}{{ m }}{{ n }} = subgroupMatrixMultiplyAccumulate(matA{{ m }}, matB{{ n }}, {{ matrixAccumulator }}{{ m }}{{ n }});
296
  {% endfor %}
297
  {% endfor %}
298
 
299
  }
300
  workgroupBarrier();
301
  }
302
+ {% if useChunkedAccumulation %}
303
+ {{ storeMatrixAccumulators() }}
304
+ workgroupBarrier();
305
+ {% for m in range(aTilesPerSubgroup) %}
306
+ {% for n in range(bTilesPerSubgroup) %}
307
+ {
308
+ let partial = vec2<f32>(scratch[bank + {{ m * bTilesPerSubgroup + n }}u][row * 8u + col], scratch[bank + {{ m * bTilesPerSubgroup + n }}u][row * 8u + col + 1u]);
309
+ let corrected = partial - chunkErrors[{{ m * bTilesPerSubgroup + n }}];
310
+ let next = chunkSums[{{ m * bTilesPerSubgroup + n }}] + corrected;
311
+ // Infinite totals keep a zero correction: Inf - Inf must not poison
312
+ // a later finite partial. NaN totals still propagate through the sum.
313
+ chunkErrors[{{ m * bTilesPerSubgroup + n }}] = select(vec2<f32>(0.0), (next - chunkSums[{{ m * bTilesPerSubgroup + n }}]) - corrected, abs(next) <= vec2<f32>(3.4028234663852886e38));
314
+ chunkSums[{{ m * bTilesPerSubgroup + n }}] = next;
315
+ }
316
+ {% endfor %}
317
+ {% endfor %}
318
+ workgroupBarrier();
319
+ }
320
+ {% endif %}
321
 
322
  let matrix_c_offset: u32 = (a_global_base + base_A) * OUT_F + w_global_base + base_B;
323
+ {% if useChunkedAccumulation %}
324
+ {% for m in range(aTilesPerSubgroup) %}
325
+ {% for n in range(bTilesPerSubgroup) %}
326
+ scratch[bank + {{ m * bTilesPerSubgroup + n }}u][row * 8u + col] = chunkSums[{{ m * bTilesPerSubgroup + n }}].x;
327
+ scratch[bank + {{ m * bTilesPerSubgroup + n }}u][row * 8u + col + 1u] = chunkSums[{{ m * bTilesPerSubgroup + n }}].y;
328
+ {% endfor %}
329
+ {% endfor %}
330
+ workgroupBarrier();
331
+ {% else %}
332
  let row: u32 = sg_id / 4u;
333
  let col: u32 = (sg_id % 4u) * 2u;
334
  // Stage every result into a distinct bank before one barrier. Tail/bias paths
 
336
  // across lanes, so its cross-lane readback must be published before partial-M
337
  // guards diverge. The epilogue also handles output conversion and bias.
338
  let bank: u32 = subtile_id * {{ aTilesPerSubgroup * bTilesPerSubgroup }}u;
339
+ {{ storeMatrixAccumulators() }}
 
 
 
 
340
  workgroupBarrier();
341
 
342
+ {% endif %}
343
  {% for m in range(aTilesPerSubgroup) %}
344
  let row_limit_{{ m }}: i32 = i32(params.M) - i32(a_global_base + base_A + {{ m * 8 }}u);
345
  {% for n in range(bTilesPerSubgroup) %}
 
351
  row,
352
  col,
353
  bank + {{ m * bTilesPerSubgroup + n }}u,
354
+ row_limit_{{ m }}{{ ", pairTotals[" ~ (m * bTilesPerSubgroup + n) ~ "]" if useBalancedAccumulation else "" }}
355
  );
356
  {% endfor %}
357
  {% endfor %}
build/webgpu/gemv-contiguous.wgsl.jinja ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% set gemvFused = gemvFused | default(false) %}
2
+ {% set COLS = gemvColumns | default(4) %}
3
+ {% set ACC = "f32" if COLS == 1 else "vec4<f32>" %}
4
+ {% macro wgsl_tree_fold_stmt(a, op, idx, svar) %}
5
+ {% if op == "max" %}
6
+ {{ a }}[{{ idx }}] = max({{ a }}[{{ idx }}], {{ a }}[{{ idx }} + {{ svar }}]);
7
+ {%- else %}
8
+ {{ a }}[{{ idx }}] = {{ a }}[{{ idx }}] + {{ a }}[{{ idx }} + {{ svar }}];
9
+ {%- endif %}
10
+ {% endmacro %}
11
+ {% macro wgsl_tree_fold(arrays, op="add", idx="lid", wg="WORKGROUP_SIZE", svar="stride", typed=false, form="tail", breakInline=false, bodyInline=false, barrierFirst=false) %}
12
+ var {{ svar }}{{ ": u32 " if typed else " " }}= {{ wg }} / 2u;
13
+ loop {
14
+ {% if form == "head" %}
15
+ {% if breakInline %}
16
+ if ({{ svar }} == 0u) { break; }
17
+ {% else %}
18
+ if ({{ svar }} == 0u) {
19
+ break;
20
+ }
21
+ {% endif %}
22
+ {% endif %}
23
+ {% if bodyInline %}
24
+ if ({{ idx }} < {{ svar }}) { {{ wgsl_tree_fold_stmt(arrays[0], op, idx, svar) }} }
25
+ {% else %}
26
+ if ({{ idx }} < {{ svar }}) {
27
+ {% for a in arrays %}
28
+ {{ wgsl_tree_fold_stmt(a, op, idx, svar) }}
29
+ {% endfor %}
30
+ }
31
+ {% endif %}
32
+ {% if form == "head" %}
33
+ {% if barrierFirst %}
34
+ workgroupBarrier();
35
+ {{ svar }} = {{ svar }} / 2u;
36
+ {% else %}
37
+ {{ svar }} = {{ svar }} / 2u;
38
+ workgroupBarrier();
39
+ {% endif %}
40
+ {% else %}
41
+ workgroupBarrier();
42
+ if ({{ svar }} == 1u) {
43
+ break;
44
+ }
45
+ {{ svar }} = {{ svar }} / 2u;
46
+ {% endif %}
47
+ }
48
+ {%- endmacro %}
49
+
50
+ {{ env.wgsl.resourceDeclarations }}
51
+ {% if gemvFused and (hasActivation | default(false)) %}
52
+ {% set actMode = gemmActivation | default("") %}
53
+ {% set actA = gemmActAlpha | default(0.0) %}
54
+ {% set actB = gemmActBeta | default(0.0) %}
55
+ {% macro fused_act_return(mode, alpha, beta) -%}
56
+ {% if mode == "Relu" %}
57
+ return max(v, 0.0);
58
+ {% elif mode == "Clip" %}
59
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
60
+ {% elif mode == "LeakyRelu" %}
61
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
62
+ {% elif mode == "Sigmoid" %}
63
+ return 1.0 / (1.0 + exp(-v));
64
+ {% elif mode == "Tanh" %}
65
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
66
+ // the builtin preserves that saturated result for larger accumulators.
67
+ return tanh(clamp(v, -10.0, 10.0));
68
+ {% elif mode == "HardSigmoid" %}
69
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
70
+ {% else %}
71
+ return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
72
+ {% endif %}
73
+ {%- endmacro -%}
74
+ fn fused_act(v: f32) -> f32 {
75
+ {{ fused_act_return(actMode, actA, actB) -}}
76
+ }
77
+
78
+ {% endif %}
79
+
80
+ const IN_F: u32 = {{ inFeatures }}u;
81
+ const OUT_F: u32 = {{ outFeatures }}u;
82
+ const WG: u32 = {{ workgroupSize }}u;
83
+
84
+ var<workgroup> partials: array<{{ ACC }}, {{ workgroupSize }}>;
85
+
86
+ @compute @workgroup_size(WG, 1, 1)
87
+ fn main(
88
+ @builtin(workgroup_id) wg: vec3<u32>,
89
+ @builtin(local_invocation_id) lid: vec3<u32>
90
+ ) {
91
+ let tid = lid.x;
92
+ // wg.z carries column-group bits past the per-dimension dispatch limit.
93
+ // This reduces to wg.x when the dispatch does not fold. col_base is workgroup-uniform, so
94
+ // this guard returns the whole over-dispatched workgroup before any barrier.
95
+ let col_base = (wg.x + wg.z * {{ DISPATCH_FOLD_WIDTH }}u) * {{ COLS }}u;
96
+ if (col_base >= OUT_F) {
97
+ return;
98
+ }
99
+ var acc = {{ ACC }}(0.0);
100
+
101
+ for (var i = tid; i < IN_F; i = i + WG) {
102
+ {% if xScalar == "f16" %}
103
+ let xv = f32(x[i]);
104
+ {% else %}
105
+ let xv = x[i];
106
+ {% endif %}
107
+ {% for j in range(COLS) %}
108
+ let w{{ j }} = w[(col_base + {{ j }}u) * IN_F + i];
109
+ {% endfor %}
110
+ {% if wScalar == "f16" %}
111
+ acc = acc + xv * {{ "f32(w0)" if COLS == 1 else "vec4<f32>(f32(w0), f32(w1), f32(w2), f32(w3))" }};
112
+ {% else %}
113
+ acc = acc + xv * {{ "w0" if COLS == 1 else "vec4<f32>(w0, w1, w2, w3)" }};
114
+ {% endif %}
115
+ }
116
+
117
+ partials[tid] = acc;
118
+ workgroupBarrier();
119
+
120
+ {{ wgsl_tree_fold(["partials"], idx="tid", wg="WG", form="head") }}
121
+
122
+ if (tid == 0u) {
123
+ {% if gemvFused %}
124
+ {% for j in range(COLS) %}
125
+ {
126
+ let n = col_base + {{ j }}u;
127
+ let product = select(partials[0]{% if COLS > 1 %}[{{ j }}u]{% endif %} * params.alpha, 0.0, params.alpha == 0.0);
128
+ {% if hasBias %}
129
+ let bias_term = select(f32(bias[{% if scalarBiasOk | default(false) %}0u{% else %}n{% endif %}]) * params.beta, 0.0, params.beta == 0.0);
130
+ {% endif %}
131
+ y[n] = {{ yScalar }}({% if hasActivation | default(false) %}fused_act({% endif %}product{% if hasBias %} + bias_term{% endif %}{% if hasActivation | default(false) %}){% endif %});
132
+ }
133
+ {% endfor %}
134
+ {% else %}
135
+ {% if hasBias is defined and hasBias %}
136
+ // C is a row vector over the output columns, so these four columns read four
137
+ // consecutive entries. This specialization requires beta = 1, so bias is
138
+ // added without a per-output multiply.
139
+ let total = partials[0] + vec4<f32>(
140
+ f32(bias[col_base + 0u]), f32(bias[col_base + 1u]),
141
+ f32(bias[col_base + 2u]), f32(bias[col_base + 3u]));
142
+ {% else %}
143
+ let total = partials[0];
144
+ {% endif %}
145
+ {% if yScalar == "f16" %}
146
+ y[col_base + 0u] = f16(total.x);
147
+ y[col_base + 1u] = f16(total.y);
148
+ y[col_base + 2u] = f16(total.z);
149
+ y[col_base + 3u] = f16(total.w);
150
+ {% else %}
151
+ y[col_base + 0u] = total.x;
152
+ y[col_base + 1u] = total.y;
153
+ y[col_base + 2u] = total.z;
154
+ y[col_base + 3u] = total.w;
155
+ {% endif %}
156
+ {% endif %}
157
+ }
158
+ }
build/webgpu/manifest.json CHANGED
@@ -2,54 +2,23 @@
2
  "domain": "com.microsoft",
3
  "name": "FusedGemm",
4
  "sinceVersion": 1,
5
- "description": "Gemm with a fused activation: `Y = act(alpha * A' * B' + beta * C)`, where `A'` and `B'` are optionally transposed and `C` is broadcastable to `(M, N)`. The activation runs in the f32 accumulator before the single output narrowing. This package supports `Relu`, `LeakyRelu`, `Sigmoid`, `Tanh` and `HardSigmoid`; the other activation strings and numeric types admitted by the open schema are not implemented. Omitting `activation` gives plain Gemm.",
6
- "inputs": [
7
- {
8
- "role": "A",
9
- "dtype": "T",
10
- "rank": 2,
11
- "description": "Left operand, `(M, K)` when `transA` is 0 and `(K, M)` otherwise."
12
- },
13
- {
14
- "role": "B",
15
- "dtype": "T",
16
- "rank": 2,
17
- "description": "Right operand, `(K, N)` when `transB` is 0 and `(N, K)` otherwise."
18
- },
19
- {
20
- "role": "C",
21
- "dtype": "T",
22
- "optional": true,
23
- "description": "Optional additive term, unidirectionally broadcastable to `(M, N)`: a scalar, a row `(N)`, a column `(M, 1)`, or the full matrix."
24
- }
25
- ],
26
- "outputs": [
27
- {
28
- "role": "Y",
29
- "dtype": "T",
30
- "rank": 2,
31
- "shape": "[gemmM, gemmN]",
32
- "description": "`act(alpha * A' * B' + beta * C)` with shape `(M, N)`."
33
- }
34
- ],
35
- "attributes": { "alpha": 1, "beta": 1, "transA": 0, "transB": 0 },
36
- "attributeDescriptions": {
37
- "alpha": "Scalar multiplier for the product `A' * B'`; the standard default is 1.",
38
- "beta": "Scalar multiplier for `C`; the standard default is 1.",
39
- "transA": "Whether `A` is stored transposed. The standard default is 0.",
40
- "transB": "Whether `B` is stored transposed. The standard default is 0.",
41
- "activation": "Optional fused activation name. Supported modes are `Relu`, `LeakyRelu`, `Sigmoid`, `Tanh` and `HardSigmoid`; omission applies none.",
42
- "activation_alpha": "First activation parameter: the slope for `LeakyRelu` or `alpha` for `HardSigmoid`.",
43
- "activation_beta": "Second activation parameter: `beta` for `HardSigmoid`."
44
  },
45
  "attributeConstraints": { "activation": { "values": ["Relu", "LeakyRelu", "Sigmoid", "Tanh", "HardSigmoid"] } },
46
  "typeConstraints": { "T": ["float32", "float16"] },
47
- "args": {
48
- "A": { "kind": "tensor", "semantic": "A", "role": "input" },
49
- "B": { "kind": "tensor", "semantic": "B", "role": "weights" },
50
- "C": { "kind": "tensor", "semantic": "C", "role": "weights", "required": false },
51
- "Y": { "kind": "tensor", "semantic": "Y", "role": "output" }
52
- },
53
  "derive": {
54
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
55
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
@@ -78,89 +47,114 @@
78
  "noBiasContract": "baseContract and not present.C",
79
  "biasContract": "baseContract and present.C and cOk",
80
  "sgmatOperandBytes": "2 if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else 4",
81
- "sgmatStorageBytes": "(32 * 32 + 64 * 32) * sgmatOperandBytes + 32 * 64 * 4",
 
 
 
 
82
  "sgmatResourcesFit": "128 <= deviceWorkgroupCap and sgmatStorageBytes <= device.limits.maxComputeWorkgroupStorageSize",
83
  "sgmatScalingOk": "(attrs.alpha if has(attrs, \"alpha\") else 1) == 1 and (attrs.beta if has(attrs, \"beta\") else 1) == 1",
84
- "sgmatLayoutOk": "gemmM > 0 and gemmK % 32 == 0 and gemmN % 64 == 0 and ((tensorDtypes.A == \"float16\" and device.features.has(\"shader-f16\") and gemmM >= 2) or (tensorDtypes.A == \"float32\" and gemmM >= 32)) and ceilDiv(gemmM, 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceilDiv(gemmN, 64) <= device.limits.maxComputeWorkgroupsPerDimension",
85
  "sgmatContract": "wave32Effective and sgmatLayoutOk and sgmatScalingOk and sgmatResourcesFit",
86
- "sgmatRowBiasContract": "sgmatContract and rowBiasOk and ranks.C == 1"
87
- },
88
- "constants": {
89
  "usesF16": "tensorDtypes.A == \"float16\"",
90
  "gemmActivation": "activationName",
91
  "gemmActAlpha": "activationAlpha",
92
  "gemmActBeta": "activationBeta",
93
- "gemmEpilogue": "\"activation\" if hasActivation else \"none\""
 
 
94
  },
95
- "bindingSets": {
96
- "noBias": [
97
- { "name": "a", "arg": "A", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
98
- { "name": "b", "arg": "B", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
99
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
100
- {
101
- "name": "params",
102
- "semantic": "kernel.params",
103
- "buffer": { "type": "uniform" },
104
- "struct": {
105
- "name": "Params",
106
- "fields": [
107
- { "name": "M", "type": "u32", "value": "gemmM" },
108
- { "name": "N", "type": "u32", "value": "gemmN" },
109
- { "name": "K", "type": "u32", "value": "gemmK" },
110
- { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" }
111
- ]
112
- }
113
- }
114
- ],
115
- "withBias": [
116
- { "name": "a", "arg": "A", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
117
- { "name": "b", "arg": "B", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
118
- { "name": "bias", "arg": "C", "semantic": "C", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
119
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
120
- {
121
- "name": "params",
122
- "semantic": "kernel.params",
123
- "buffer": { "type": "uniform" },
124
- "struct": {
125
- "name": "Params",
126
- "fields": [
127
- { "name": "M", "type": "u32", "value": "gemmM" },
128
- { "name": "N", "type": "u32", "value": "gemmN" },
129
- { "name": "K", "type": "u32", "value": "gemmK" },
130
- { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" },
131
- { "name": "beta", "type": "f32", "value": "attrs.beta if has(attrs, \"beta\") else 1" }
132
- ]
133
- }
134
- }
135
- ],
136
- "sgmatNoBias": [
137
- { "name": "x", "arg": "A", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
138
- { "name": "w", "arg": "B", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
139
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
140
- {
141
- "name": "params",
142
- "semantic": "kernel.params",
143
- "buffer": { "type": "uniform" },
144
- "struct": { "name": "Params", "fields": [{ "name": "M", "type": "u32", "value": "gemmM" }] }
145
- }
146
- ],
147
- "sgmatBias": [
148
- { "name": "x", "arg": "A", "semantic": "A", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
149
- { "name": "w", "arg": "B", "semantic": "B", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
150
- { "name": "bias", "arg": "C", "semantic": "C", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
151
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
152
- {
153
- "name": "params",
154
- "semantic": "kernel.params",
155
- "buffer": { "type": "uniform" },
156
- "struct": { "name": "Params", "fields": [{ "name": "M", "type": "u32", "value": "gemmM" }] }
157
- }
158
- ]
159
  },
160
  "variants": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  {
162
  "id": "notrans_sgmat_bias",
163
  "priority": 100,
 
164
  "requires": {
165
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
166
  "subgroupMatrixConfigs": [
@@ -168,32 +162,38 @@
168
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
169
  ]
170
  },
171
- "when": ["sgmatRowBiasContract", "transAFlag == false", "transBFlag == false"],
172
- "constants": {
173
  "hasBias": true,
174
  "aTransposedStorage": false,
175
  "bTransposedStorage": "not false",
176
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
177
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
178
- "bScalar": "\"f16\" if tensorDtypes.C == \"float16\" else \"f32\"",
179
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
180
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
181
  "inFeatures": "gemmK",
182
- "outFeatures": "gemmN"
 
 
 
 
 
183
  },
184
  "passes": [
185
  {
186
  "id": "main",
187
  "name": "FusedGemm.SubgroupMatrixBias",
188
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
189
- "bindings": "sgmatBias",
190
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
191
  }
192
  ]
193
  },
194
  {
195
  "id": "notrans_sgmat",
196
  "priority": 100,
 
197
  "requires": {
198
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
199
  "subgroupMatrixConfigs": [
@@ -201,31 +201,38 @@
201
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
202
  ]
203
  },
204
- "when": ["noBiasContract and sgmatContract", "transAFlag == false", "transBFlag == false"],
205
- "constants": {
206
  "hasBias": false,
207
  "aTransposedStorage": false,
208
  "bTransposedStorage": "not false",
209
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
210
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
211
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
212
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
213
  "inFeatures": "gemmK",
214
- "outFeatures": "gemmN"
 
 
 
 
 
215
  },
216
  "passes": [
217
  {
218
  "id": "main",
219
  "name": "FusedGemm.SubgroupMatrix",
220
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
221
- "bindings": "sgmatNoBias",
222
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
223
  }
224
  ]
225
  },
226
  {
227
  "id": "transA_sgmat_bias",
228
  "priority": 100,
 
229
  "requires": {
230
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
231
  "subgroupMatrixConfigs": [
@@ -233,32 +240,38 @@
233
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
234
  ]
235
  },
236
- "when": ["sgmatRowBiasContract", "transAFlag == true", "transBFlag == false"],
237
- "constants": {
238
  "hasBias": true,
239
  "aTransposedStorage": true,
240
  "bTransposedStorage": "not false",
241
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
242
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
243
- "bScalar": "\"f16\" if tensorDtypes.C == \"float16\" else \"f32\"",
244
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
245
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
246
  "inFeatures": "gemmK",
247
- "outFeatures": "gemmN"
 
 
 
 
 
248
  },
249
  "passes": [
250
  {
251
  "id": "main",
252
  "name": "FusedGemm.SubgroupMatrixBias",
253
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
254
- "bindings": "sgmatBias",
255
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
256
  }
257
  ]
258
  },
259
  {
260
  "id": "transA_sgmat",
261
  "priority": 100,
 
262
  "requires": {
263
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
264
  "subgroupMatrixConfigs": [
@@ -266,31 +279,38 @@
266
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
267
  ]
268
  },
269
- "when": ["noBiasContract and sgmatContract", "transAFlag == true", "transBFlag == false"],
270
- "constants": {
271
  "hasBias": false,
272
  "aTransposedStorage": true,
273
  "bTransposedStorage": "not false",
274
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
275
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
276
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
277
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
278
  "inFeatures": "gemmK",
279
- "outFeatures": "gemmN"
 
 
 
 
 
280
  },
281
  "passes": [
282
  {
283
  "id": "main",
284
  "name": "FusedGemm.SubgroupMatrix",
285
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
286
- "bindings": "sgmatNoBias",
287
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
288
  }
289
  ]
290
  },
291
  {
292
  "id": "transB_sgmat_bias",
293
  "priority": 100,
 
294
  "requires": {
295
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
296
  "subgroupMatrixConfigs": [
@@ -298,32 +318,38 @@
298
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
299
  ]
300
  },
301
- "when": ["sgmatRowBiasContract", "transAFlag == false", "transBFlag == true"],
302
- "constants": {
303
  "hasBias": true,
304
  "aTransposedStorage": false,
305
  "bTransposedStorage": "not true",
306
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
307
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
308
- "bScalar": "\"f16\" if tensorDtypes.C == \"float16\" else \"f32\"",
309
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
310
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
311
  "inFeatures": "gemmK",
312
- "outFeatures": "gemmN"
 
 
 
 
 
313
  },
314
  "passes": [
315
  {
316
  "id": "main",
317
  "name": "FusedGemm.SubgroupMatrixBias",
318
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
319
- "bindings": "sgmatBias",
320
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
321
  }
322
  ]
323
  },
324
  {
325
  "id": "transB_sgmat",
326
  "priority": 100,
 
327
  "requires": {
328
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
329
  "subgroupMatrixConfigs": [
@@ -331,31 +357,38 @@
331
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
332
  ]
333
  },
334
- "when": ["noBiasContract and sgmatContract", "transAFlag == false", "transBFlag == true"],
335
- "constants": {
336
  "hasBias": false,
337
  "aTransposedStorage": false,
338
  "bTransposedStorage": "not true",
339
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
340
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
341
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
342
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
343
  "inFeatures": "gemmK",
344
- "outFeatures": "gemmN"
 
 
 
 
 
345
  },
346
  "passes": [
347
  {
348
  "id": "main",
349
  "name": "FusedGemm.SubgroupMatrix",
350
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
351
- "bindings": "sgmatNoBias",
352
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
353
  }
354
  ]
355
  },
356
  {
357
  "id": "transAB_sgmat_bias",
358
  "priority": 100,
 
359
  "requires": {
360
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
361
  "subgroupMatrixConfigs": [
@@ -363,32 +396,38 @@
363
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
364
  ]
365
  },
366
- "when": ["sgmatRowBiasContract", "transAFlag == true", "transBFlag == true"],
367
- "constants": {
368
  "hasBias": true,
369
  "aTransposedStorage": true,
370
  "bTransposedStorage": "not true",
371
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
372
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
373
- "bScalar": "\"f16\" if tensorDtypes.C == \"float16\" else \"f32\"",
374
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
375
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
376
  "inFeatures": "gemmK",
377
- "outFeatures": "gemmN"
 
 
 
 
 
378
  },
379
  "passes": [
380
  {
381
  "id": "main",
382
  "name": "FusedGemm.SubgroupMatrixBias",
383
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
384
- "bindings": "sgmatBias",
385
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
386
  }
387
  ]
388
  },
389
  {
390
  "id": "transAB_sgmat",
391
  "priority": 100,
 
392
  "requires": {
393
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
394
  "subgroupMatrixConfigs": [
@@ -396,25 +435,31 @@
396
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
397
  ]
398
  },
399
- "when": ["noBiasContract and sgmatContract", "transAFlag == true", "transBFlag == true"],
400
- "constants": {
401
  "hasBias": false,
402
  "aTransposedStorage": true,
403
  "bTransposedStorage": "not true",
404
- "xScalar": "\"f16\" if tensorDtypes.A == \"float16\" else \"f32\"",
405
- "wScalar": "\"f16\" if tensorDtypes.B == \"float16\" else \"f32\"",
406
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
407
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
408
  "inFeatures": "gemmK",
409
- "outFeatures": "gemmN"
 
 
 
 
 
410
  },
411
  "passes": [
412
  {
413
  "id": "main",
414
  "name": "FusedGemm.SubgroupMatrix",
415
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
416
- "bindings": "sgmatNoBias",
417
- "dispatch": { "workgroups": "ceilDiv(gemmM, 32)", "x": "ceilDiv(gemmN, 64)" }
 
 
 
 
418
  }
419
  ]
420
  },
@@ -422,7 +467,7 @@
422
  "id": "notrans_bias",
423
  "priority": 0,
424
  "when": ["biasContract", "transAFlag == false", "transBFlag == false"],
425
- "constants": {
426
  "transA": false,
427
  "transB": false,
428
  "hasBias": true,
@@ -435,10 +480,25 @@
435
  "id": "main",
436
  "name": "FusedGemm.Bias",
437
  "shader": "fused-gemm.wgsl.jinja",
438
- "bindings": "withBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  "dispatch": {
440
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
441
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
442
  }
443
  }
444
  ]
@@ -447,16 +507,29 @@
447
  "id": "notrans",
448
  "priority": 0,
449
  "when": ["noBiasContract", "transAFlag == false", "transBFlag == false"],
450
- "constants": { "transA": false, "transB": false, "hasBias": false },
451
  "passes": [
452
  {
453
  "id": "main",
454
  "name": "FusedGemm",
455
  "shader": "fused-gemm.wgsl.jinja",
456
- "bindings": "noBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
457
  "dispatch": {
458
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
459
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
460
  }
461
  }
462
  ]
@@ -465,7 +538,7 @@
465
  "id": "transA_bias",
466
  "priority": 0,
467
  "when": ["biasContract", "transAFlag == true", "transBFlag == false"],
468
- "constants": {
469
  "transA": true,
470
  "transB": false,
471
  "hasBias": true,
@@ -478,10 +551,25 @@
478
  "id": "main",
479
  "name": "FusedGemm.Bias",
480
  "shader": "fused-gemm.wgsl.jinja",
481
- "bindings": "withBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  "dispatch": {
483
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
484
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
485
  }
486
  }
487
  ]
@@ -490,16 +578,29 @@
490
  "id": "transA",
491
  "priority": 0,
492
  "when": ["noBiasContract", "transAFlag == true", "transBFlag == false"],
493
- "constants": { "transA": true, "transB": false, "hasBias": false },
494
  "passes": [
495
  {
496
  "id": "main",
497
  "name": "FusedGemm",
498
  "shader": "fused-gemm.wgsl.jinja",
499
- "bindings": "noBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  "dispatch": {
501
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
502
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
503
  }
504
  }
505
  ]
@@ -508,7 +609,7 @@
508
  "id": "transB_bias",
509
  "priority": 0,
510
  "when": ["biasContract", "transAFlag == false", "transBFlag == true"],
511
- "constants": {
512
  "transA": false,
513
  "transB": true,
514
  "hasBias": true,
@@ -521,10 +622,25 @@
521
  "id": "main",
522
  "name": "FusedGemm.Bias",
523
  "shader": "fused-gemm.wgsl.jinja",
524
- "bindings": "withBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  "dispatch": {
526
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
527
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
528
  }
529
  }
530
  ]
@@ -533,16 +649,29 @@
533
  "id": "transB",
534
  "priority": 0,
535
  "when": ["noBiasContract", "transAFlag == false", "transBFlag == true"],
536
- "constants": { "transA": false, "transB": true, "hasBias": false },
537
  "passes": [
538
  {
539
  "id": "main",
540
  "name": "FusedGemm",
541
  "shader": "fused-gemm.wgsl.jinja",
542
- "bindings": "noBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
543
  "dispatch": {
544
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
545
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
546
  }
547
  }
548
  ]
@@ -551,7 +680,7 @@
551
  "id": "transAB_bias",
552
  "priority": 0,
553
  "when": ["biasContract", "transAFlag == true", "transBFlag == true"],
554
- "constants": {
555
  "transA": true,
556
  "transB": true,
557
  "hasBias": true,
@@ -564,10 +693,25 @@
564
  "id": "main",
565
  "name": "FusedGemm.Bias",
566
  "shader": "fused-gemm.wgsl.jinja",
567
- "bindings": "withBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  "dispatch": {
569
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
570
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
571
  }
572
  }
573
  ]
@@ -576,16 +720,29 @@
576
  "id": "transAB",
577
  "priority": 0,
578
  "when": ["noBiasContract", "transAFlag == true", "transBFlag == true"],
579
- "constants": { "transA": true, "transB": true, "hasBias": false },
580
  "passes": [
581
  {
582
  "id": "main",
583
  "name": "FusedGemm",
584
  "shader": "fused-gemm.wgsl.jinja",
585
- "bindings": "noBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
586
  "dispatch": {
587
- "x": "min(ceilDiv(gemmN, 64), device.limits.maxComputeWorkgroupsPerDimension)",
588
- "y": "min(ceilDiv(gemmM, 64), device.limits.maxComputeWorkgroupsPerDimension)"
589
  }
590
  }
591
  ]
 
2
  "domain": "com.microsoft",
3
  "name": "FusedGemm",
4
  "sinceVersion": 1,
5
+ "inputs": {
6
+ "A": { "dtype": "T", "rank": 2 },
7
+ "B": { "dtype": "T", "rank": 2 },
8
+ "C": { "dtype": "T", "optional": true }
9
+ },
10
+ "outputs": { "Y": { "dtype": "T", "rank": 2, "shape": "[gemmM, gemmN]" } },
11
+ "attributes": {
12
+ "alpha": { "default": 1 },
13
+ "beta": { "default": 1 },
14
+ "transA": { "default": 0 },
15
+ "transB": { "default": 0 },
16
+ "activation": {},
17
+ "activation_alpha": {},
18
+ "activation_beta": {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  },
20
  "attributeConstraints": { "activation": { "values": ["Relu", "LeakyRelu", "Sigmoid", "Tanh", "HardSigmoid"] } },
21
  "typeConstraints": { "T": ["float32", "float16"] },
 
 
 
 
 
 
22
  "derive": {
23
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
24
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
 
47
  "noBiasContract": "baseContract and not present.C",
48
  "biasContract": "baseContract and present.C and cOk",
49
  "sgmatOperandBytes": "2 if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else 4",
50
+ "sgmatTileM": "32",
51
+ "sgmatTileN": "64",
52
+ "sgmatTileK": "32",
53
+ "sgmatDirectA": "tensorDtypes.A == \"float32\" and not transAFlag and gemmK > 0 and gemmM % sgmatTileM == 0",
54
+ "sgmatStorageBytes": "((0 if sgmatDirectA else sgmatTileM) + sgmatTileN) * sgmatTileK * sgmatOperandBytes + sgmatTileM * sgmatTileN * 4",
55
  "sgmatResourcesFit": "128 <= deviceWorkgroupCap and sgmatStorageBytes <= device.limits.maxComputeWorkgroupStorageSize",
56
  "sgmatScalingOk": "(attrs.alpha if has(attrs, \"alpha\") else 1) == 1 and (attrs.beta if has(attrs, \"beta\") else 1) == 1",
57
+ "sgmatLayoutOk": "gemmM > 0 and gemmK % sgmatTileK == 0 and gemmN % sgmatTileN == 0 and ((tensorDtypes.A == \"float16\" and device.features.has(\"shader-f16\") and (gemmM >= 2 or (not transAFlag and transBFlag))) or (tensorDtypes.A == \"float32\" and gemmM >= sgmatTileM)) and ceilDiv(gemmM, sgmatTileM) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceilDiv(gemmN, sgmatTileN) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535)",
58
  "sgmatContract": "wave32Effective and sgmatLayoutOk and sgmatScalingOk and sgmatResourcesFit",
59
+ "sgmatRowBiasContract": "sgmatContract and rowBiasOk and ranks.C == 1",
 
 
60
  "usesF16": "tensorDtypes.A == \"float16\"",
61
  "gemmActivation": "activationName",
62
  "gemmActAlpha": "activationAlpha",
63
  "gemmActBeta": "activationBeta",
64
+ "gemmEpilogue": "\"activation\" if hasActivation else \"none\"",
65
+ "gemvColumns": "4 if gemmN % 4 == 0 else 1",
66
+ "gemvWorkgroup": "min(pow(2, log2ceil(deviceWorkgroupCap + 1) - 1), pow2ceil(max(gemmK, 1)), 64)"
67
  },
68
+ "bindings": {
69
+ "x": { "arg": "A", "buffer": "read-only-storage", "elementType": "$T" },
70
+ "w": { "arg": "B", "buffer": "read-only-storage", "elementType": "$T" },
71
+ "bias": { "arg": "C", "buffer": "read-only-storage", "elementType": "$T" },
72
+ "y": { "arg": "Y", "buffer": "storage", "elementType": "$T" },
73
+ "params": { "buffer": "uniform", "struct": [{ "name": "M", "type": "u32", "value": "gemmM" }] },
74
+ "a": { "arg": "A", "buffer": "read-only-storage", "elementType": "$T" },
75
+ "b": { "arg": "B", "buffer": "read-only-storage", "elementType": "$T" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  },
77
  "variants": [
78
+ {
79
+ "id": "gemv_plain",
80
+ "priority": 200,
81
+ "when": ["noBiasContract", "gemmM == 1 and gemmN > 0 and gemmK > 0 and transBFlag", "gemvWorkgroup * gemvColumns * 4 <= device.limits.maxComputeWorkgroupStorageSize"],
82
+ "derive": {
83
+ "hasBias": false,
84
+ "workgroupSize": "gemvWorkgroup",
85
+ "gemvFused": true,
86
+ "inFeatures": "gemmK",
87
+ "outFeatures": "gemmN",
88
+ "xScalar": "\"f16\" if usesF16 else \"f32\"",
89
+ "wScalar": "\"f16\" if usesF16 else \"f32\"",
90
+ "yScalar": "\"f16\" if usesF16 else \"f32\""
91
+ },
92
+ "passes": [
93
+ {
94
+ "id": "main",
95
+ "name": "FusedGemm.Gemv",
96
+ "shader": "gemv-contiguous.wgsl.jinja",
97
+ "bindings": [
98
+ "x",
99
+ "w",
100
+ "y",
101
+ {
102
+ "name": "params",
103
+ "struct": [{ "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" }]
104
+ }
105
+ ],
106
+ "dispatch": {
107
+ "x": "min(ceilDiv(gemmN, gemvColumns), DISPATCH_FOLD_WIDTH)",
108
+ "y": "1",
109
+ "z": "ceilDiv(ceilDiv(gemmN, gemvColumns), DISPATCH_FOLD_WIDTH)"
110
+ }
111
+ }
112
+ ]
113
+ },
114
+ {
115
+ "id": "gemv_bias",
116
+ "priority": 200,
117
+ "when": ["biasContract", "gemmM == 1 and gemmN > 0 and gemmK > 0 and transBFlag", "gemvWorkgroup * gemvColumns * 4 <= device.limits.maxComputeWorkgroupStorageSize"],
118
+ "derive": {
119
+ "hasBias": true,
120
+ "workgroupSize": "gemvWorkgroup",
121
+ "gemvFused": true,
122
+ "inFeatures": "gemmK",
123
+ "outFeatures": "gemmN",
124
+ "xScalar": "\"f16\" if usesF16 else \"f32\"",
125
+ "wScalar": "\"f16\" if usesF16 else \"f32\"",
126
+ "yScalar": "\"f16\" if usesF16 else \"f32\""
127
+ },
128
+ "passes": [
129
+ {
130
+ "id": "main",
131
+ "name": "FusedGemm.Gemv",
132
+ "shader": "gemv-contiguous.wgsl.jinja",
133
+ "bindings": [
134
+ "x",
135
+ "w",
136
+ "bias",
137
+ "y",
138
+ {
139
+ "name": "params",
140
+ "struct": [
141
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" },
142
+ { "name": "beta", "type": "f32", "value": "attrs.beta if has(attrs, \"beta\") else 1" }
143
+ ]
144
+ }
145
+ ],
146
+ "dispatch": {
147
+ "x": "min(ceilDiv(gemmN, gemvColumns), DISPATCH_FOLD_WIDTH)",
148
+ "y": "1",
149
+ "z": "ceilDiv(ceilDiv(gemmN, gemvColumns), DISPATCH_FOLD_WIDTH)"
150
+ }
151
+ }
152
+ ]
153
+ },
154
  {
155
  "id": "notrans_sgmat_bias",
156
  "priority": 100,
157
+ "when": ["sgmatRowBiasContract", "transAFlag == false", "transBFlag == false"],
158
  "requires": {
159
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
160
  "subgroupMatrixConfigs": [
 
162
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
163
  ]
164
  },
165
+ "derive": {
 
166
  "hasBias": true,
167
  "aTransposedStorage": false,
168
  "bTransposedStorage": "not false",
 
 
 
169
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
170
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
171
  "inFeatures": "gemmK",
172
+ "outFeatures": "gemmN",
173
+ "tileM": "sgmatTileM",
174
+ "tileN": "sgmatTileN",
175
+ "tileK": "sgmatTileK",
176
+ "directAStorage": "sgmatDirectA",
177
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
178
  },
179
  "passes": [
180
  {
181
  "id": "main",
182
  "name": "FusedGemm.SubgroupMatrixBias",
183
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
184
+ "bindings": ["x", "w", "bias", "y", "params"],
185
+ "dispatch": {
186
+ "x": "ceilDiv(gemmN, sgmatTileN)",
187
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
188
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
189
+ }
190
  }
191
  ]
192
  },
193
  {
194
  "id": "notrans_sgmat",
195
  "priority": 100,
196
+ "when": ["noBiasContract and sgmatContract", "transAFlag == false", "transBFlag == false"],
197
  "requires": {
198
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
199
  "subgroupMatrixConfigs": [
 
201
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
202
  ]
203
  },
204
+ "derive": {
 
205
  "hasBias": false,
206
  "aTransposedStorage": false,
207
  "bTransposedStorage": "not false",
 
 
208
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
209
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
210
  "inFeatures": "gemmK",
211
+ "outFeatures": "gemmN",
212
+ "tileM": "sgmatTileM",
213
+ "tileN": "sgmatTileN",
214
+ "tileK": "sgmatTileK",
215
+ "directAStorage": "sgmatDirectA",
216
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
217
  },
218
  "passes": [
219
  {
220
  "id": "main",
221
  "name": "FusedGemm.SubgroupMatrix",
222
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
223
+ "bindings": ["x", "w", "y", "params"],
224
+ "dispatch": {
225
+ "x": "ceilDiv(gemmN, sgmatTileN)",
226
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
227
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
228
+ }
229
  }
230
  ]
231
  },
232
  {
233
  "id": "transA_sgmat_bias",
234
  "priority": 100,
235
+ "when": ["sgmatRowBiasContract", "transAFlag == true", "transBFlag == false"],
236
  "requires": {
237
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
238
  "subgroupMatrixConfigs": [
 
240
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
241
  ]
242
  },
243
+ "derive": {
 
244
  "hasBias": true,
245
  "aTransposedStorage": true,
246
  "bTransposedStorage": "not false",
 
 
 
247
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
248
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
249
  "inFeatures": "gemmK",
250
+ "outFeatures": "gemmN",
251
+ "tileM": "sgmatTileM",
252
+ "tileN": "sgmatTileN",
253
+ "tileK": "sgmatTileK",
254
+ "directAStorage": "sgmatDirectA",
255
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
256
  },
257
  "passes": [
258
  {
259
  "id": "main",
260
  "name": "FusedGemm.SubgroupMatrixBias",
261
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
262
+ "bindings": ["x", "w", "bias", "y", "params"],
263
+ "dispatch": {
264
+ "x": "ceilDiv(gemmN, sgmatTileN)",
265
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
266
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
267
+ }
268
  }
269
  ]
270
  },
271
  {
272
  "id": "transA_sgmat",
273
  "priority": 100,
274
+ "when": ["noBiasContract and sgmatContract", "transAFlag == true", "transBFlag == false"],
275
  "requires": {
276
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
277
  "subgroupMatrixConfigs": [
 
279
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
280
  ]
281
  },
282
+ "derive": {
 
283
  "hasBias": false,
284
  "aTransposedStorage": true,
285
  "bTransposedStorage": "not false",
 
 
286
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
287
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
288
  "inFeatures": "gemmK",
289
+ "outFeatures": "gemmN",
290
+ "tileM": "sgmatTileM",
291
+ "tileN": "sgmatTileN",
292
+ "tileK": "sgmatTileK",
293
+ "directAStorage": "sgmatDirectA",
294
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
295
  },
296
  "passes": [
297
  {
298
  "id": "main",
299
  "name": "FusedGemm.SubgroupMatrix",
300
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
301
+ "bindings": ["x", "w", "y", "params"],
302
+ "dispatch": {
303
+ "x": "ceilDiv(gemmN, sgmatTileN)",
304
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
305
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
306
+ }
307
  }
308
  ]
309
  },
310
  {
311
  "id": "transB_sgmat_bias",
312
  "priority": 100,
313
+ "when": ["sgmatRowBiasContract", "transAFlag == false", "transBFlag == true"],
314
  "requires": {
315
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
316
  "subgroupMatrixConfigs": [
 
318
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
319
  ]
320
  },
321
+ "derive": {
 
322
  "hasBias": true,
323
  "aTransposedStorage": false,
324
  "bTransposedStorage": "not true",
 
 
 
325
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
326
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
327
  "inFeatures": "gemmK",
328
+ "outFeatures": "gemmN",
329
+ "tileM": "sgmatTileM",
330
+ "tileN": "sgmatTileN",
331
+ "tileK": "sgmatTileK",
332
+ "directAStorage": "sgmatDirectA",
333
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
334
  },
335
  "passes": [
336
  {
337
  "id": "main",
338
  "name": "FusedGemm.SubgroupMatrixBias",
339
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
340
+ "bindings": ["x", "w", "bias", "y", "params"],
341
+ "dispatch": {
342
+ "x": "ceilDiv(gemmN, sgmatTileN)",
343
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
344
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
345
+ }
346
  }
347
  ]
348
  },
349
  {
350
  "id": "transB_sgmat",
351
  "priority": 100,
352
+ "when": ["noBiasContract and sgmatContract", "transAFlag == false", "transBFlag == true"],
353
  "requires": {
354
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
355
  "subgroupMatrixConfigs": [
 
357
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
358
  ]
359
  },
360
+ "derive": {
 
361
  "hasBias": false,
362
  "aTransposedStorage": false,
363
  "bTransposedStorage": "not true",
 
 
364
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
365
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
366
  "inFeatures": "gemmK",
367
+ "outFeatures": "gemmN",
368
+ "tileM": "sgmatTileM",
369
+ "tileN": "sgmatTileN",
370
+ "tileK": "sgmatTileK",
371
+ "directAStorage": "sgmatDirectA",
372
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
373
  },
374
  "passes": [
375
  {
376
  "id": "main",
377
  "name": "FusedGemm.SubgroupMatrix",
378
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
379
+ "bindings": ["x", "w", "y", "params"],
380
+ "dispatch": {
381
+ "x": "ceilDiv(gemmN, sgmatTileN)",
382
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
383
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
384
+ }
385
  }
386
  ]
387
  },
388
  {
389
  "id": "transAB_sgmat_bias",
390
  "priority": 100,
391
+ "when": ["sgmatRowBiasContract", "transAFlag == true", "transBFlag == true"],
392
  "requires": {
393
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
394
  "subgroupMatrixConfigs": [
 
396
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
397
  ]
398
  },
399
+ "derive": {
 
400
  "hasBias": true,
401
  "aTransposedStorage": true,
402
  "bTransposedStorage": "not true",
 
 
 
403
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
404
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
405
  "inFeatures": "gemmK",
406
+ "outFeatures": "gemmN",
407
+ "tileM": "sgmatTileM",
408
+ "tileN": "sgmatTileN",
409
+ "tileK": "sgmatTileK",
410
+ "directAStorage": "sgmatDirectA",
411
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
412
  },
413
  "passes": [
414
  {
415
  "id": "main",
416
  "name": "FusedGemm.SubgroupMatrixBias",
417
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
418
+ "bindings": ["x", "w", "bias", "y", "params"],
419
+ "dispatch": {
420
+ "x": "ceilDiv(gemmN, sgmatTileN)",
421
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
422
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
423
+ }
424
  }
425
  ]
426
  },
427
  {
428
  "id": "transAB_sgmat",
429
  "priority": 100,
430
+ "when": ["noBiasContract and sgmatContract", "transAFlag == true", "transBFlag == true"],
431
  "requires": {
432
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
433
  "subgroupMatrixConfigs": [
 
435
  { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
436
  ]
437
  },
438
+ "derive": {
 
439
  "hasBias": false,
440
  "aTransposedStorage": true,
441
  "bTransposedStorage": "not true",
 
 
442
  "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
443
  "mmaScalar": "\"f16\" if tensorDtypes.A == \"float16\" and tensorDtypes.B == \"float16\" else \"f32\"",
444
  "inFeatures": "gemmK",
445
+ "outFeatures": "gemmN",
446
+ "tileM": "sgmatTileM",
447
+ "tileN": "sgmatTileN",
448
+ "tileK": "sgmatTileK",
449
+ "directAStorage": "sgmatDirectA",
450
+ "compensatedMatrixChunkTiles": "2 if sgmatDirectA and gemmK >= 8 * sgmatTileK else 0"
451
  },
452
  "passes": [
453
  {
454
  "id": "main",
455
  "name": "FusedGemm.SubgroupMatrix",
456
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
457
+ "bindings": ["x", "w", "y", "params"],
458
+ "dispatch": {
459
+ "x": "ceilDiv(gemmN, sgmatTileN)",
460
+ "y": "min(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)",
461
+ "z": "ceilDiv(ceilDiv(gemmM, sgmatTileM), DISPATCH_FOLD_WIDTH)"
462
+ }
463
  }
464
  ]
465
  },
 
467
  "id": "notrans_bias",
468
  "priority": 0,
469
  "when": ["biasContract", "transAFlag == false", "transBFlag == false"],
470
+ "derive": {
471
  "transA": false,
472
  "transB": false,
473
  "hasBias": true,
 
480
  "id": "main",
481
  "name": "FusedGemm.Bias",
482
  "shader": "fused-gemm.wgsl.jinja",
483
+ "bindings": [
484
+ "a",
485
+ "b",
486
+ "bias",
487
+ "y",
488
+ {
489
+ "name": "params",
490
+ "struct": [
491
+ { "name": "M", "type": "u32", "value": "gemmM" },
492
+ { "name": "N", "type": "u32", "value": "gemmN" },
493
+ { "name": "K", "type": "u32", "value": "gemmK" },
494
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" },
495
+ { "name": "beta", "type": "f32", "value": "attrs.beta if has(attrs, \"beta\") else 1" }
496
+ ]
497
+ }
498
+ ],
499
  "dispatch": {
500
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
501
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
502
  }
503
  }
504
  ]
 
507
  "id": "notrans",
508
  "priority": 0,
509
  "when": ["noBiasContract", "transAFlag == false", "transBFlag == false"],
510
+ "derive": { "transA": false, "transB": false, "hasBias": false },
511
  "passes": [
512
  {
513
  "id": "main",
514
  "name": "FusedGemm",
515
  "shader": "fused-gemm.wgsl.jinja",
516
+ "bindings": [
517
+ "a",
518
+ "b",
519
+ "y",
520
+ {
521
+ "name": "params",
522
+ "struct": [
523
+ { "name": "M", "type": "u32", "value": "gemmM" },
524
+ { "name": "N", "type": "u32", "value": "gemmN" },
525
+ { "name": "K", "type": "u32", "value": "gemmK" },
526
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" }
527
+ ]
528
+ }
529
+ ],
530
  "dispatch": {
531
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
532
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
533
  }
534
  }
535
  ]
 
538
  "id": "transA_bias",
539
  "priority": 0,
540
  "when": ["biasContract", "transAFlag == true", "transBFlag == false"],
541
+ "derive": {
542
  "transA": true,
543
  "transB": false,
544
  "hasBias": true,
 
551
  "id": "main",
552
  "name": "FusedGemm.Bias",
553
  "shader": "fused-gemm.wgsl.jinja",
554
+ "bindings": [
555
+ "a",
556
+ "b",
557
+ "bias",
558
+ "y",
559
+ {
560
+ "name": "params",
561
+ "struct": [
562
+ { "name": "M", "type": "u32", "value": "gemmM" },
563
+ { "name": "N", "type": "u32", "value": "gemmN" },
564
+ { "name": "K", "type": "u32", "value": "gemmK" },
565
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" },
566
+ { "name": "beta", "type": "f32", "value": "attrs.beta if has(attrs, \"beta\") else 1" }
567
+ ]
568
+ }
569
+ ],
570
  "dispatch": {
571
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
572
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
573
  }
574
  }
575
  ]
 
578
  "id": "transA",
579
  "priority": 0,
580
  "when": ["noBiasContract", "transAFlag == true", "transBFlag == false"],
581
+ "derive": { "transA": true, "transB": false, "hasBias": false },
582
  "passes": [
583
  {
584
  "id": "main",
585
  "name": "FusedGemm",
586
  "shader": "fused-gemm.wgsl.jinja",
587
+ "bindings": [
588
+ "a",
589
+ "b",
590
+ "y",
591
+ {
592
+ "name": "params",
593
+ "struct": [
594
+ { "name": "M", "type": "u32", "value": "gemmM" },
595
+ { "name": "N", "type": "u32", "value": "gemmN" },
596
+ { "name": "K", "type": "u32", "value": "gemmK" },
597
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" }
598
+ ]
599
+ }
600
+ ],
601
  "dispatch": {
602
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
603
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
604
  }
605
  }
606
  ]
 
609
  "id": "transB_bias",
610
  "priority": 0,
611
  "when": ["biasContract", "transAFlag == false", "transBFlag == true"],
612
+ "derive": {
613
  "transA": false,
614
  "transB": true,
615
  "hasBias": true,
 
622
  "id": "main",
623
  "name": "FusedGemm.Bias",
624
  "shader": "fused-gemm.wgsl.jinja",
625
+ "bindings": [
626
+ "a",
627
+ "b",
628
+ "bias",
629
+ "y",
630
+ {
631
+ "name": "params",
632
+ "struct": [
633
+ { "name": "M", "type": "u32", "value": "gemmM" },
634
+ { "name": "N", "type": "u32", "value": "gemmN" },
635
+ { "name": "K", "type": "u32", "value": "gemmK" },
636
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" },
637
+ { "name": "beta", "type": "f32", "value": "attrs.beta if has(attrs, \"beta\") else 1" }
638
+ ]
639
+ }
640
+ ],
641
  "dispatch": {
642
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
643
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
644
  }
645
  }
646
  ]
 
649
  "id": "transB",
650
  "priority": 0,
651
  "when": ["noBiasContract", "transAFlag == false", "transBFlag == true"],
652
+ "derive": { "transA": false, "transB": true, "hasBias": false },
653
  "passes": [
654
  {
655
  "id": "main",
656
  "name": "FusedGemm",
657
  "shader": "fused-gemm.wgsl.jinja",
658
+ "bindings": [
659
+ "a",
660
+ "b",
661
+ "y",
662
+ {
663
+ "name": "params",
664
+ "struct": [
665
+ { "name": "M", "type": "u32", "value": "gemmM" },
666
+ { "name": "N", "type": "u32", "value": "gemmN" },
667
+ { "name": "K", "type": "u32", "value": "gemmK" },
668
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" }
669
+ ]
670
+ }
671
+ ],
672
  "dispatch": {
673
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
674
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
675
  }
676
  }
677
  ]
 
680
  "id": "transAB_bias",
681
  "priority": 0,
682
  "when": ["biasContract", "transAFlag == true", "transBFlag == true"],
683
+ "derive": {
684
  "transA": true,
685
  "transB": true,
686
  "hasBias": true,
 
693
  "id": "main",
694
  "name": "FusedGemm.Bias",
695
  "shader": "fused-gemm.wgsl.jinja",
696
+ "bindings": [
697
+ "a",
698
+ "b",
699
+ "bias",
700
+ "y",
701
+ {
702
+ "name": "params",
703
+ "struct": [
704
+ { "name": "M", "type": "u32", "value": "gemmM" },
705
+ { "name": "N", "type": "u32", "value": "gemmN" },
706
+ { "name": "K", "type": "u32", "value": "gemmK" },
707
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" },
708
+ { "name": "beta", "type": "f32", "value": "attrs.beta if has(attrs, \"beta\") else 1" }
709
+ ]
710
+ }
711
+ ],
712
  "dispatch": {
713
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
714
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
715
  }
716
  }
717
  ]
 
720
  "id": "transAB",
721
  "priority": 0,
722
  "when": ["noBiasContract", "transAFlag == true", "transBFlag == true"],
723
+ "derive": { "transA": true, "transB": true, "hasBias": false },
724
  "passes": [
725
  {
726
  "id": "main",
727
  "name": "FusedGemm",
728
  "shader": "fused-gemm.wgsl.jinja",
729
+ "bindings": [
730
+ "a",
731
+ "b",
732
+ "y",
733
+ {
734
+ "name": "params",
735
+ "struct": [
736
+ { "name": "M", "type": "u32", "value": "gemmM" },
737
+ { "name": "N", "type": "u32", "value": "gemmN" },
738
+ { "name": "K", "type": "u32", "value": "gemmK" },
739
+ { "name": "alpha", "type": "f32", "value": "attrs.alpha if has(attrs, \"alpha\") else 1" }
740
+ ]
741
+ }
742
+ ],
743
  "dispatch": {
744
+ "x": "min(ceilDiv(gemmN, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
745
+ "y": "min(ceilDiv(gemmM, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
746
  }
747
  }
748
  ]
build/webgpu/metadata.json CHANGED
@@ -1,19 +1,42 @@
1
  {
2
  "name": "com.microsoft.FusedGemm",
3
- "id": "_com_microsoft_fusedgemm_webgpu_61fbd6b",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "5+Pgon1ED+99xPL6smxxz2G47C5gDnfB0kJ+Zj2wZLc=",
11
- "fused-gemm.wgsl.jinja": "XL5rMA9oUlUJZCMHq2PZAzmORxFZoz/xa5Mo05/aK3k=",
12
- "gemm-subgroup-matrix.wgsl.jinja": "PF4rqax3Fvj0sfPRjj+0BeuUkfoJVM2Bt6r7yz4Q2y0=",
13
- "manifest.json": "s7cSq5so+VQS0XTbiHalmSHGnQQA2ylTu524o3V815U=",
14
- "test.json": "LRhFk3xI+8tnvhAwEvFnept0OjRWCvXhjpjQ31ZIK0E="
 
15
  }
16
  },
17
- "provenance": { "kernel": { "sha": "c928d21e6cc1310861cba3bafb75f5f679ecf5f3", "dirty": false } },
18
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.FusedGemm" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
 
1
  {
2
  "name": "com.microsoft.FusedGemm",
3
+ "id": "_com_microsoft_fusedgemm_webgpu_abf1d0b",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "wb8UHX4mLph1fuImZI56hUkFg3rsazdLNuDl69XrKdM=",
11
+ "fused-gemm.wgsl.jinja": "A7FcGNQgqc57hkI4skKQutwPNJzfif1nI3C2f36hZcc=",
12
+ "gemm-subgroup-matrix.wgsl.jinja": "PSvZd1vPhlsbysyRrWuBMC4iHBhQq6VuOjGmFWsF2K8=",
13
+ "gemv-contiguous.wgsl.jinja": "sBwHBkgw/X6QKltFcxbi4TSPvrCh5xM8QAGZIt8nMzg=",
14
+ "manifest.json": "JSX1Yq1ypgxc4wVUAoDBbUKhyOn5NiuF5pzNihs85SY=",
15
+ "test.json": "j+1eYKc0EE8Rt4YEY/oS5mEuBucLF/pRQCkqnb0zB6Q="
16
  }
17
  },
18
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
19
+ "webgpu": {
20
+ "manifestSpec": "2.0",
21
+ "variants": {
22
+ "gemv_plain": ["gemv-contiguous.wgsl.jinja"],
23
+ "gemv_bias": ["gemv-contiguous.wgsl.jinja"],
24
+ "notrans_sgmat_bias": ["gemm-subgroup-matrix.wgsl.jinja"],
25
+ "notrans_sgmat": ["gemm-subgroup-matrix.wgsl.jinja"],
26
+ "transA_sgmat_bias": ["gemm-subgroup-matrix.wgsl.jinja"],
27
+ "transA_sgmat": ["gemm-subgroup-matrix.wgsl.jinja"],
28
+ "transB_sgmat_bias": ["gemm-subgroup-matrix.wgsl.jinja"],
29
+ "transB_sgmat": ["gemm-subgroup-matrix.wgsl.jinja"],
30
+ "transAB_sgmat_bias": ["gemm-subgroup-matrix.wgsl.jinja"],
31
+ "transAB_sgmat": ["gemm-subgroup-matrix.wgsl.jinja"],
32
+ "notrans_bias": ["fused-gemm.wgsl.jinja"],
33
+ "notrans": ["fused-gemm.wgsl.jinja"],
34
+ "transA_bias": ["fused-gemm.wgsl.jinja"],
35
+ "transA": ["fused-gemm.wgsl.jinja"],
36
+ "transB_bias": ["fused-gemm.wgsl.jinja"],
37
+ "transB": ["fused-gemm.wgsl.jinja"],
38
+ "transAB_bias": ["fused-gemm.wgsl.jinja"],
39
+ "transAB": ["fused-gemm.wgsl.jinja"]
40
+ }
41
+ }
42
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "com.microsoft.FusedGemm",
3
  "cases": [
4
  {
5
  "name": "notrans_rowbias_relu",
@@ -95,7 +94,7 @@
95
  },
96
  {
97
  "name": "notrans_nobias_sigmoid",
98
- "provenance": { "notes": "No bias at all -- the other binding set -- with Sigmoid." },
99
  "attrs": { "activation": "Sigmoid" },
100
  "inputs": {
101
  "A": {
@@ -294,7 +293,7 @@
294
  },
295
  {
296
  "name": "beta_zero_relu",
297
- "provenance": { "notes": "beta = 0 nulls the C term the same way." },
298
  "attrs": { "beta": 0, "activation": "Relu" },
299
  "inputs": {
300
  "A": {
@@ -481,7 +480,7 @@
481
  {
482
  "name": "sgmat_notrans_rowbias_relu",
483
  "provenance": {
484
- "notes": "Aligned for the subgroup-matrix gate (M >= 32, K % 32 == 0, N % 64 == 0), so this selects notrans_sgmat_bias where the unaligned fixtures above stay on the tiled kernel. Stages A as [M,K] and B as [K,N], which is the operand orientation this transpose pair asks the matrix kernel for, and fuses Relu in the accumulator domain over a rank-1 row bias."
485
  },
486
  "attrs": { "activation": "Relu" },
487
  "inputs": {
@@ -506,7 +505,7 @@
506
  {
507
  "name": "sgmat_notrans_nobias_tanh",
508
  "provenance": {
509
- "notes": "The no-bias arm of the same gate and layout: the matrix kernel renders a separate set of store sites without the bias term."
510
  },
511
  "attrs": { "activation": "Tanh" },
512
  "inputs": {
@@ -526,7 +525,7 @@
526
  {
527
  "name": "notrans_rowbias_tanh_saturating_preactivation",
528
  "provenance": {
529
- "notes": "Saturating pre-activations: the row bias is large enough that most |alpha*A*B + beta*C| land past 44, where the hardware tanh's internal e^2v overflows and the quotient becomes Inf/Inf = NaN. The bias carries the magnitude rather than the operands so the accumulator stays small and well conditioned — large operands make the near-cancelling dot products disagree between accumulation orders, which is a property of the fixture rather than of the activation. Unaligned for the subgroup-matrix gate, so this covers the tiled store path."
530
  },
531
  "attrs": { "activation": "Tanh" },
532
  "inputs": {
@@ -551,7 +550,7 @@
551
  {
552
  "name": "sgmat_notrans_rowbias_tanh_saturating_preactivation",
553
  "provenance": {
554
- "notes": "The aligned twin of the saturating fixture above (M >= 32, K % 32 == 0, N % 64 == 0), so the subgroup-matrix route evaluates the same overflowing pre-activations through its own store sites."
555
  },
556
  "attrs": { "activation": "Tanh" },
557
  "inputs": {
@@ -576,7 +575,7 @@
576
  {
577
  "name": "sgmat_transa_rowbias_tanh",
578
  "provenance": {
579
- "notes": "Aligned for the subgroup-matrix gate (M >= 32, K % 32 == 0, N % 64 == 0), so this selects transa_sgmat_bias where the unaligned fixtures above stay on the tiled kernel. Stages A as [K,M] and B as [K,N], which is the operand orientation this transpose pair asks the matrix kernel for, and fuses Tanh in the accumulator domain over a rank-1 row bias."
580
  },
581
  "attrs": { "transA": 1, "activation": "Tanh" },
582
  "inputs": {
@@ -601,7 +600,7 @@
601
  {
602
  "name": "sgmat_transa_nobias_sigmoid",
603
  "provenance": {
604
- "notes": "The no-bias arm of the same gate and layout: the matrix kernel renders a separate set of store sites without the bias term."
605
  },
606
  "attrs": { "transA": 1, "activation": "Sigmoid" },
607
  "inputs": {
@@ -621,7 +620,7 @@
621
  {
622
  "name": "sgmat_transb_rowbias_sigmoid",
623
  "provenance": {
624
- "notes": "Aligned for the subgroup-matrix gate (M >= 32, K % 32 == 0, N % 64 == 0), so this selects transb_sgmat_bias where the unaligned fixtures above stay on the tiled kernel. Stages A as [M,K] and B as [N,K], which is the operand orientation this transpose pair asks the matrix kernel for, and fuses Sigmoid in the accumulator domain over a rank-1 row bias."
625
  },
626
  "attrs": { "transB": 1, "activation": "Sigmoid" },
627
  "inputs": {
@@ -646,7 +645,7 @@
646
  {
647
  "name": "sgmat_transb_nobias_leakyrelu",
648
  "provenance": {
649
- "notes": "The no-bias arm of the same gate and layout: the matrix kernel renders a separate set of store sites without the bias term."
650
  },
651
  "attrs": { "transB": 1, "activation": "LeakyRelu" },
652
  "inputs": {
@@ -666,7 +665,7 @@
666
  {
667
  "name": "sgmat_transab_rowbias_leakyrelu",
668
  "provenance": {
669
- "notes": "Aligned for the subgroup-matrix gate (M >= 32, K % 32 == 0, N % 64 == 0), so this selects transab_sgmat_bias where the unaligned fixtures above stay on the tiled kernel. Stages A as [K,M] and B as [N,K], which is the operand orientation this transpose pair asks the matrix kernel for, and fuses LeakyRelu in the accumulator domain over a rank-1 row bias."
670
  },
671
  "attrs": { "transA": 1, "transB": 1, "activation": "LeakyRelu" },
672
  "inputs": {
@@ -691,7 +690,7 @@
691
  {
692
  "name": "sgmat_transab_nobias_relu",
693
  "provenance": {
694
- "notes": "The no-bias arm of the same gate and layout: the matrix kernel renders a separate set of store sites without the bias term."
695
  },
696
  "attrs": { "transA": 1, "transB": 1, "activation": "Relu" },
697
  "inputs": {
@@ -707,6 +706,1944 @@
707
  }
708
  },
709
  "outputs": { "Y": { "dtype": "float32", "shape": [64, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
710
  }
711
  ]
712
  }
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "notrans_rowbias_relu",
 
94
  },
95
  {
96
  "name": "notrans_nobias_sigmoid",
97
+ "provenance": { "notes": "No bias, no transposes, and a fused Sigmoid activation." },
98
  "attrs": { "activation": "Sigmoid" },
99
  "inputs": {
100
  "A": {
 
293
  },
294
  {
295
  "name": "beta_zero_relu",
296
+ "provenance": { "notes": "`beta = 0` suppresses the C term exactly." },
297
  "attrs": { "beta": 0, "activation": "Relu" },
298
  "inputs": {
299
  "A": {
 
480
  {
481
  "name": "sgmat_notrans_rowbias_relu",
482
  "provenance": {
483
+ "notes": "Aligned `M >= 32`, `K % 32 = 0`, and `N % 64 = 0` select the subgroup-matrix route. A is stored as [M, K], B as [K, N], and Relu is fused over a rank-1 row bias."
484
  },
485
  "attrs": { "activation": "Relu" },
486
  "inputs": {
 
505
  {
506
  "name": "sgmat_notrans_nobias_tanh",
507
  "provenance": {
508
+ "notes": "Aligned non-transposed operands exercise the no-bias subgroup-matrix store with fused Tanh."
509
  },
510
  "attrs": { "activation": "Tanh" },
511
  "inputs": {
 
525
  {
526
  "name": "notrans_rowbias_tanh_saturating_preactivation",
527
  "provenance": {
528
+ "notes": "A large row bias drives most pre-activations beyond magnitude 44 while the dot products remain small and well conditioned. Unaligned dimensions exercise guarded Tanh saturation on the tiled store path."
529
  },
530
  "attrs": { "activation": "Tanh" },
531
  "inputs": {
 
550
  {
551
  "name": "sgmat_notrans_rowbias_tanh_saturating_preactivation",
552
  "provenance": {
553
+ "notes": "Aligned dimensions and a large row bias exercise guarded Tanh saturation on the subgroup-matrix store path."
554
  },
555
  "attrs": { "activation": "Tanh" },
556
  "inputs": {
 
575
  {
576
  "name": "sgmat_transa_rowbias_tanh",
577
  "provenance": {
578
+ "notes": "Aligned dimensions select the subgroup-matrix route with A stored as [K, M], B as [K, N], a rank-1 row bias, and fused Tanh."
579
  },
580
  "attrs": { "transA": 1, "activation": "Tanh" },
581
  "inputs": {
 
600
  {
601
  "name": "sgmat_transa_nobias_sigmoid",
602
  "provenance": {
603
+ "notes": "Aligned dimensions exercise the transposed-A, no-bias subgroup-matrix store with fused Sigmoid."
604
  },
605
  "attrs": { "transA": 1, "activation": "Sigmoid" },
606
  "inputs": {
 
620
  {
621
  "name": "sgmat_transb_rowbias_sigmoid",
622
  "provenance": {
623
+ "notes": "Aligned dimensions select the subgroup-matrix route with A stored as [M, K], B as [N, K], a rank-1 row bias, and fused Sigmoid."
624
  },
625
  "attrs": { "transB": 1, "activation": "Sigmoid" },
626
  "inputs": {
 
645
  {
646
  "name": "sgmat_transb_nobias_leakyrelu",
647
  "provenance": {
648
+ "notes": "Aligned dimensions exercise the transposed-B, no-bias subgroup-matrix store with fused LeakyRelu."
649
  },
650
  "attrs": { "transB": 1, "activation": "LeakyRelu" },
651
  "inputs": {
 
665
  {
666
  "name": "sgmat_transab_rowbias_leakyrelu",
667
  "provenance": {
668
+ "notes": "Aligned dimensions select the subgroup-matrix route with A stored as [K, M], B as [N, K], a rank-1 row bias, and fused LeakyRelu."
669
  },
670
  "attrs": { "transA": 1, "transB": 1, "activation": "LeakyRelu" },
671
  "inputs": {
 
690
  {
691
  "name": "sgmat_transab_nobias_relu",
692
  "provenance": {
693
+ "notes": "Aligned dimensions exercise the doubly transposed, no-bias subgroup-matrix store with fused Relu."
694
  },
695
  "attrs": { "transA": 1, "transB": 1, "activation": "Relu" },
696
  "inputs": {
 
706
  }
707
  },
708
  "outputs": { "Y": { "dtype": "float32", "shape": [64, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
709
+ },
710
+ {
711
+ "name": "f16_transb_rowbias_relu_m1_sgmat_floor_pathology",
712
+ "provenance": {
713
+ "notes": "A single float16 decode row with aligned K and N remains below the subgroup-matrix row floor and exercises the generic register tile."
714
+ },
715
+ "attrs": { "transB": 1, "activation": "Relu" },
716
+ "inputs": {
717
+ "A": {
718
+ "dtype": "float16",
719
+ "shape": [1, 64],
720
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
721
+ },
722
+ "B": {
723
+ "dtype": "float16",
724
+ "shape": [128, 64],
725
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
726
+ },
727
+ "C": {
728
+ "dtype": "float16",
729
+ "shape": [128],
730
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
731
+ }
732
+ },
733
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
734
+ },
735
+ {
736
+ "name": "f16_transb_rowbias_relu_m2_sgmat_floor_control",
737
+ "provenance": {
738
+ "notes": "Two float16 rows with aligned K and N meet the subgroup-matrix row floor and exercise that route."
739
+ },
740
+ "attrs": { "transB": 1, "activation": "Relu" },
741
+ "inputs": {
742
+ "A": {
743
+ "dtype": "float16",
744
+ "shape": [2, 64],
745
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
746
+ },
747
+ "B": {
748
+ "dtype": "float16",
749
+ "shape": [128, 64],
750
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
751
+ },
752
+ "C": {
753
+ "dtype": "float16",
754
+ "shape": [128],
755
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
756
+ }
757
+ },
758
+ "outputs": { "Y": { "dtype": "float16", "shape": [2, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
759
+ },
760
+ {
761
+ "name": "f16_single_row_nobias_identity",
762
+ "provenance": {
763
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
764
+ },
765
+ "attrs": { "transB": 1 },
766
+ "inputs": {
767
+ "A": {
768
+ "dtype": "float16",
769
+ "shape": [1, 64],
770
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
771
+ },
772
+ "B": {
773
+ "dtype": "float16",
774
+ "shape": [128, 64],
775
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
776
+ }
777
+ },
778
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
779
+ },
780
+ {
781
+ "name": "f16_single_row_nobias_Relu",
782
+ "provenance": {
783
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
784
+ },
785
+ "attrs": { "transB": 1, "activation": "Relu" },
786
+ "inputs": {
787
+ "A": {
788
+ "dtype": "float16",
789
+ "shape": [1, 64],
790
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
791
+ },
792
+ "B": {
793
+ "dtype": "float16",
794
+ "shape": [128, 64],
795
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
796
+ }
797
+ },
798
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
799
+ },
800
+ {
801
+ "name": "f16_single_row_nobias_LeakyRelu",
802
+ "provenance": {
803
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
804
+ },
805
+ "attrs": { "transB": 1, "activation": "LeakyRelu", "activation_alpha": 0.125 },
806
+ "inputs": {
807
+ "A": {
808
+ "dtype": "float16",
809
+ "shape": [1, 64],
810
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
811
+ },
812
+ "B": {
813
+ "dtype": "float16",
814
+ "shape": [128, 64],
815
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
816
+ }
817
+ },
818
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
819
+ },
820
+ {
821
+ "name": "f16_single_row_nobias_Sigmoid",
822
+ "provenance": {
823
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
824
+ },
825
+ "attrs": { "transB": 1, "activation": "Sigmoid" },
826
+ "inputs": {
827
+ "A": {
828
+ "dtype": "float16",
829
+ "shape": [1, 64],
830
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
831
+ },
832
+ "B": {
833
+ "dtype": "float16",
834
+ "shape": [128, 64],
835
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
836
+ }
837
+ },
838
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
839
+ },
840
+ {
841
+ "name": "f16_single_row_nobias_Tanh",
842
+ "provenance": {
843
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
844
+ },
845
+ "attrs": { "transB": 1, "activation": "Tanh" },
846
+ "inputs": {
847
+ "A": {
848
+ "dtype": "float16",
849
+ "shape": [1, 64],
850
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
851
+ },
852
+ "B": {
853
+ "dtype": "float16",
854
+ "shape": [128, 64],
855
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
856
+ }
857
+ },
858
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
859
+ },
860
+ {
861
+ "name": "f16_single_row_nobias_HardSigmoid",
862
+ "provenance": {
863
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
864
+ },
865
+ "attrs": { "transB": 1, "activation": "HardSigmoid", "activation_alpha": 0.15, "activation_beta": 0.4 },
866
+ "inputs": {
867
+ "A": {
868
+ "dtype": "float16",
869
+ "shape": [1, 64],
870
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
871
+ },
872
+ "B": {
873
+ "dtype": "float16",
874
+ "shape": [128, 64],
875
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
876
+ }
877
+ },
878
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
879
+ },
880
+ {
881
+ "name": "f16_single_row_empty_reduction_nobias",
882
+ "provenance": { "notes": "An empty reduction leaves the additive term, or zero without C, before applying Relu." },
883
+ "attrs": { "transB": 1, "activation": "Relu" },
884
+ "inputs": {
885
+ "A": { "dtype": "float16", "shape": [1, 0], "data": { "kind": "constant", "value": 0.0 } },
886
+ "B": { "dtype": "float16", "shape": [128, 0], "data": { "kind": "constant", "value": 0.0 } }
887
+ },
888
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
889
+ },
890
+ {
891
+ "name": "f16_single_row_bias_identity",
892
+ "provenance": {
893
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
894
+ },
895
+ "attrs": { "transB": 1 },
896
+ "inputs": {
897
+ "A": {
898
+ "dtype": "float16",
899
+ "shape": [1, 64],
900
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
901
+ },
902
+ "B": {
903
+ "dtype": "float16",
904
+ "shape": [128, 64],
905
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
906
+ },
907
+ "C": {
908
+ "dtype": "float16",
909
+ "shape": [128],
910
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
911
+ }
912
+ },
913
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
914
+ },
915
+ {
916
+ "name": "f16_single_row_bias_LeakyRelu",
917
+ "provenance": {
918
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
919
+ },
920
+ "attrs": { "transB": 1, "activation": "LeakyRelu", "activation_alpha": 0.125 },
921
+ "inputs": {
922
+ "A": {
923
+ "dtype": "float16",
924
+ "shape": [1, 64],
925
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
926
+ },
927
+ "B": {
928
+ "dtype": "float16",
929
+ "shape": [128, 64],
930
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
931
+ },
932
+ "C": {
933
+ "dtype": "float16",
934
+ "shape": [128],
935
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
936
+ }
937
+ },
938
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
939
+ },
940
+ {
941
+ "name": "f16_single_row_bias_Sigmoid",
942
+ "provenance": {
943
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
944
+ },
945
+ "attrs": { "transB": 1, "activation": "Sigmoid" },
946
+ "inputs": {
947
+ "A": {
948
+ "dtype": "float16",
949
+ "shape": [1, 64],
950
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
951
+ },
952
+ "B": {
953
+ "dtype": "float16",
954
+ "shape": [128, 64],
955
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
956
+ },
957
+ "C": {
958
+ "dtype": "float16",
959
+ "shape": [128],
960
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
961
+ }
962
+ },
963
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
964
+ },
965
+ {
966
+ "name": "f16_single_row_bias_Tanh",
967
+ "provenance": {
968
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
969
+ },
970
+ "attrs": { "transB": 1, "activation": "Tanh" },
971
+ "inputs": {
972
+ "A": {
973
+ "dtype": "float16",
974
+ "shape": [1, 64],
975
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
976
+ },
977
+ "B": {
978
+ "dtype": "float16",
979
+ "shape": [128, 64],
980
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
981
+ },
982
+ "C": {
983
+ "dtype": "float16",
984
+ "shape": [128],
985
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
986
+ }
987
+ },
988
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
989
+ },
990
+ {
991
+ "name": "f16_single_row_bias_HardSigmoid",
992
+ "provenance": {
993
+ "notes": "A single output row with contiguous reduction dimensions in both operands. The activation is applied to the f32 accumulator before the final f16 store."
994
+ },
995
+ "attrs": { "transB": 1, "activation": "HardSigmoid", "activation_alpha": 0.15, "activation_beta": 0.4 },
996
+ "inputs": {
997
+ "A": {
998
+ "dtype": "float16",
999
+ "shape": [1, 64],
1000
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1001
+ },
1002
+ "B": {
1003
+ "dtype": "float16",
1004
+ "shape": [128, 64],
1005
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41 }
1006
+ },
1007
+ "C": {
1008
+ "dtype": "float16",
1009
+ "shape": [128],
1010
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1011
+ }
1012
+ },
1013
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
1014
+ },
1015
+ {
1016
+ "name": "f16_single_row_empty_reduction_bias",
1017
+ "provenance": { "notes": "An empty reduction leaves the additive term, or zero without C, before applying Relu." },
1018
+ "attrs": { "transB": 1, "activation": "Relu" },
1019
+ "inputs": {
1020
+ "A": { "dtype": "float16", "shape": [1, 0], "data": { "kind": "constant", "value": 0.0 } },
1021
+ "B": { "dtype": "float16", "shape": [128, 0], "data": { "kind": "constant", "value": 0.0 } },
1022
+ "C": {
1023
+ "dtype": "float16",
1024
+ "shape": [128],
1025
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1026
+ }
1027
+ },
1028
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 128], "tolerance": 0.006, "relTolerance": 0.006 } }
1029
+ },
1030
+ {
1031
+ "name": "gemv-probe-float32-k64-n9-none-identity-ta0",
1032
+ "provenance": {
1033
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1034
+ },
1035
+ "attrs": {
1036
+ "transA": 0,
1037
+ "transB": 1,
1038
+ "alpha": -0.75,
1039
+ "beta": 0,
1040
+ "activation_alpha": 0.125,
1041
+ "activation_beta": 0.375
1042
+ },
1043
+ "inputs": {
1044
+ "A": {
1045
+ "dtype": "float32",
1046
+ "shape": [1, 64],
1047
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1048
+ },
1049
+ "B": {
1050
+ "dtype": "float32",
1051
+ "shape": [9, 64],
1052
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1053
+ }
1054
+ },
1055
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 9], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1056
+ },
1057
+ {
1058
+ "name": "gemv-probe-float32-k63-n9-scalar-Relu-ta1",
1059
+ "provenance": {
1060
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1061
+ },
1062
+ "attrs": {
1063
+ "transA": 1,
1064
+ "transB": 1,
1065
+ "alpha": -0.75,
1066
+ "beta": 0,
1067
+ "activation": "Relu",
1068
+ "activation_alpha": 0.125,
1069
+ "activation_beta": 0.375
1070
+ },
1071
+ "inputs": {
1072
+ "A": {
1073
+ "dtype": "float32",
1074
+ "shape": [63, 1],
1075
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1076
+ },
1077
+ "B": {
1078
+ "dtype": "float32",
1079
+ "shape": [9, 63],
1080
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1081
+ },
1082
+ "C": {
1083
+ "dtype": "float32",
1084
+ "shape": [],
1085
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1086
+ }
1087
+ },
1088
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 9], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1089
+ },
1090
+ {
1091
+ "name": "gemv-probe-float32-k64-n9-scalar1d-LeakyRelu-ta1",
1092
+ "provenance": {
1093
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1094
+ },
1095
+ "attrs": {
1096
+ "transA": 1,
1097
+ "transB": 1,
1098
+ "alpha": -0.75,
1099
+ "beta": 0,
1100
+ "activation": "LeakyRelu",
1101
+ "activation_alpha": 0.125,
1102
+ "activation_beta": 0.375
1103
+ },
1104
+ "inputs": {
1105
+ "A": {
1106
+ "dtype": "float32",
1107
+ "shape": [64, 1],
1108
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1109
+ },
1110
+ "B": {
1111
+ "dtype": "float32",
1112
+ "shape": [9, 64],
1113
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1114
+ },
1115
+ "C": {
1116
+ "dtype": "float32",
1117
+ "shape": [1],
1118
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1119
+ }
1120
+ },
1121
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 9], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1122
+ },
1123
+ {
1124
+ "name": "gemv-probe-float32-k63-n9-scalar2d-Sigmoid-ta0",
1125
+ "provenance": {
1126
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1127
+ },
1128
+ "attrs": {
1129
+ "transA": 0,
1130
+ "transB": 1,
1131
+ "alpha": -0.75,
1132
+ "beta": 0,
1133
+ "activation": "Sigmoid",
1134
+ "activation_alpha": 0.125,
1135
+ "activation_beta": 0.375
1136
+ },
1137
+ "inputs": {
1138
+ "A": {
1139
+ "dtype": "float32",
1140
+ "shape": [1, 63],
1141
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1142
+ },
1143
+ "B": {
1144
+ "dtype": "float32",
1145
+ "shape": [9, 63],
1146
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1147
+ },
1148
+ "C": {
1149
+ "dtype": "float32",
1150
+ "shape": [1, 1],
1151
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1152
+ }
1153
+ },
1154
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 9], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1155
+ },
1156
+ {
1157
+ "name": "gemv-probe-float32-k64-n9-row-Tanh-ta0",
1158
+ "provenance": {
1159
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1160
+ },
1161
+ "attrs": {
1162
+ "transA": 0,
1163
+ "transB": 1,
1164
+ "alpha": -0.75,
1165
+ "beta": 0,
1166
+ "activation": "Tanh",
1167
+ "activation_alpha": 0.125,
1168
+ "activation_beta": 0.375
1169
+ },
1170
+ "inputs": {
1171
+ "A": {
1172
+ "dtype": "float32",
1173
+ "shape": [1, 64],
1174
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1175
+ },
1176
+ "B": {
1177
+ "dtype": "float32",
1178
+ "shape": [9, 64],
1179
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1180
+ },
1181
+ "C": {
1182
+ "dtype": "float32",
1183
+ "shape": [9],
1184
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1185
+ }
1186
+ },
1187
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 9], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1188
+ },
1189
+ {
1190
+ "name": "gemv-probe-float32-k63-n9-row2d-HardSigmoid-ta1",
1191
+ "provenance": {
1192
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1193
+ },
1194
+ "attrs": {
1195
+ "transA": 1,
1196
+ "transB": 1,
1197
+ "alpha": -0.75,
1198
+ "beta": 0,
1199
+ "activation": "HardSigmoid",
1200
+ "activation_alpha": 0.125,
1201
+ "activation_beta": 0.375
1202
+ },
1203
+ "inputs": {
1204
+ "A": {
1205
+ "dtype": "float32",
1206
+ "shape": [63, 1],
1207
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1208
+ },
1209
+ "B": {
1210
+ "dtype": "float32",
1211
+ "shape": [9, 63],
1212
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1213
+ },
1214
+ "C": {
1215
+ "dtype": "float32",
1216
+ "shape": [1, 9],
1217
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1218
+ }
1219
+ },
1220
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 9], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1221
+ },
1222
+ {
1223
+ "name": "gemv-probe-float32-k64-n12-none-Tanh-ta0",
1224
+ "provenance": {
1225
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1226
+ },
1227
+ "attrs": {
1228
+ "transA": 0,
1229
+ "transB": 1,
1230
+ "alpha": -0.75,
1231
+ "beta": 0.25,
1232
+ "activation": "Tanh",
1233
+ "activation_alpha": 0.125,
1234
+ "activation_beta": 0.375
1235
+ },
1236
+ "inputs": {
1237
+ "A": {
1238
+ "dtype": "float32",
1239
+ "shape": [1, 64],
1240
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1241
+ },
1242
+ "B": {
1243
+ "dtype": "float32",
1244
+ "shape": [12, 64],
1245
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1246
+ }
1247
+ },
1248
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1249
+ },
1250
+ {
1251
+ "name": "gemv-probe-float32-k63-n12-scalar-HardSigmoid-ta1",
1252
+ "provenance": {
1253
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1254
+ },
1255
+ "attrs": {
1256
+ "transA": 1,
1257
+ "transB": 1,
1258
+ "alpha": -0.75,
1259
+ "beta": 0.25,
1260
+ "activation": "HardSigmoid",
1261
+ "activation_alpha": 0.125,
1262
+ "activation_beta": 0.375
1263
+ },
1264
+ "inputs": {
1265
+ "A": {
1266
+ "dtype": "float32",
1267
+ "shape": [63, 1],
1268
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1269
+ },
1270
+ "B": {
1271
+ "dtype": "float32",
1272
+ "shape": [12, 63],
1273
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1274
+ },
1275
+ "C": {
1276
+ "dtype": "float32",
1277
+ "shape": [],
1278
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1279
+ }
1280
+ },
1281
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1282
+ },
1283
+ {
1284
+ "name": "gemv-probe-float32-k64-n12-scalar1d-identity-ta0",
1285
+ "provenance": {
1286
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1287
+ },
1288
+ "attrs": {
1289
+ "transA": 0,
1290
+ "transB": 1,
1291
+ "alpha": -0.75,
1292
+ "beta": 0.25,
1293
+ "activation_alpha": 0.125,
1294
+ "activation_beta": 0.375
1295
+ },
1296
+ "inputs": {
1297
+ "A": {
1298
+ "dtype": "float32",
1299
+ "shape": [1, 64],
1300
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1301
+ },
1302
+ "B": {
1303
+ "dtype": "float32",
1304
+ "shape": [12, 64],
1305
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1306
+ },
1307
+ "C": {
1308
+ "dtype": "float32",
1309
+ "shape": [1],
1310
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1311
+ }
1312
+ },
1313
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1314
+ },
1315
+ {
1316
+ "name": "gemv-probe-float32-k63-n12-scalar2d-Relu-ta1",
1317
+ "provenance": {
1318
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1319
+ },
1320
+ "attrs": {
1321
+ "transA": 1,
1322
+ "transB": 1,
1323
+ "alpha": -0.75,
1324
+ "beta": 0.25,
1325
+ "activation": "Relu",
1326
+ "activation_alpha": 0.125,
1327
+ "activation_beta": 0.375
1328
+ },
1329
+ "inputs": {
1330
+ "A": {
1331
+ "dtype": "float32",
1332
+ "shape": [63, 1],
1333
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1334
+ },
1335
+ "B": {
1336
+ "dtype": "float32",
1337
+ "shape": [12, 63],
1338
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1339
+ },
1340
+ "C": {
1341
+ "dtype": "float32",
1342
+ "shape": [1, 1],
1343
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1344
+ }
1345
+ },
1346
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1347
+ },
1348
+ {
1349
+ "name": "gemv-probe-float32-k64-n12-row-LeakyRelu-ta1",
1350
+ "provenance": {
1351
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1352
+ },
1353
+ "attrs": {
1354
+ "transA": 1,
1355
+ "transB": 1,
1356
+ "alpha": -0.75,
1357
+ "beta": 0.25,
1358
+ "activation": "LeakyRelu",
1359
+ "activation_alpha": 0.125,
1360
+ "activation_beta": 0.375
1361
+ },
1362
+ "inputs": {
1363
+ "A": {
1364
+ "dtype": "float32",
1365
+ "shape": [64, 1],
1366
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1367
+ },
1368
+ "B": {
1369
+ "dtype": "float32",
1370
+ "shape": [12, 64],
1371
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1372
+ },
1373
+ "C": {
1374
+ "dtype": "float32",
1375
+ "shape": [12],
1376
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1377
+ }
1378
+ },
1379
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1380
+ },
1381
+ {
1382
+ "name": "gemv-probe-float32-k63-n12-row2d-Sigmoid-ta0",
1383
+ "provenance": {
1384
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1385
+ },
1386
+ "attrs": {
1387
+ "transA": 0,
1388
+ "transB": 1,
1389
+ "alpha": -0.75,
1390
+ "beta": 0.25,
1391
+ "activation": "Sigmoid",
1392
+ "activation_alpha": 0.125,
1393
+ "activation_beta": 0.375
1394
+ },
1395
+ "inputs": {
1396
+ "A": {
1397
+ "dtype": "float32",
1398
+ "shape": [1, 63],
1399
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1400
+ },
1401
+ "B": {
1402
+ "dtype": "float32",
1403
+ "shape": [12, 63],
1404
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1405
+ },
1406
+ "C": {
1407
+ "dtype": "float32",
1408
+ "shape": [1, 12],
1409
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.3, "cosStep": 0.14 }
1410
+ }
1411
+ },
1412
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1413
+ },
1414
+ {
1415
+ "name": "gemv-probe-float16-k63-n9-none-HardSigmoid-ta0",
1416
+ "provenance": {
1417
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1418
+ },
1419
+ "attrs": {
1420
+ "transA": 0,
1421
+ "transB": 1,
1422
+ "alpha": -0.75,
1423
+ "beta": 0,
1424
+ "activation": "HardSigmoid",
1425
+ "activation_alpha": 0.125,
1426
+ "activation_beta": 0.375
1427
+ },
1428
+ "inputs": {
1429
+ "A": {
1430
+ "dtype": "float16",
1431
+ "shape": [1, 63],
1432
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1433
+ },
1434
+ "B": {
1435
+ "dtype": "float16",
1436
+ "shape": [9, 63],
1437
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1438
+ }
1439
+ },
1440
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 9], "tolerance": 0.006, "relTolerance": 0.006 } }
1441
+ },
1442
+ {
1443
+ "name": "gemv-probe-float16-k64-n9-scalar-identity-ta1",
1444
+ "provenance": {
1445
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1446
+ },
1447
+ "attrs": {
1448
+ "transA": 1,
1449
+ "transB": 1,
1450
+ "alpha": -0.75,
1451
+ "beta": 0.25,
1452
+ "activation_alpha": 0.125,
1453
+ "activation_beta": 0.375
1454
+ },
1455
+ "inputs": {
1456
+ "A": {
1457
+ "dtype": "float16",
1458
+ "shape": [64, 1],
1459
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1460
+ },
1461
+ "B": {
1462
+ "dtype": "float16",
1463
+ "shape": [9, 64],
1464
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1465
+ },
1466
+ "C": {
1467
+ "dtype": "float16",
1468
+ "shape": [],
1469
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1470
+ }
1471
+ },
1472
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 9], "tolerance": 0.006, "relTolerance": 0.006 } }
1473
+ },
1474
+ {
1475
+ "name": "gemv-probe-float16-k63-n9-scalar1d-Relu-ta0",
1476
+ "provenance": {
1477
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1478
+ },
1479
+ "attrs": {
1480
+ "transA": 0,
1481
+ "transB": 1,
1482
+ "alpha": -0.75,
1483
+ "beta": 0.25,
1484
+ "activation": "Relu",
1485
+ "activation_alpha": 0.125,
1486
+ "activation_beta": 0.375
1487
+ },
1488
+ "inputs": {
1489
+ "A": {
1490
+ "dtype": "float16",
1491
+ "shape": [1, 63],
1492
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1493
+ },
1494
+ "B": {
1495
+ "dtype": "float16",
1496
+ "shape": [9, 63],
1497
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1498
+ },
1499
+ "C": {
1500
+ "dtype": "float16",
1501
+ "shape": [1],
1502
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1503
+ }
1504
+ },
1505
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 9], "tolerance": 0.006, "relTolerance": 0.006 } }
1506
+ },
1507
+ {
1508
+ "name": "gemv-probe-float16-k64-n9-scalar2d-LeakyRelu-ta0",
1509
+ "provenance": {
1510
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1511
+ },
1512
+ "attrs": {
1513
+ "transA": 0,
1514
+ "transB": 1,
1515
+ "alpha": -0.75,
1516
+ "beta": 0.25,
1517
+ "activation": "LeakyRelu",
1518
+ "activation_alpha": 0.125,
1519
+ "activation_beta": 0.375
1520
+ },
1521
+ "inputs": {
1522
+ "A": {
1523
+ "dtype": "float16",
1524
+ "shape": [1, 64],
1525
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1526
+ },
1527
+ "B": {
1528
+ "dtype": "float16",
1529
+ "shape": [9, 64],
1530
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1531
+ },
1532
+ "C": {
1533
+ "dtype": "float16",
1534
+ "shape": [1, 1],
1535
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1536
+ }
1537
+ },
1538
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 9], "tolerance": 0.006, "relTolerance": 0.006 } }
1539
+ },
1540
+ {
1541
+ "name": "gemv-probe-float16-k63-n9-row-Sigmoid-ta1",
1542
+ "provenance": {
1543
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1544
+ },
1545
+ "attrs": {
1546
+ "transA": 1,
1547
+ "transB": 1,
1548
+ "alpha": -0.75,
1549
+ "beta": 0.25,
1550
+ "activation": "Sigmoid",
1551
+ "activation_alpha": 0.125,
1552
+ "activation_beta": 0.375
1553
+ },
1554
+ "inputs": {
1555
+ "A": {
1556
+ "dtype": "float16",
1557
+ "shape": [63, 1],
1558
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1559
+ },
1560
+ "B": {
1561
+ "dtype": "float16",
1562
+ "shape": [9, 63],
1563
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1564
+ },
1565
+ "C": {
1566
+ "dtype": "float16",
1567
+ "shape": [9],
1568
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1569
+ }
1570
+ },
1571
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 9], "tolerance": 0.006, "relTolerance": 0.006 } }
1572
+ },
1573
+ {
1574
+ "name": "gemv-probe-float16-k64-n9-row2d-Tanh-ta1",
1575
+ "provenance": {
1576
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1577
+ },
1578
+ "attrs": {
1579
+ "transA": 1,
1580
+ "transB": 1,
1581
+ "alpha": -0.75,
1582
+ "beta": 0.25,
1583
+ "activation": "Tanh",
1584
+ "activation_alpha": 0.125,
1585
+ "activation_beta": 0.375
1586
+ },
1587
+ "inputs": {
1588
+ "A": {
1589
+ "dtype": "float16",
1590
+ "shape": [64, 1],
1591
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1592
+ },
1593
+ "B": {
1594
+ "dtype": "float16",
1595
+ "shape": [9, 64],
1596
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1597
+ },
1598
+ "C": {
1599
+ "dtype": "float16",
1600
+ "shape": [1, 9],
1601
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1602
+ }
1603
+ },
1604
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 9], "tolerance": 0.006, "relTolerance": 0.006 } }
1605
+ },
1606
+ {
1607
+ "name": "gemv-probe-float16-k63-n12-none-Sigmoid-ta1",
1608
+ "provenance": {
1609
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1610
+ },
1611
+ "attrs": {
1612
+ "transA": 1,
1613
+ "transB": 1,
1614
+ "alpha": -0.75,
1615
+ "beta": 0.25,
1616
+ "activation": "Sigmoid",
1617
+ "activation_alpha": 0.125,
1618
+ "activation_beta": 0.375
1619
+ },
1620
+ "inputs": {
1621
+ "A": {
1622
+ "dtype": "float16",
1623
+ "shape": [63, 1],
1624
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1625
+ },
1626
+ "B": {
1627
+ "dtype": "float16",
1628
+ "shape": [12, 63],
1629
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1630
+ }
1631
+ },
1632
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1633
+ },
1634
+ {
1635
+ "name": "gemv-probe-float16-k64-n12-scalar-Tanh-ta1",
1636
+ "provenance": {
1637
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1638
+ },
1639
+ "attrs": {
1640
+ "transA": 1,
1641
+ "transB": 1,
1642
+ "alpha": -0.75,
1643
+ "beta": 0.25,
1644
+ "activation": "Tanh",
1645
+ "activation_alpha": 0.125,
1646
+ "activation_beta": 0.375
1647
+ },
1648
+ "inputs": {
1649
+ "A": {
1650
+ "dtype": "float16",
1651
+ "shape": [64, 1],
1652
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1653
+ },
1654
+ "B": {
1655
+ "dtype": "float16",
1656
+ "shape": [12, 64],
1657
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1658
+ },
1659
+ "C": {
1660
+ "dtype": "float16",
1661
+ "shape": [],
1662
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1663
+ }
1664
+ },
1665
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1666
+ },
1667
+ {
1668
+ "name": "gemv-probe-float16-k63-n12-scalar1d-HardSigmoid-ta0",
1669
+ "provenance": {
1670
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1671
+ },
1672
+ "attrs": {
1673
+ "transA": 0,
1674
+ "transB": 1,
1675
+ "alpha": -0.75,
1676
+ "beta": 0.25,
1677
+ "activation": "HardSigmoid",
1678
+ "activation_alpha": 0.125,
1679
+ "activation_beta": 0.375
1680
+ },
1681
+ "inputs": {
1682
+ "A": {
1683
+ "dtype": "float16",
1684
+ "shape": [1, 63],
1685
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1686
+ },
1687
+ "B": {
1688
+ "dtype": "float16",
1689
+ "shape": [12, 63],
1690
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1691
+ },
1692
+ "C": {
1693
+ "dtype": "float16",
1694
+ "shape": [1],
1695
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1696
+ }
1697
+ },
1698
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1699
+ },
1700
+ {
1701
+ "name": "gemv-probe-float16-k64-n12-scalar2d-identity-ta1",
1702
+ "provenance": {
1703
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1704
+ },
1705
+ "attrs": {
1706
+ "transA": 1,
1707
+ "transB": 1,
1708
+ "alpha": -0.75,
1709
+ "beta": 0,
1710
+ "activation_alpha": 0.125,
1711
+ "activation_beta": 0.375
1712
+ },
1713
+ "inputs": {
1714
+ "A": {
1715
+ "dtype": "float16",
1716
+ "shape": [64, 1],
1717
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1718
+ },
1719
+ "B": {
1720
+ "dtype": "float16",
1721
+ "shape": [12, 64],
1722
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1723
+ },
1724
+ "C": {
1725
+ "dtype": "float16",
1726
+ "shape": [1, 1],
1727
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1728
+ }
1729
+ },
1730
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1731
+ },
1732
+ {
1733
+ "name": "gemv-probe-float16-k63-n12-row-Relu-ta0",
1734
+ "provenance": {
1735
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1736
+ },
1737
+ "attrs": {
1738
+ "transA": 0,
1739
+ "transB": 1,
1740
+ "alpha": -0.75,
1741
+ "beta": 0,
1742
+ "activation": "Relu",
1743
+ "activation_alpha": 0.125,
1744
+ "activation_beta": 0.375
1745
+ },
1746
+ "inputs": {
1747
+ "A": {
1748
+ "dtype": "float16",
1749
+ "shape": [1, 63],
1750
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1751
+ },
1752
+ "B": {
1753
+ "dtype": "float16",
1754
+ "shape": [12, 63],
1755
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1756
+ },
1757
+ "C": {
1758
+ "dtype": "float16",
1759
+ "shape": [12],
1760
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1761
+ }
1762
+ },
1763
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1764
+ },
1765
+ {
1766
+ "name": "gemv-probe-float16-k64-n12-row2d-LeakyRelu-ta0",
1767
+ "provenance": {
1768
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1769
+ },
1770
+ "attrs": {
1771
+ "transA": 0,
1772
+ "transB": 1,
1773
+ "alpha": -0.75,
1774
+ "beta": 0,
1775
+ "activation": "LeakyRelu",
1776
+ "activation_alpha": 0.125,
1777
+ "activation_beta": 0.375
1778
+ },
1779
+ "inputs": {
1780
+ "A": {
1781
+ "dtype": "float16",
1782
+ "shape": [1, 64],
1783
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
1784
+ },
1785
+ "B": {
1786
+ "dtype": "float16",
1787
+ "shape": [12, 64],
1788
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
1789
+ },
1790
+ "C": {
1791
+ "dtype": "float16",
1792
+ "shape": [1, 12],
1793
+ "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.47, "cosStep": 0.31 }
1794
+ }
1795
+ },
1796
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1797
+ },
1798
+ {
1799
+ "name": "gemv-float32-alpha-zero-nonfinite-input",
1800
+ "provenance": {
1801
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1802
+ },
1803
+ "attrs": { "transB": 1, "alpha": 0, "beta": 1, "activation": "Relu" },
1804
+ "inputs": {
1805
+ "A": { "dtype": "float32", "shape": [1, 8], "data": { "kind": "constant", "value": 1e+39 } },
1806
+ "B": { "dtype": "float32", "shape": [12, 8], "data": { "kind": "constant", "value": 1.0 } },
1807
+ "C": { "dtype": "float32", "shape": [12], "data": { "kind": "constant", "value": 2.0 } }
1808
+ },
1809
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1810
+ },
1811
+ {
1812
+ "name": "gemv-float32-beta-zero-nonfinite-input",
1813
+ "provenance": {
1814
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1815
+ },
1816
+ "attrs": { "transB": 1, "alpha": 1, "beta": 0, "activation": "Relu" },
1817
+ "inputs": {
1818
+ "A": { "dtype": "float32", "shape": [1, 8], "data": { "kind": "constant", "value": 1.0 } },
1819
+ "B": { "dtype": "float32", "shape": [12, 8], "data": { "kind": "constant", "value": 1.0 } },
1820
+ "C": {
1821
+ "dtype": "float32",
1822
+ "shape": [12],
1823
+ "data": { "kind": "constant", "value": 2.0, "nanStart": 0.0, "nanCount": 12.0 }
1824
+ }
1825
+ },
1826
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 12], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1827
+ },
1828
+ {
1829
+ "name": "gemv-float16-alpha-zero-nonfinite-input",
1830
+ "provenance": {
1831
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1832
+ },
1833
+ "attrs": { "transB": 1, "alpha": 0, "beta": 1, "activation": "Relu" },
1834
+ "inputs": {
1835
+ "A": { "dtype": "float16", "shape": [1, 8], "data": { "kind": "constant", "value": 1e+39 } },
1836
+ "B": { "dtype": "float16", "shape": [12, 8], "data": { "kind": "constant", "value": 1.0 } },
1837
+ "C": { "dtype": "float16", "shape": [12], "data": { "kind": "constant", "value": 2.0 } }
1838
+ },
1839
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1840
+ },
1841
+ {
1842
+ "name": "gemv-float16-beta-zero-nonfinite-input",
1843
+ "provenance": {
1844
+ "notes": "Single-row contiguous-weight reduction with an odd reduction or output tail, optional broadcast bias, scaling, and fused activation."
1845
+ },
1846
+ "attrs": { "transB": 1, "alpha": 1, "beta": 0, "activation": "Relu" },
1847
+ "inputs": {
1848
+ "A": { "dtype": "float16", "shape": [1, 8], "data": { "kind": "constant", "value": 1.0 } },
1849
+ "B": { "dtype": "float16", "shape": [12, 8], "data": { "kind": "constant", "value": 1.0 } },
1850
+ "C": {
1851
+ "dtype": "float16",
1852
+ "shape": [12],
1853
+ "data": { "kind": "constant", "value": 2.0, "nanStart": 0.0, "nanCount": 12.0 }
1854
+ }
1855
+ },
1856
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 12], "tolerance": 0.006, "relTolerance": 0.006 } }
1857
+ },
1858
+ {
1859
+ "name": "gemv-boundary-float32-k1-n1",
1860
+ "provenance": {
1861
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
1862
+ },
1863
+ "attrs": { "transB": 1, "activation": "Relu" },
1864
+ "inputs": {
1865
+ "A": {
1866
+ "dtype": "float32",
1867
+ "shape": [1, 1],
1868
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1869
+ },
1870
+ "B": {
1871
+ "dtype": "float32",
1872
+ "shape": [1, 1],
1873
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1874
+ }
1875
+ },
1876
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 1], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1877
+ },
1878
+ {
1879
+ "name": "gemv-boundary-float32-k7-n5",
1880
+ "provenance": {
1881
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
1882
+ },
1883
+ "attrs": { "transB": 1, "activation": "Relu" },
1884
+ "inputs": {
1885
+ "A": {
1886
+ "dtype": "float32",
1887
+ "shape": [1, 7],
1888
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1889
+ },
1890
+ "B": {
1891
+ "dtype": "float32",
1892
+ "shape": [5, 7],
1893
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1894
+ }
1895
+ },
1896
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 5], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1897
+ },
1898
+ {
1899
+ "name": "gemv-boundary-float32-k31-n33",
1900
+ "provenance": {
1901
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
1902
+ },
1903
+ "attrs": { "transB": 1, "activation": "Relu" },
1904
+ "inputs": {
1905
+ "A": {
1906
+ "dtype": "float32",
1907
+ "shape": [1, 31],
1908
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1909
+ },
1910
+ "B": {
1911
+ "dtype": "float32",
1912
+ "shape": [33, 31],
1913
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1914
+ }
1915
+ },
1916
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 33], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1917
+ },
1918
+ {
1919
+ "name": "gemv-boundary-float32-k32-n32",
1920
+ "provenance": {
1921
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
1922
+ },
1923
+ "attrs": { "transB": 1, "activation": "Relu" },
1924
+ "inputs": {
1925
+ "A": {
1926
+ "dtype": "float32",
1927
+ "shape": [1, 32],
1928
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1929
+ },
1930
+ "B": {
1931
+ "dtype": "float32",
1932
+ "shape": [32, 32],
1933
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1934
+ }
1935
+ },
1936
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 32], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1937
+ },
1938
+ {
1939
+ "name": "gemv-boundary-float32-k33-n33",
1940
+ "provenance": {
1941
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
1942
+ },
1943
+ "attrs": { "transB": 1, "activation": "Relu" },
1944
+ "inputs": {
1945
+ "A": {
1946
+ "dtype": "float32",
1947
+ "shape": [1, 33],
1948
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1949
+ },
1950
+ "B": {
1951
+ "dtype": "float32",
1952
+ "shape": [33, 33],
1953
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1954
+ }
1955
+ },
1956
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 33], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1957
+ },
1958
+ {
1959
+ "name": "gemv-boundary-float32-k129-n16",
1960
+ "provenance": {
1961
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
1962
+ },
1963
+ "attrs": { "transB": 1, "activation": "Relu" },
1964
+ "inputs": {
1965
+ "A": {
1966
+ "dtype": "float32",
1967
+ "shape": [1, 129],
1968
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1969
+ },
1970
+ "B": {
1971
+ "dtype": "float32",
1972
+ "shape": [16, 129],
1973
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1974
+ }
1975
+ },
1976
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 16], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1977
+ },
1978
+ {
1979
+ "name": "gemv-boundary-float32-k1023-n257",
1980
+ "provenance": {
1981
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
1982
+ },
1983
+ "attrs": { "transB": 1, "activation": "Relu" },
1984
+ "inputs": {
1985
+ "A": {
1986
+ "dtype": "float32",
1987
+ "shape": [1, 1023],
1988
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
1989
+ },
1990
+ "B": {
1991
+ "dtype": "float32",
1992
+ "shape": [257, 1023],
1993
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
1994
+ }
1995
+ },
1996
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 257], "tolerance": 0.00002, "relTolerance": 0.0002 } }
1997
+ },
1998
+ {
1999
+ "name": "gemv-boundary-float32-k4096-n64",
2000
+ "provenance": {
2001
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2002
+ },
2003
+ "attrs": { "transB": 1, "activation": "Relu" },
2004
+ "inputs": {
2005
+ "A": {
2006
+ "dtype": "float32",
2007
+ "shape": [1, 4096],
2008
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
2009
+ },
2010
+ "B": {
2011
+ "dtype": "float32",
2012
+ "shape": [64, 4096],
2013
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
2014
+ }
2015
+ },
2016
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 64], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2017
+ },
2018
+ {
2019
+ "name": "gemv-boundary-float32-k1-n65537",
2020
+ "provenance": {
2021
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2022
+ },
2023
+ "attrs": { "transB": 1, "activation": "Relu" },
2024
+ "inputs": {
2025
+ "A": {
2026
+ "dtype": "float32",
2027
+ "shape": [1, 1],
2028
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
2029
+ },
2030
+ "B": {
2031
+ "dtype": "float32",
2032
+ "shape": [65537, 1],
2033
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
2034
+ }
2035
+ },
2036
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 65537], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2037
+ },
2038
+ {
2039
+ "name": "gemv-boundary-float32-k1-n262148",
2040
+ "provenance": {
2041
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2042
+ },
2043
+ "attrs": { "transB": 1, "activation": "Relu" },
2044
+ "inputs": {
2045
+ "A": {
2046
+ "dtype": "float32",
2047
+ "shape": [1, 1],
2048
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
2049
+ },
2050
+ "B": {
2051
+ "dtype": "float32",
2052
+ "shape": [262148, 1],
2053
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
2054
+ }
2055
+ },
2056
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 262148], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2057
+ },
2058
+ {
2059
+ "name": "gemv-boundary-float16-k1-n1",
2060
+ "provenance": {
2061
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2062
+ },
2063
+ "attrs": { "transB": 1, "activation": "Relu" },
2064
+ "inputs": {
2065
+ "A": {
2066
+ "dtype": "float16",
2067
+ "shape": [1, 1],
2068
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2069
+ },
2070
+ "B": {
2071
+ "dtype": "float16",
2072
+ "shape": [1, 1],
2073
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2074
+ }
2075
+ },
2076
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 1], "tolerance": 0.006, "relTolerance": 0.006 } }
2077
+ },
2078
+ {
2079
+ "name": "gemv-boundary-float16-k7-n5",
2080
+ "provenance": {
2081
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2082
+ },
2083
+ "attrs": { "transB": 1, "activation": "Relu" },
2084
+ "inputs": {
2085
+ "A": {
2086
+ "dtype": "float16",
2087
+ "shape": [1, 7],
2088
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2089
+ },
2090
+ "B": {
2091
+ "dtype": "float16",
2092
+ "shape": [5, 7],
2093
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2094
+ }
2095
+ },
2096
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 5], "tolerance": 0.006, "relTolerance": 0.006 } }
2097
+ },
2098
+ {
2099
+ "name": "gemv-boundary-float16-k31-n33",
2100
+ "provenance": {
2101
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2102
+ },
2103
+ "attrs": { "transB": 1, "activation": "Relu" },
2104
+ "inputs": {
2105
+ "A": {
2106
+ "dtype": "float16",
2107
+ "shape": [1, 31],
2108
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2109
+ },
2110
+ "B": {
2111
+ "dtype": "float16",
2112
+ "shape": [33, 31],
2113
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2114
+ }
2115
+ },
2116
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 33], "tolerance": 0.006, "relTolerance": 0.006 } }
2117
+ },
2118
+ {
2119
+ "name": "gemv-boundary-float16-k32-n32",
2120
+ "provenance": {
2121
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2122
+ },
2123
+ "attrs": { "transB": 1, "activation": "Relu" },
2124
+ "inputs": {
2125
+ "A": {
2126
+ "dtype": "float16",
2127
+ "shape": [1, 32],
2128
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2129
+ },
2130
+ "B": {
2131
+ "dtype": "float16",
2132
+ "shape": [32, 32],
2133
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2134
+ }
2135
+ },
2136
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 32], "tolerance": 0.006, "relTolerance": 0.006 } }
2137
+ },
2138
+ {
2139
+ "name": "gemv-boundary-float16-k33-n33",
2140
+ "provenance": {
2141
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2142
+ },
2143
+ "attrs": { "transB": 1, "activation": "Relu" },
2144
+ "inputs": {
2145
+ "A": {
2146
+ "dtype": "float16",
2147
+ "shape": [1, 33],
2148
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2149
+ },
2150
+ "B": {
2151
+ "dtype": "float16",
2152
+ "shape": [33, 33],
2153
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2154
+ }
2155
+ },
2156
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 33], "tolerance": 0.006, "relTolerance": 0.006 } }
2157
+ },
2158
+ {
2159
+ "name": "gemv-boundary-float16-k129-n16",
2160
+ "provenance": {
2161
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2162
+ },
2163
+ "attrs": { "transB": 1, "activation": "Relu" },
2164
+ "inputs": {
2165
+ "A": {
2166
+ "dtype": "float16",
2167
+ "shape": [1, 129],
2168
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2169
+ },
2170
+ "B": {
2171
+ "dtype": "float16",
2172
+ "shape": [16, 129],
2173
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2174
+ }
2175
+ },
2176
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 16], "tolerance": 0.006, "relTolerance": 0.006 } }
2177
+ },
2178
+ {
2179
+ "name": "gemv-boundary-float16-k1023-n257",
2180
+ "provenance": {
2181
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2182
+ },
2183
+ "attrs": { "transB": 1, "activation": "Relu" },
2184
+ "inputs": {
2185
+ "A": {
2186
+ "dtype": "float16",
2187
+ "shape": [1, 1023],
2188
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2189
+ },
2190
+ "B": {
2191
+ "dtype": "float16",
2192
+ "shape": [257, 1023],
2193
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2194
+ }
2195
+ },
2196
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 257], "tolerance": 0.006, "relTolerance": 0.006 } }
2197
+ },
2198
+ {
2199
+ "name": "gemv-boundary-float16-k4096-n64",
2200
+ "provenance": {
2201
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2202
+ },
2203
+ "attrs": { "transB": 1, "activation": "Relu" },
2204
+ "inputs": {
2205
+ "A": {
2206
+ "dtype": "float16",
2207
+ "shape": [1, 4096],
2208
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2209
+ },
2210
+ "B": {
2211
+ "dtype": "float16",
2212
+ "shape": [64, 4096],
2213
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2214
+ }
2215
+ },
2216
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 64], "tolerance": 0.006, "relTolerance": 0.006 } }
2217
+ },
2218
+ {
2219
+ "name": "gemv-boundary-float16-k1-n65537",
2220
+ "provenance": {
2221
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2222
+ },
2223
+ "attrs": { "transB": 1, "activation": "Relu" },
2224
+ "inputs": {
2225
+ "A": {
2226
+ "dtype": "float16",
2227
+ "shape": [1, 1],
2228
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2229
+ },
2230
+ "B": {
2231
+ "dtype": "float16",
2232
+ "shape": [65537, 1],
2233
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2234
+ }
2235
+ },
2236
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 65537], "tolerance": 0.006, "relTolerance": 0.006 } }
2237
+ },
2238
+ {
2239
+ "name": "gemv-boundary-float16-k1-n262148",
2240
+ "provenance": {
2241
+ "notes": "Single-row contiguous-weight projection covers reduction-tree widths, scalar column tails and folded output-column dispatch."
2242
+ },
2243
+ "attrs": { "transB": 1, "activation": "Relu" },
2244
+ "inputs": {
2245
+ "A": {
2246
+ "dtype": "float16",
2247
+ "shape": [1, 1],
2248
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2249
+ },
2250
+ "B": {
2251
+ "dtype": "float16",
2252
+ "shape": [262148, 1],
2253
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2254
+ }
2255
+ },
2256
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 262148], "tolerance": 0.006, "relTolerance": 0.006 } }
2257
+ },
2258
+ {
2259
+ "name": "gemv-empty-output-float32-m0-n8",
2260
+ "provenance": { "notes": "Empty output with a contiguous stored weight reduction." },
2261
+ "attrs": { "transB": 1 },
2262
+ "inputs": {
2263
+ "A": {
2264
+ "dtype": "float32",
2265
+ "shape": [0, 1],
2266
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
2267
+ },
2268
+ "B": {
2269
+ "dtype": "float32",
2270
+ "shape": [8, 1],
2271
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
2272
+ }
2273
+ },
2274
+ "outputs": { "Y": { "dtype": "float32", "shape": [0, 8], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2275
+ },
2276
+ {
2277
+ "name": "gemv-empty-output-float32-m1-n0",
2278
+ "provenance": { "notes": "Empty output with a contiguous stored weight reduction." },
2279
+ "attrs": { "transB": 1 },
2280
+ "inputs": {
2281
+ "A": {
2282
+ "dtype": "float32",
2283
+ "shape": [1, 1],
2284
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.18000000000000002, "cosStep": 0.32 }
2285
+ },
2286
+ "B": {
2287
+ "dtype": "float32",
2288
+ "shape": [0, 1],
2289
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.12, "cosStep": 0.24000000000000002 }
2290
+ }
2291
+ },
2292
+ "outputs": { "Y": { "dtype": "float32", "shape": [1, 0], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2293
+ },
2294
+ {
2295
+ "name": "gemv-empty-output-float16-m0-n8",
2296
+ "provenance": { "notes": "Empty output with a contiguous stored weight reduction." },
2297
+ "attrs": { "transB": 1 },
2298
+ "inputs": {
2299
+ "A": {
2300
+ "dtype": "float16",
2301
+ "shape": [0, 1],
2302
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2303
+ },
2304
+ "B": {
2305
+ "dtype": "float16",
2306
+ "shape": [8, 1],
2307
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2308
+ }
2309
+ },
2310
+ "outputs": { "Y": { "dtype": "float16", "shape": [0, 8], "tolerance": 0.006, "relTolerance": 0.006 } }
2311
+ },
2312
+ {
2313
+ "name": "gemv-empty-output-float16-m1-n0",
2314
+ "provenance": { "notes": "Empty output with a contiguous stored weight reduction." },
2315
+ "attrs": { "transB": 1 },
2316
+ "inputs": {
2317
+ "A": {
2318
+ "dtype": "float16",
2319
+ "shape": [1, 1],
2320
+ "data": { "kind": "fillFloat32", "scale": 0.9, "sinStep": 0.35, "cosStep": 0.49 }
2321
+ },
2322
+ "B": {
2323
+ "dtype": "float16",
2324
+ "shape": [0, 1],
2325
+ "data": { "kind": "fillFloat32", "scale": 0.6, "sinStep": 0.29, "cosStep": 0.41000000000000003 }
2326
+ }
2327
+ },
2328
+ "outputs": { "Y": { "dtype": "float16", "shape": [1, 0], "tolerance": 0.006, "relTolerance": 0.006 } }
2329
+ },
2330
+ {
2331
+ "name": "direct_a_m31_k96_n128_tb1",
2332
+ "attrs": { "transB": 1, "activation": "Relu" },
2333
+ "provenance": {
2334
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2335
+ },
2336
+ "inputs": {
2337
+ "A": {
2338
+ "dtype": "float32",
2339
+ "shape": [31, 96],
2340
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2341
+ },
2342
+ "B": {
2343
+ "dtype": "float32",
2344
+ "shape": [128, 96],
2345
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2346
+ },
2347
+ "C": {
2348
+ "dtype": "float32",
2349
+ "shape": [128],
2350
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2351
+ }
2352
+ },
2353
+ "outputs": { "Y": { "dtype": "float32", "shape": [31, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2354
+ },
2355
+ {
2356
+ "name": "direct_a_m32_k96_n128_tb1",
2357
+ "attrs": { "transB": 1, "activation": "Relu" },
2358
+ "provenance": {
2359
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2360
+ },
2361
+ "inputs": {
2362
+ "A": {
2363
+ "dtype": "float32",
2364
+ "shape": [32, 96],
2365
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2366
+ },
2367
+ "B": {
2368
+ "dtype": "float32",
2369
+ "shape": [128, 96],
2370
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2371
+ },
2372
+ "C": {
2373
+ "dtype": "float32",
2374
+ "shape": [128],
2375
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2376
+ }
2377
+ },
2378
+ "outputs": { "Y": { "dtype": "float32", "shape": [32, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2379
+ },
2380
+ {
2381
+ "name": "direct_a_m33_k96_n128_tb1",
2382
+ "attrs": { "transB": 1, "activation": "Relu" },
2383
+ "provenance": {
2384
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2385
+ },
2386
+ "inputs": {
2387
+ "A": {
2388
+ "dtype": "float32",
2389
+ "shape": [33, 96],
2390
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2391
+ },
2392
+ "B": {
2393
+ "dtype": "float32",
2394
+ "shape": [128, 96],
2395
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2396
+ },
2397
+ "C": {
2398
+ "dtype": "float32",
2399
+ "shape": [128],
2400
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2401
+ }
2402
+ },
2403
+ "outputs": { "Y": { "dtype": "float32", "shape": [33, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2404
+ },
2405
+ {
2406
+ "name": "direct_a_m63_k96_n128_tb1",
2407
+ "attrs": { "transB": 1, "activation": "Relu" },
2408
+ "provenance": {
2409
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2410
+ },
2411
+ "inputs": {
2412
+ "A": {
2413
+ "dtype": "float32",
2414
+ "shape": [63, 96],
2415
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2416
+ },
2417
+ "B": {
2418
+ "dtype": "float32",
2419
+ "shape": [128, 96],
2420
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2421
+ },
2422
+ "C": {
2423
+ "dtype": "float32",
2424
+ "shape": [128],
2425
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2426
+ }
2427
+ },
2428
+ "outputs": { "Y": { "dtype": "float32", "shape": [63, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2429
+ },
2430
+ {
2431
+ "name": "direct_a_m64_k96_n128_tb1",
2432
+ "attrs": { "transB": 1, "activation": "Relu" },
2433
+ "provenance": {
2434
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2435
+ },
2436
+ "inputs": {
2437
+ "A": {
2438
+ "dtype": "float32",
2439
+ "shape": [64, 96],
2440
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2441
+ },
2442
+ "B": {
2443
+ "dtype": "float32",
2444
+ "shape": [128, 96],
2445
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2446
+ },
2447
+ "C": {
2448
+ "dtype": "float32",
2449
+ "shape": [128],
2450
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2451
+ }
2452
+ },
2453
+ "outputs": { "Y": { "dtype": "float32", "shape": [64, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2454
+ },
2455
+ {
2456
+ "name": "direct_a_m65_k96_n128_tb1",
2457
+ "attrs": { "transB": 1, "activation": "Relu" },
2458
+ "provenance": {
2459
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2460
+ },
2461
+ "inputs": {
2462
+ "A": {
2463
+ "dtype": "float32",
2464
+ "shape": [65, 96],
2465
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2466
+ },
2467
+ "B": {
2468
+ "dtype": "float32",
2469
+ "shape": [128, 96],
2470
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2471
+ },
2472
+ "C": {
2473
+ "dtype": "float32",
2474
+ "shape": [128],
2475
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2476
+ }
2477
+ },
2478
+ "outputs": { "Y": { "dtype": "float32", "shape": [65, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2479
+ },
2480
+ {
2481
+ "name": "direct_a_m64_k32_n64_tb0",
2482
+ "attrs": { "transB": 0, "activation": "Tanh" },
2483
+ "provenance": {
2484
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2485
+ },
2486
+ "inputs": {
2487
+ "A": {
2488
+ "dtype": "float32",
2489
+ "shape": [64, 32],
2490
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2491
+ },
2492
+ "B": {
2493
+ "dtype": "float32",
2494
+ "shape": [32, 64],
2495
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2496
+ }
2497
+ },
2498
+ "outputs": { "Y": { "dtype": "float32", "shape": [64, 64], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2499
+ },
2500
+ {
2501
+ "name": "direct_a_m64_k0_n64_tb1",
2502
+ "attrs": { "transB": 1, "activation": "HardSigmoid" },
2503
+ "provenance": {
2504
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2505
+ },
2506
+ "inputs": {
2507
+ "A": {
2508
+ "dtype": "float32",
2509
+ "shape": [64, 0],
2510
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2511
+ },
2512
+ "B": {
2513
+ "dtype": "float32",
2514
+ "shape": [64, 0],
2515
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2516
+ },
2517
+ "C": {
2518
+ "dtype": "float32",
2519
+ "shape": [64],
2520
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2521
+ }
2522
+ },
2523
+ "outputs": { "Y": { "dtype": "float32", "shape": [64, 64], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2524
+ },
2525
+ {
2526
+ "name": "direct_a_m32_k224_n64_tb0",
2527
+ "attrs": { "transB": 0 },
2528
+ "provenance": {
2529
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2530
+ },
2531
+ "inputs": {
2532
+ "A": {
2533
+ "dtype": "float32",
2534
+ "shape": [32, 224],
2535
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2536
+ },
2537
+ "B": {
2538
+ "dtype": "float32",
2539
+ "shape": [224, 64],
2540
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2541
+ }
2542
+ },
2543
+ "outputs": { "Y": { "dtype": "float32", "shape": [32, 64], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2544
+ },
2545
+ {
2546
+ "name": "direct_a_m32_k256_n64_tb1",
2547
+ "attrs": { "transB": 1 },
2548
+ "provenance": {
2549
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2550
+ },
2551
+ "inputs": {
2552
+ "A": {
2553
+ "dtype": "float32",
2554
+ "shape": [32, 256],
2555
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2556
+ },
2557
+ "B": {
2558
+ "dtype": "float32",
2559
+ "shape": [64, 256],
2560
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2561
+ }
2562
+ },
2563
+ "outputs": { "Y": { "dtype": "float32", "shape": [32, 64], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2564
+ },
2565
+ {
2566
+ "name": "direct_a_m32_k288_n64_tb0",
2567
+ "attrs": { "transB": 0 },
2568
+ "provenance": {
2569
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2570
+ },
2571
+ "inputs": {
2572
+ "A": {
2573
+ "dtype": "float32",
2574
+ "shape": [32, 288],
2575
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2576
+ },
2577
+ "B": {
2578
+ "dtype": "float32",
2579
+ "shape": [288, 64],
2580
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2581
+ }
2582
+ },
2583
+ "outputs": { "Y": { "dtype": "float32", "shape": [32, 64], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2584
+ },
2585
+ {
2586
+ "name": "direct_a_m32_k768_n128_tb0",
2587
+ "attrs": { "transB": 0, "activation": "Relu" },
2588
+ "provenance": {
2589
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2590
+ },
2591
+ "inputs": {
2592
+ "A": {
2593
+ "dtype": "float32",
2594
+ "shape": [32, 768],
2595
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2596
+ },
2597
+ "B": {
2598
+ "dtype": "float32",
2599
+ "shape": [768, 128],
2600
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2601
+ },
2602
+ "C": {
2603
+ "dtype": "float32",
2604
+ "shape": [128],
2605
+ "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.39, "scale": 0.2 }
2606
+ }
2607
+ },
2608
+ "outputs": { "Y": { "dtype": "float32", "shape": [32, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2609
+ },
2610
+ {
2611
+ "name": "direct_a_m64_k1024_n128_tb0",
2612
+ "attrs": { "transB": 0, "activation": "Tanh" },
2613
+ "provenance": {
2614
+ "notes": "Complete A tiles and adjacent row/reduction boundaries exercise direct storage loads, compensated reduction blocks, and guarded staging with signed nonuniform data."
2615
+ },
2616
+ "inputs": {
2617
+ "A": {
2618
+ "dtype": "float32",
2619
+ "shape": [64, 1024],
2620
+ "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.037, "scale": 0.2 }
2621
+ },
2622
+ "B": {
2623
+ "dtype": "float32",
2624
+ "shape": [1024, 128],
2625
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.043, "scale": 0.2 }
2626
+ }
2627
+ },
2628
+ "outputs": { "Y": { "dtype": "float32", "shape": [64, 128], "tolerance": 0.00002, "relTolerance": 0.0002 } }
2629
+ },
2630
+ {
2631
+ "name": "compensated_negative_infinity",
2632
+ "provenance": {
2633
+ "notes": "A 257-element cycle spans 256-element rows: one row stays finite with distinct column results while others contain negative infinity. This detects misplaced outputs and requires infinite partial sums to keep zero compensation before later finite partials."
2634
+ },
2635
+ "inputs": {
2636
+ "A": {
2637
+ "dtype": "float32",
2638
+ "shape": [32, 256],
2639
+ "data": {
2640
+ "kind": "cycle",
2641
+ "values": [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -3.0, -2.0, -1.0, 0.0, 1.0, "-Infinity", 3.0, -3.0, -2.0, -1.0, 0.0, 1.0]
2642
+ }
2643
+ },
2644
+ "B": { "dtype": "float32", "shape": [256, 64], "data": { "kind": "cycle", "values": [1.0, 2.0, 3.0] } }
2645
+ },
2646
+ "outputs": { "Y": { "dtype": "float32", "shape": [32, 64], "tolerance": 0 } }
2647
  }
2648
  ]
2649
  }