Xenova HF Staff commited on
Commit
2284b3a
·
verified ·
1 Parent(s): 70967a1

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,17 +18,17 @@ See the [ONNX `Conv` spec](https://onnx.ai/onnx/operators/onnx__Conv.html) for t
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `x` | `T` | — | — | Input data tensor of shape `(N x C x D1 x ... x Dn)`, where `N` is the batch size and `C` is the number of channels. | required |
24
- | `W` | `w` | `T` | — | — | Convolution filter weights of shape `(M x C/group x k1 x ... x kn)`, where `M` is the number of output feature maps. | required |
25
- | `B` | `bias` | `T` | `1` | — | Optional 1D bias of length M added to each output channel. | optional |
26
 
27
  ## Outputs
28
 
29
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
30
  | --- | --- | --- | --- | --- | --- | --- |
31
- | `Y` | `y` | `T` | same as `X` | derived; see description | Output tensor whose spatial dimensions are determined by the kernel size, strides, dilations, and padding. | required |
32
 
33
  ## Attributes
34
 
@@ -37,8 +37,8 @@ Attributes and default values (overridable per request):
37
  | Attribute | Default | Description |
38
  | --- | --- | --- |
39
  | `auto_pad` | `"NOTSET"` | Automatic padding mode. `NOTSET` uses `pads`; `SAME_UPPER` and `SAME_LOWER` choose padding so each output spatial size is `ceil(input / stride)`; `VALID` uses no padding. |
40
- | `group` | `1` | Number of groups that input and output channels are split into; defaults to 1. |
41
  | `dilations` | — | Optional dilation factors, one positive integer per spatial axis. Omission means all ones. |
 
42
  | `kernel_shape` | — | Optional kernel shape, one positive integer per spatial axis. When present, it must match the spatial dimensions of the weight tensor; omission infers the shape from the weights. |
43
  | `pads` | — | Optional explicit padding in ONNX order `[begin_axis_0, ..., begin_axis_n, end_axis_0, ..., end_axis_n]`. Omission means all zeros; it cannot be combined with an automatic padding mode. |
44
  | `strides` | — | Optional stride factors, one positive integer per spatial axis. Omission means all ones. |
@@ -49,13 +49,45 @@ Attributes and default values (overridable per request):
49
  | --- | --- |
50
  | `T` | `float32`, `float16` |
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  ## Device requirements
53
 
54
- 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.
55
 
56
  ## Files
57
 
58
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
59
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
60
  - [`test.json`](build/webgpu/test.json) — correctness cases
61
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -73,10 +105,14 @@ Some implementation variants require `subgroup-matrix` and `subgroups`. These ar
73
 
74
  ## Use with `@huggingface/kernels`
75
 
76
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
77
- It then allocates the result tensors automatically.
 
 
 
78
 
79
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
80
 
81
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
82
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `x` | `X` | `T` | — | — | Input data tensor of shape `(N x C x D1 x ... x Dn)`, where `N` is the batch size and `C` is the number of channels. | required |
24
+ | `w` | `W` | `T` | — | — | Convolution filter weights of shape `(M x C/group x k1 x ... x kn)`, where `M` is the number of output feature maps. | required |
25
+ | `bias` | `B` | `T` | `1` | — | Optional 1D bias of length M added to each output channel. | optional |
26
 
27
  ## Outputs
28
 
29
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
30
  | --- | --- | --- | --- | --- | --- | --- |
31
+ | `y` | `Y` | `T` | same as `x` | derived | Output tensor whose spatial dimensions are determined by the kernel size, strides, dilations, and padding. | required |
32
 
33
  ## Attributes
34
 
 
37
  | Attribute | Default | Description |
38
  | --- | --- | --- |
39
  | `auto_pad` | `"NOTSET"` | Automatic padding mode. `NOTSET` uses `pads`; `SAME_UPPER` and `SAME_LOWER` choose padding so each output spatial size is `ceil(input / stride)`; `VALID` uses no padding. |
 
40
  | `dilations` | — | Optional dilation factors, one positive integer per spatial axis. Omission means all ones. |
41
+ | `group` | `1` | Number of groups that input and output channels are split into; defaults to 1. |
42
  | `kernel_shape` | — | Optional kernel shape, one positive integer per spatial axis. When present, it must match the spatial dimensions of the weight tensor; omission infers the shape from the weights. |
43
  | `pads` | — | Optional explicit padding in ONNX order `[begin_axis_0, ..., begin_axis_n, end_axis_0, ..., end_axis_n]`. Omission means all zeros; it cannot be combined with an automatic padding mode. |
44
  | `strides` | — | Optional stride factors, one positive integer per spatial axis. Omission means all ones. |
 
49
  | --- | --- |
50
  | `T` | `float32`, `float16` |
51
 
52
+ ## Implementation variants
53
+
54
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
55
+
56
+ - `pointwise_channel_reduce` — Handles pointwise unit-stride convolution with few output channels by assigning one invocation to each spatial position and accumulating every output channel in registers. It does not materialize im2col scratch.
57
+ - `pointwise_channel_reduce_bias` — Handles pointwise unit-stride convolution with few output channels by assigning one invocation to each spatial position and accumulating every output channel in registers. It does not materialize im2col scratch.
58
+ - `gemm_1x1_subgroup_matrix` — Multiplies 1x1 weights directly by NCHW input. Complete output-channel tiles use direct subgroup-matrix loads, eliminating input staging and reduction-loop barriers; partial tiles retain guarded staging. Tile geometry follows the device's workgroup limits.
59
+ - `gemm_1x1_subgroup_matrix_bias` — Multiplies 1x1 weights directly by NCHW input. Complete output-channel tiles use direct subgroup-matrix loads, eliminating input staging and reduction-loop barriers; partial tiles retain guarded staging. Tile geometry follows the device's workgroup limits.
60
+ - `im2col_gemm_subgroup_matrix` — Materializes convolution windows as a column matrix, then multiplies it by the weights with subgroup-matrix operations. It applies when reduction and spatial extents are tile-aligned and column scratch fits.
61
+ - `im2col_gemm_subgroup_matrix_bias` — Materializes convolution windows as a column matrix, then multiplies it by the weights with subgroup-matrix operations. It applies when reduction and spatial extents are tile-aligned and column scratch fits.
62
+ - `im2col_direct_f32_subgroup_matrix` — Materialize tile-aligned convolution columns and load weights and columns directly into subgroup matrices, removing operand staging and K-loop barriers. Retain f32 accumulation, existing split-K reduction order, and the implicit gather preference below the measured channel-tile reuse threshold.
63
+ - `im2col_direct_f16_subgroup_matrix` — Materialize tile-aligned convolution columns and load weights and columns directly into subgroup matrices, removing operand staging and K-loop barriers. Retain f32 accumulation, existing split-K reduction order, and the implicit gather preference below the measured channel-tile reuse threshold.
64
+ - `im2col_direct_f32_subgroup_matrix_bias` — Materialize tile-aligned convolution columns and load weights and columns directly into subgroup matrices, removing operand staging and K-loop barriers. Retain f32 accumulation, existing split-K reduction order, and the implicit gather preference below the measured channel-tile reuse threshold.
65
+ - `im2col_direct_f16_subgroup_matrix_bias` — Materialize tile-aligned convolution columns and load weights and columns directly into subgroup matrices, removing operand staging and K-loop barriers. Retain f32 accumulation, existing split-K reduction order, and the implicit gather preference below the measured channel-tile reuse threshold.
66
+ - `implicit_im2col_subgroup_matrix` — Gathers each logical im2col tile directly from X instead of materializing a column matrix, trading extra address arithmetic for the scratch and bandwidth that materialization would cost. It applies when the output has few output-channel tiles, so the gather is not repeated across many tiles.
67
+ - `implicit_im2col_subgroup_matrix_bias` — Gathers each logical im2col tile directly from X instead of materializing a column matrix, trading extra address arithmetic for the scratch and bandwidth that materialization would cost. It applies when the output has few output-channel tiles, so the gather is not repeated across many tiles.
68
+ - `implicit_im2col3d_subgroup_matrix` — Gathers each logical 3-D im2col tile directly from X instead of materializing a column matrix, trading extra address arithmetic for the scratch and bandwidth a volumetric materialization would cost. It applies when the output has few output-channel tiles, so the gather is not repeated across many tiles.
69
+ - `implicit_im2col3d_subgroup_matrix_bias` — Gathers each logical 3-D im2col tile directly from X instead of materializing a column matrix, trading extra address arithmetic for the scratch and bandwidth a volumetric materialization would cost. It applies when the output has few output-channel tiles, so the gather is not repeated across many tiles.
70
+ - `implicit_im2col_subgroup_matrix_splitk` — Partitions the implicit-im2col reduction across workgroups when the unsplit output grid is too small, then combines raw partial sums from scratch. The combine reassociates floating-point addition relative to the unsplit route.
71
+ - `implicit_im2col_subgroup_matrix_bias_splitk` — Partitions the implicit-im2col reduction across workgroups when the unsplit output grid is too small, then combines raw partial sums from scratch. The combine reassociates floating-point addition relative to the unsplit route.
72
+ - `im2col_gemm_subgroup_matrix_padded` — Materializes a zero-padded column matrix before a subgroup-matrix multiply, allowing logical reduction and spatial extents that are not tile-aligned. Padded values contribute zero and are excluded from the logical output.
73
+ - `im2col_gemm_subgroup_matrix_padded_bias` — Materializes a zero-padded column matrix before a subgroup-matrix multiply, allowing logical reduction and spatial extents that are not tile-aligned. Padded values contribute zero and are excluded from the logical output.
74
+ - `im2col_gemm_subgroup_matrix_padded_splitk` — Materializes a zero-padded column matrix, partitions its subgroup-matrix reduction across workgroups, and combines raw partial sums from scratch. It preserves the padded route for low-parallelism outputs; the combine reassociates floating-point addition.
75
+ - `im2col_gemm_subgroup_matrix_padded_bias_splitk` — Materializes a zero-padded column matrix, partitions its subgroup-matrix reduction across workgroups, and combines raw partial sums from scratch. It preserves the padded route for low-parallelism outputs; the combine reassociates floating-point addition.
76
+ - `implicit_im2col_tiled_bias_reg_m32` — Gathers logical im2col elements directly from X while filling register-blocked GEMM tiles. It avoids column scratch and provides the portable implicit route without subgroup-matrix support.
77
+ - `implicit_im2col_tiled_reg` — Gathers logical im2col elements directly from X while filling register-blocked GEMM tiles. It avoids column scratch and provides the portable implicit route without subgroup-matrix support.
78
+ - `implicit_im2col_tiled_bias_reg` — Gathers logical im2col elements directly from X while filling register-blocked GEMM tiles. It avoids column scratch and provides the portable implicit route without subgroup-matrix support.
79
+ - `implicit_im2col_tiled_reg_splitk` — Partitions the register-blocked implicit-im2col reduction across workgroups when the unsplit output grid is too small, then combines raw partial sums. The combine reassociates floating-point addition relative to the unsplit route.
80
+ - `implicit_im2col_tiled_bias_reg_splitk` — Partitions the register-blocked implicit-im2col reduction across workgroups when the unsplit output grid is too small, then combines raw partial sums. The combine reassociates floating-point addition relative to the unsplit route.
81
+ - `gemm_1x1_tiled_reg_splitk` — Partitions the register-blocked 1x1 GEMM reduction across additional workgroups when the output grid is small, then combines raw partial sums. The combine reassociates floating-point addition relative to the unsplit route.
82
+ - `gemm_1x1_tiled_bias_reg_splitk` — Partitions the register-blocked 1x1 GEMM reduction across additional workgroups when the output grid is small, then combines raw partial sums. The combine reassociates floating-point addition relative to the unsplit route.
83
+
84
  ## Device requirements
85
 
86
+ Some implementation variants require `subgroup-matrix`, `shader-f16`, and `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
87
 
88
  ## Files
89
 
90
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
91
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
92
  - [`test.json`](build/webgpu/test.json) — correctness cases
93
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
105
 
106
  ## Use with `@huggingface/kernels`
107
 
108
+ ```sh
109
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
110
+ ```
111
+
112
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
113
 
114
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
115
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
116
 
117
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
118
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Conv",
3
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
4
  "cases": [
5
  {
@@ -139,8 +138,9 @@
139
  {
140
  "name": "audio-feature-1d-f32-b1c256m256-w4096-k7-pathology",
141
  "preset": "stress",
 
142
  "provenance": {
143
- "source": "authored for performance coverage",
144
  "notes": "Realistic long-sequence audio feature block with K and output width aligned for the materialized subgroup-matrix Conv1d path; reduced-feature tiers retain the general implicit-GEMM and direct NCW implementations."
145
  },
146
  "vars": { "batch": 1, "inChannels": 256, "outChannels": 256, "inW": 4096, "kernelW": 7, "strideW": 1, "padW": 3 },
@@ -198,7 +198,7 @@
198
  "name": "medical-segmentation-3d-f32-b1c32m32-32x32x32-k3-pathology",
199
  "preset": "stress",
200
  "provenance": {
201
- "source": "authored for performance coverage",
202
  "notes": "Realistic 3D medical-segmentation feature block for the direct NCDHW kernel. Each output voxel serially scans 32 input channels and a 3x3x3 kernel."
203
  },
204
  "vars": {
@@ -547,8 +547,8 @@
547
  {
548
  "name": "conv-f32-group4-k5-64c-48x48-mid-kernel-band",
549
  "provenance": {
550
- "source": "authored for performance coverage",
551
- "notes": "Grouped 5x5 in the middle of the kernel-size band grouped_large_kernel_w4 used to refuse. No grouped benchmark existed between k3 and k9, so the k>=7 floor had nothing measured on either side of it."
552
  },
553
  "preset": "model",
554
  "vars": {
@@ -976,8 +976,8 @@
976
  "name": "manifest-gap-conv-f32-group8-k9-256c-64x63-w4-boundary",
977
  "preset": "stress",
978
  "provenance": {
979
- "source": "authored for boundary coverage",
980
- "notes": "A realistic odd-width grouped large-kernel convolution that falls outside grouped_large_kernel_w4's output-width % 4 == 0 guard; the paired W64 case isolates the route boundary."
981
  },
982
  "vars": {
983
  "batch": 1,
@@ -2163,6 +2163,599 @@
2163
  }
2164
  ]
2165
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2166
  }
2167
  ]
2168
  }
 
1
  {
 
2
  "tunableSpace": { "WORKGROUP_SIZE": [64, 128, 256] },
3
  "cases": [
4
  {
 
138
  {
139
  "name": "audio-feature-1d-f32-b1c256m256-w4096-k7-pathology",
140
  "preset": "stress",
141
+ "tunableSpace": { "CONV1D_TILE_M": [4, 8], "CONV1D_TILE_N": [4, 8], "CONV1D_K_TILE": [16, 32] },
142
  "provenance": {
143
+ "source": "repository-authored benchmark",
144
  "notes": "Realistic long-sequence audio feature block with K and output width aligned for the materialized subgroup-matrix Conv1d path; reduced-feature tiers retain the general implicit-GEMM and direct NCW implementations."
145
  },
146
  "vars": { "batch": 1, "inChannels": 256, "outChannels": 256, "inW": 4096, "kernelW": 7, "strideW": 1, "padW": 3 },
 
198
  "name": "medical-segmentation-3d-f32-b1c32m32-32x32x32-k3-pathology",
199
  "preset": "stress",
200
  "provenance": {
201
+ "source": "repository-authored benchmark",
202
  "notes": "Realistic 3D medical-segmentation feature block for the direct NCDHW kernel. Each output voxel serially scans 32 input channels and a 3x3x3 kernel."
203
  },
204
  "vars": {
 
547
  {
548
  "name": "conv-f32-group4-k5-64c-48x48-mid-kernel-band",
549
  "provenance": {
550
+ "source": "repository-authored benchmark",
551
+ "notes": "A grouped 5x5 convolution exercises the wide-output grouped route at an intermediate kernel size between the 3x3 and 9x9 benchmark shapes."
552
  },
553
  "preset": "model",
554
  "vars": {
 
976
  "name": "manifest-gap-conv-f32-group8-k9-256c-64x63-w4-boundary",
977
  "preset": "stress",
978
  "provenance": {
979
+ "source": "repository-authored benchmark",
980
+ "notes": "A realistic grouped 9x9 convolution produces 63 columns, so the final four-lane output group contains three valid columns and uses masked tail stores."
981
  },
982
  "vars": {
983
  "batch": 1,
 
2163
  }
2164
  ]
2165
  }
2166
+ },
2167
+ {
2168
+ "name": "conv-f32-depthwise-96c-56x56-k3-bias",
2169
+ "preset": "edge",
2170
+ "attrs": { "group": 96, "kernel_shape": [3, 3], "pads": [1, 1, 1, 1], "strides": [1, 1] },
2171
+ "inputs": {
2172
+ "x": { "dtype": "float32", "shape": [1, 96, 56, 56], "dist": "normal", "seed": 7, "scale": 1 },
2173
+ "w": { "dtype": "float32", "shape": [96, 1, 3, 3], "dist": "normal", "seed": 11, "scale": 0.2 },
2174
+ "bias": { "dtype": "float32", "shape": [96], "dist": "normal", "seed": 13, "scale": 0.1 }
2175
+ },
2176
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 96, 56, 56] } },
2177
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 96 * 56 * 56 * 9" }] }
2178
+ },
2179
+ {
2180
+ "name": "conv-f32-depthwise-96c-56x56-k3-no-bias-control",
2181
+ "preset": "edge",
2182
+ "attrs": { "group": 96, "kernel_shape": [3, 3], "pads": [1, 1, 1, 1], "strides": [1, 1] },
2183
+ "inputs": {
2184
+ "x": { "dtype": "float32", "shape": [1, 96, 56, 56], "dist": "normal", "seed": 7, "scale": 1 },
2185
+ "w": { "dtype": "float32", "shape": [96, 1, 3, 3], "dist": "normal", "seed": 11, "scale": 0.2 }
2186
+ },
2187
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 96, 56, 56] } },
2188
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 96 * 56 * 56 * 9" }] }
2189
+ },
2190
+ {
2191
+ "name": "conv-f32-depthwise-576c-14x14-k3-bias",
2192
+ "preset": "edge",
2193
+ "attrs": { "group": 576, "kernel_shape": [3, 3], "pads": [1, 1, 1, 1], "strides": [1, 1] },
2194
+ "inputs": {
2195
+ "x": { "dtype": "float32", "shape": [32, 576, 14, 14], "dist": "normal", "seed": 7, "scale": 1 },
2196
+ "w": { "dtype": "float32", "shape": [576, 1, 3, 3], "dist": "normal", "seed": 11, "scale": 0.2 },
2197
+ "bias": { "dtype": "float32", "shape": [576], "dist": "normal", "seed": 13, "scale": 0.1 }
2198
+ },
2199
+ "outputs": { "y": { "dtype": "float32", "shape": [32, 576, 14, 14] } },
2200
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 32 * 576 * 14 * 14 * 9" }] }
2201
+ },
2202
+ {
2203
+ "name": "conv-f32-depthwise-960c-7x7-k3-bias",
2204
+ "preset": "edge",
2205
+ "attrs": { "group": 960, "kernel_shape": [3, 3], "pads": [1, 1, 1, 1], "strides": [1, 1] },
2206
+ "inputs": {
2207
+ "x": { "dtype": "float32", "shape": [32, 960, 7, 7], "dist": "normal", "seed": 7, "scale": 1 },
2208
+ "w": { "dtype": "float32", "shape": [960, 1, 3, 3], "dist": "normal", "seed": 11, "scale": 0.2 },
2209
+ "bias": { "dtype": "float32", "shape": [960], "dist": "normal", "seed": 13, "scale": 0.1 }
2210
+ },
2211
+ "outputs": { "y": { "dtype": "float32", "shape": [32, 960, 7, 7] } },
2212
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 32 * 960 * 7 * 7 * 9" }] }
2213
+ },
2214
+ {
2215
+ "name": "conv-f32-depthwise-192c-28x28-k3-bias",
2216
+ "preset": "edge",
2217
+ "attrs": { "group": 192, "kernel_shape": [3, 3], "pads": [1, 1, 1, 1], "strides": [1, 1] },
2218
+ "inputs": {
2219
+ "x": { "dtype": "float32", "shape": [32, 192, 28, 28], "dist": "normal", "seed": 7, "scale": 1 },
2220
+ "w": { "dtype": "float32", "shape": [192, 1, 3, 3], "dist": "normal", "seed": 11, "scale": 0.2 },
2221
+ "bias": { "dtype": "float32", "shape": [192], "dist": "normal", "seed": 13, "scale": 0.1 }
2222
+ },
2223
+ "outputs": { "y": { "dtype": "float32", "shape": [32, 192, 28, 28] } },
2224
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 32 * 192 * 28 * 28 * 9" }] }
2225
+ },
2226
+ {
2227
+ "name": "conv-f16-64c-56x56-k3-bias-sgmat",
2228
+ "preset": "edge",
2229
+ "attrs": { "kernel_shape": [3, 3], "pads": [1, 1, 1, 1], "strides": [1, 1] },
2230
+ "inputs": {
2231
+ "x": { "dtype": "float16", "shape": [1, 64, 56, 56], "dist": "normal", "seed": 5, "scale": 1 },
2232
+ "w": { "dtype": "float16", "shape": [64, 64, 3, 3], "dist": "normal", "seed": 9, "scale": 0.05 },
2233
+ "bias": { "dtype": "float16", "shape": [64], "dist": "normal", "seed": 3, "scale": 0.1 }
2234
+ },
2235
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 64, 56, 56] } },
2236
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 64 * 64 * 56 * 56 * 9" }] }
2237
+ },
2238
+ {
2239
+ "name": "conv-f32-64c-56x56-k3-bias-sgmat-control",
2240
+ "preset": "edge",
2241
+ "attrs": { "kernel_shape": [3, 3], "pads": [1, 1, 1, 1], "strides": [1, 1] },
2242
+ "inputs": {
2243
+ "x": { "dtype": "float32", "shape": [1, 64, 56, 56], "dist": "normal", "seed": 5, "scale": 1 },
2244
+ "w": { "dtype": "float32", "shape": [64, 64, 3, 3], "dist": "normal", "seed": 9, "scale": 0.05 },
2245
+ "bias": { "dtype": "float32", "shape": [64], "dist": "normal", "seed": 3, "scale": 0.1 }
2246
+ },
2247
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 64, 56, 56] } },
2248
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 64 * 64 * 56 * 56 * 9" }] }
2249
+ },
2250
+ {
2251
+ "name": "conv-reuse-float32-m32-c64-n16384",
2252
+ "preset": "edge",
2253
+ "inputs": {
2254
+ "x": { "dtype": "float32", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2255
+ "w": { "dtype": "float32", "shape": [32, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2256
+ },
2257
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 32, 128, 128] } },
2258
+ "attrs": { "pads": [1, 1, 1, 1] },
2259
+ "bench": {
2260
+ "metrics": [
2261
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2262
+ ]
2263
+ }
2264
+ },
2265
+ {
2266
+ "name": "conv-reuse-float32-m32-c256-n16384",
2267
+ "preset": "edge",
2268
+ "inputs": {
2269
+ "x": { "dtype": "float32", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2270
+ "w": { "dtype": "float32", "shape": [32, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2271
+ },
2272
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 32, 128, 128] } },
2273
+ "attrs": { "pads": [1, 1, 1, 1] },
2274
+ "bench": {
2275
+ "metrics": [
2276
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2277
+ ]
2278
+ }
2279
+ },
2280
+ {
2281
+ "name": "conv-reuse-float32-m64-c64-n16384",
2282
+ "preset": "edge",
2283
+ "inputs": {
2284
+ "x": { "dtype": "float32", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2285
+ "w": { "dtype": "float32", "shape": [64, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2286
+ },
2287
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 64, 128, 128] } },
2288
+ "attrs": { "pads": [1, 1, 1, 1] },
2289
+ "bench": {
2290
+ "metrics": [
2291
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2292
+ ]
2293
+ }
2294
+ },
2295
+ {
2296
+ "name": "conv-reuse-float32-m64-c256-n16384",
2297
+ "preset": "edge",
2298
+ "inputs": {
2299
+ "x": { "dtype": "float32", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2300
+ "w": { "dtype": "float32", "shape": [64, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2301
+ },
2302
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 64, 128, 128] } },
2303
+ "attrs": { "pads": [1, 1, 1, 1] },
2304
+ "bench": {
2305
+ "metrics": [
2306
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2307
+ ]
2308
+ }
2309
+ },
2310
+ {
2311
+ "name": "conv-reuse-float32-m96-c64-n16384",
2312
+ "preset": "edge",
2313
+ "inputs": {
2314
+ "x": { "dtype": "float32", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2315
+ "w": { "dtype": "float32", "shape": [96, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2316
+ },
2317
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 96, 128, 128] } },
2318
+ "attrs": { "pads": [1, 1, 1, 1] },
2319
+ "bench": {
2320
+ "metrics": [
2321
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2322
+ ]
2323
+ }
2324
+ },
2325
+ {
2326
+ "name": "conv-reuse-float32-m96-c256-n16384",
2327
+ "preset": "edge",
2328
+ "inputs": {
2329
+ "x": { "dtype": "float32", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2330
+ "w": { "dtype": "float32", "shape": [96, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2331
+ },
2332
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 96, 128, 128] } },
2333
+ "attrs": { "pads": [1, 1, 1, 1] },
2334
+ "bench": {
2335
+ "metrics": [
2336
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2337
+ ]
2338
+ }
2339
+ },
2340
+ {
2341
+ "name": "conv-reuse-float32-m128-c64-n16384",
2342
+ "preset": "edge",
2343
+ "inputs": {
2344
+ "x": { "dtype": "float32", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2345
+ "w": { "dtype": "float32", "shape": [128, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2346
+ },
2347
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 128, 128, 128] } },
2348
+ "attrs": { "pads": [1, 1, 1, 1] },
2349
+ "bench": {
2350
+ "metrics": [
2351
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2352
+ ]
2353
+ }
2354
+ },
2355
+ {
2356
+ "name": "conv-reuse-float32-m128-c256-n16384",
2357
+ "preset": "edge",
2358
+ "inputs": {
2359
+ "x": { "dtype": "float32", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2360
+ "w": { "dtype": "float32", "shape": [128, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2361
+ },
2362
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 128, 128, 128] } },
2363
+ "attrs": { "pads": [1, 1, 1, 1] },
2364
+ "bench": {
2365
+ "metrics": [
2366
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2367
+ ]
2368
+ }
2369
+ },
2370
+ {
2371
+ "name": "conv-reuse-float32-m256-c64-n16384",
2372
+ "preset": "edge",
2373
+ "inputs": {
2374
+ "x": { "dtype": "float32", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2375
+ "w": { "dtype": "float32", "shape": [256, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2376
+ },
2377
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 256, 128, 128] } },
2378
+ "attrs": { "pads": [1, 1, 1, 1] },
2379
+ "bench": {
2380
+ "metrics": [
2381
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2382
+ ]
2383
+ }
2384
+ },
2385
+ {
2386
+ "name": "conv-reuse-float32-m256-c256-n16384",
2387
+ "preset": "edge",
2388
+ "inputs": {
2389
+ "x": { "dtype": "float32", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2390
+ "w": { "dtype": "float32", "shape": [256, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2391
+ },
2392
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 256, 128, 128] } },
2393
+ "attrs": { "pads": [1, 1, 1, 1] },
2394
+ "bench": {
2395
+ "metrics": [
2396
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2397
+ ]
2398
+ }
2399
+ },
2400
+ {
2401
+ "name": "conv-reuse-float16-m32-c64-n16384",
2402
+ "preset": "edge",
2403
+ "inputs": {
2404
+ "x": { "dtype": "float16", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2405
+ "w": { "dtype": "float16", "shape": [32, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2406
+ },
2407
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 32, 128, 128] } },
2408
+ "attrs": { "pads": [1, 1, 1, 1] },
2409
+ "bench": {
2410
+ "metrics": [
2411
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2412
+ ]
2413
+ }
2414
+ },
2415
+ {
2416
+ "name": "conv-reuse-float16-m32-c256-n16384",
2417
+ "preset": "edge",
2418
+ "inputs": {
2419
+ "x": { "dtype": "float16", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2420
+ "w": { "dtype": "float16", "shape": [32, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2421
+ },
2422
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 32, 128, 128] } },
2423
+ "attrs": { "pads": [1, 1, 1, 1] },
2424
+ "bench": {
2425
+ "metrics": [
2426
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2427
+ ]
2428
+ }
2429
+ },
2430
+ {
2431
+ "name": "conv-reuse-float16-m64-c64-n16384",
2432
+ "preset": "edge",
2433
+ "inputs": {
2434
+ "x": { "dtype": "float16", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2435
+ "w": { "dtype": "float16", "shape": [64, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2436
+ },
2437
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 64, 128, 128] } },
2438
+ "attrs": { "pads": [1, 1, 1, 1] },
2439
+ "bench": {
2440
+ "metrics": [
2441
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2442
+ ]
2443
+ }
2444
+ },
2445
+ {
2446
+ "name": "conv-reuse-float16-m64-c256-n16384",
2447
+ "preset": "edge",
2448
+ "inputs": {
2449
+ "x": { "dtype": "float16", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2450
+ "w": { "dtype": "float16", "shape": [64, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2451
+ },
2452
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 64, 128, 128] } },
2453
+ "attrs": { "pads": [1, 1, 1, 1] },
2454
+ "bench": {
2455
+ "metrics": [
2456
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2457
+ ]
2458
+ }
2459
+ },
2460
+ {
2461
+ "name": "conv-reuse-float16-m96-c64-n16384",
2462
+ "preset": "edge",
2463
+ "inputs": {
2464
+ "x": { "dtype": "float16", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2465
+ "w": { "dtype": "float16", "shape": [96, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2466
+ },
2467
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 96, 128, 128] } },
2468
+ "attrs": { "pads": [1, 1, 1, 1] },
2469
+ "bench": {
2470
+ "metrics": [
2471
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2472
+ ]
2473
+ }
2474
+ },
2475
+ {
2476
+ "name": "conv-reuse-float16-m96-c256-n16384",
2477
+ "preset": "edge",
2478
+ "inputs": {
2479
+ "x": { "dtype": "float16", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2480
+ "w": { "dtype": "float16", "shape": [96, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2481
+ },
2482
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 96, 128, 128] } },
2483
+ "attrs": { "pads": [1, 1, 1, 1] },
2484
+ "bench": {
2485
+ "metrics": [
2486
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2487
+ ]
2488
+ }
2489
+ },
2490
+ {
2491
+ "name": "conv-reuse-float16-m128-c64-n16384",
2492
+ "preset": "edge",
2493
+ "inputs": {
2494
+ "x": { "dtype": "float16", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2495
+ "w": { "dtype": "float16", "shape": [128, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2496
+ },
2497
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 128, 128, 128] } },
2498
+ "attrs": { "pads": [1, 1, 1, 1] },
2499
+ "bench": {
2500
+ "metrics": [
2501
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2502
+ ]
2503
+ }
2504
+ },
2505
+ {
2506
+ "name": "conv-reuse-float16-m128-c256-n16384",
2507
+ "preset": "edge",
2508
+ "inputs": {
2509
+ "x": { "dtype": "float16", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2510
+ "w": { "dtype": "float16", "shape": [128, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2511
+ },
2512
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 128, 128, 128] } },
2513
+ "attrs": { "pads": [1, 1, 1, 1] },
2514
+ "bench": {
2515
+ "metrics": [
2516
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2517
+ ]
2518
+ }
2519
+ },
2520
+ {
2521
+ "name": "conv-reuse-float16-m256-c64-n16384",
2522
+ "preset": "edge",
2523
+ "inputs": {
2524
+ "x": { "dtype": "float16", "shape": [1, 64, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2525
+ "w": { "dtype": "float16", "shape": [256, 64, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2526
+ },
2527
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 256, 128, 128] } },
2528
+ "attrs": { "pads": [1, 1, 1, 1] },
2529
+ "bench": {
2530
+ "metrics": [
2531
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2532
+ ]
2533
+ }
2534
+ },
2535
+ {
2536
+ "name": "conv-reuse-float16-m256-c256-n16384",
2537
+ "preset": "edge",
2538
+ "inputs": {
2539
+ "x": { "dtype": "float16", "shape": [1, 256, 128, 128], "dist": "normal", "seed": 821, "scale": 0.2 },
2540
+ "w": { "dtype": "float16", "shape": [256, 256, 3, 3], "dist": "normal", "seed": 938, "scale": 0.05 }
2541
+ },
2542
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 256, 128, 128] } },
2543
+ "attrs": { "pads": [1, 1, 1, 1] },
2544
+ "bench": {
2545
+ "metrics": [
2546
+ { "type": "gflops", "value": "2 * numel(shapes.y) * dim(shapes.w, 1) * dim(shapes.w, 2) * dim(shapes.w, 3)" }
2547
+ ]
2548
+ }
2549
+ },
2550
+ {
2551
+ "name": "direct-1x1-float32-m63-k512-n4096",
2552
+ "preset": "model",
2553
+ "vars": {
2554
+ "batch": 1,
2555
+ "inChannels": 512,
2556
+ "outChannels": 63,
2557
+ "inH": 64,
2558
+ "inW": 64,
2559
+ "kernelH": 1,
2560
+ "kernelW": 1,
2561
+ "strideH": 1,
2562
+ "strideW": 1,
2563
+ "padH": 0,
2564
+ "padW": 0
2565
+ },
2566
+ "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2567
+ "inputs": {
2568
+ "x": { "shape": [1, 512, 64, 64], "dtype": "float32", "dist": "normal", "seed": 354, "scale": 0.2 },
2569
+ "w": { "shape": [63, 512, 1, 1], "dtype": "float32", "dist": "normal", "seed": 355, "scale": 0.02 }
2570
+ },
2571
+ "outputs": { "y": { "shape": [1, 63, 64, 64], "dtype": "float32" } },
2572
+ "bench": {
2573
+ "metrics": [
2574
+ {
2575
+ "type": "gflops",
2576
+ "value": "2 * args.batch * args.outChannels * dim(shapes.y, 2) * dim(shapes.y, 3) * args.inChannels * args.kernelH * args.kernelW"
2577
+ }
2578
+ ]
2579
+ },
2580
+ "provenance": {
2581
+ "source": "synthetic",
2582
+ "notes": "Row-tile boundary sweep: full, partial, and device-dependent complete row bands share the same reduction and spatial extent."
2583
+ }
2584
+ },
2585
+ {
2586
+ "name": "direct-1x1-float32-m64-k512-n4096",
2587
+ "preset": "model",
2588
+ "vars": {
2589
+ "batch": 1,
2590
+ "inChannels": 512,
2591
+ "outChannels": 64,
2592
+ "inH": 64,
2593
+ "inW": 64,
2594
+ "kernelH": 1,
2595
+ "kernelW": 1,
2596
+ "strideH": 1,
2597
+ "strideW": 1,
2598
+ "padH": 0,
2599
+ "padW": 0
2600
+ },
2601
+ "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2602
+ "inputs": {
2603
+ "x": { "shape": [1, 512, 64, 64], "dtype": "float32", "dist": "normal", "seed": 354, "scale": 0.2 },
2604
+ "w": { "shape": [64, 512, 1, 1], "dtype": "float32", "dist": "normal", "seed": 355, "scale": 0.02 }
2605
+ },
2606
+ "outputs": { "y": { "shape": [1, 64, 64, 64], "dtype": "float32" } },
2607
+ "bench": {
2608
+ "metrics": [
2609
+ {
2610
+ "type": "gflops",
2611
+ "value": "2 * args.batch * args.outChannels * dim(shapes.y, 2) * dim(shapes.y, 3) * args.inChannels * args.kernelH * args.kernelW"
2612
+ }
2613
+ ]
2614
+ },
2615
+ "provenance": {
2616
+ "source": "synthetic",
2617
+ "notes": "Row-tile boundary sweep: full, partial, and device-dependent complete row bands share the same reduction and spatial extent."
2618
+ }
2619
+ },
2620
+ {
2621
+ "name": "direct-1x1-float32-m96-k512-n4096",
2622
+ "preset": "model",
2623
+ "vars": {
2624
+ "batch": 1,
2625
+ "inChannels": 512,
2626
+ "outChannels": 96,
2627
+ "inH": 64,
2628
+ "inW": 64,
2629
+ "kernelH": 1,
2630
+ "kernelW": 1,
2631
+ "strideH": 1,
2632
+ "strideW": 1,
2633
+ "padH": 0,
2634
+ "padW": 0
2635
+ },
2636
+ "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2637
+ "inputs": {
2638
+ "x": { "shape": [1, 512, 64, 64], "dtype": "float32", "dist": "normal", "seed": 354, "scale": 0.2 },
2639
+ "w": { "shape": [96, 512, 1, 1], "dtype": "float32", "dist": "normal", "seed": 355, "scale": 0.02 }
2640
+ },
2641
+ "outputs": { "y": { "shape": [1, 96, 64, 64], "dtype": "float32" } },
2642
+ "bench": {
2643
+ "metrics": [
2644
+ {
2645
+ "type": "gflops",
2646
+ "value": "2 * args.batch * args.outChannels * dim(shapes.y, 2) * dim(shapes.y, 3) * args.inChannels * args.kernelH * args.kernelW"
2647
+ }
2648
+ ]
2649
+ },
2650
+ "provenance": {
2651
+ "source": "synthetic",
2652
+ "notes": "Row-tile boundary sweep: full, partial, and device-dependent complete row bands share the same reduction and spatial extent."
2653
+ }
2654
+ },
2655
+ {
2656
+ "name": "direct-1x1-float16-m63-k512-n4096",
2657
+ "preset": "model",
2658
+ "vars": {
2659
+ "batch": 1,
2660
+ "inChannels": 512,
2661
+ "outChannels": 63,
2662
+ "inH": 64,
2663
+ "inW": 64,
2664
+ "kernelH": 1,
2665
+ "kernelW": 1,
2666
+ "strideH": 1,
2667
+ "strideW": 1,
2668
+ "padH": 0,
2669
+ "padW": 0
2670
+ },
2671
+ "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2672
+ "inputs": {
2673
+ "x": { "shape": [1, 512, 64, 64], "dtype": "float16", "dist": "normal", "seed": 354, "scale": 0.2 },
2674
+ "w": { "shape": [63, 512, 1, 1], "dtype": "float16", "dist": "normal", "seed": 355, "scale": 0.02 }
2675
+ },
2676
+ "outputs": { "y": { "shape": [1, 63, 64, 64], "dtype": "float16" } },
2677
+ "bench": {
2678
+ "metrics": [
2679
+ {
2680
+ "type": "gflops",
2681
+ "value": "2 * args.batch * args.outChannels * dim(shapes.y, 2) * dim(shapes.y, 3) * args.inChannels * args.kernelH * args.kernelW"
2682
+ }
2683
+ ]
2684
+ },
2685
+ "provenance": {
2686
+ "source": "synthetic",
2687
+ "notes": "Row-tile boundary sweep: full, partial, and device-dependent complete row bands share the same reduction and spatial extent."
2688
+ }
2689
+ },
2690
+ {
2691
+ "name": "direct-1x1-float16-m64-k512-n4096",
2692
+ "preset": "model",
2693
+ "vars": {
2694
+ "batch": 1,
2695
+ "inChannels": 512,
2696
+ "outChannels": 64,
2697
+ "inH": 64,
2698
+ "inW": 64,
2699
+ "kernelH": 1,
2700
+ "kernelW": 1,
2701
+ "strideH": 1,
2702
+ "strideW": 1,
2703
+ "padH": 0,
2704
+ "padW": 0
2705
+ },
2706
+ "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2707
+ "inputs": {
2708
+ "x": { "shape": [1, 512, 64, 64], "dtype": "float16", "dist": "normal", "seed": 354, "scale": 0.2 },
2709
+ "w": { "shape": [64, 512, 1, 1], "dtype": "float16", "dist": "normal", "seed": 355, "scale": 0.02 }
2710
+ },
2711
+ "outputs": { "y": { "shape": [1, 64, 64, 64], "dtype": "float16" } },
2712
+ "bench": {
2713
+ "metrics": [
2714
+ {
2715
+ "type": "gflops",
2716
+ "value": "2 * args.batch * args.outChannels * dim(shapes.y, 2) * dim(shapes.y, 3) * args.inChannels * args.kernelH * args.kernelW"
2717
+ }
2718
+ ]
2719
+ },
2720
+ "provenance": {
2721
+ "source": "synthetic",
2722
+ "notes": "Row-tile boundary sweep: full, partial, and device-dependent complete row bands share the same reduction and spatial extent."
2723
+ }
2724
+ },
2725
+ {
2726
+ "name": "direct-1x1-float16-m96-k512-n4096",
2727
+ "preset": "model",
2728
+ "vars": {
2729
+ "batch": 1,
2730
+ "inChannels": 512,
2731
+ "outChannels": 96,
2732
+ "inH": 64,
2733
+ "inW": 64,
2734
+ "kernelH": 1,
2735
+ "kernelW": 1,
2736
+ "strideH": 1,
2737
+ "strideW": 1,
2738
+ "padH": 0,
2739
+ "padW": 0
2740
+ },
2741
+ "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2742
+ "inputs": {
2743
+ "x": { "shape": [1, 512, 64, 64], "dtype": "float16", "dist": "normal", "seed": 354, "scale": 0.2 },
2744
+ "w": { "shape": [96, 512, 1, 1], "dtype": "float16", "dist": "normal", "seed": 355, "scale": 0.02 }
2745
+ },
2746
+ "outputs": { "y": { "shape": [1, 96, 64, 64], "dtype": "float16" } },
2747
+ "bench": {
2748
+ "metrics": [
2749
+ {
2750
+ "type": "gflops",
2751
+ "value": "2 * args.batch * args.outChannels * dim(shapes.y, 2) * dim(shapes.y, 3) * args.inChannels * args.kernelH * args.kernelW"
2752
+ }
2753
+ ]
2754
+ },
2755
+ "provenance": {
2756
+ "source": "synthetic",
2757
+ "notes": "Row-tile boundary sweep: full, partial, and device-dependent complete row bands share the same reduction and spatial extent."
2758
+ }
2759
  }
2760
  ]
2761
  }
build/webgpu/conv-1x1-channel-reduce.wgsl.jinja CHANGED
@@ -1,13 +1,14 @@
1
  // Pointwise convolution with few output channels is evaluated as a per-position
2
  // channel reduction instead of a tiled GEMM. One invocation walks the input
3
- // channels and accumulates every output channel directly in registers.
4
  // Consecutive invocations read consecutive positions from each NCHW channel
5
- // plane, so each channel remains a coalesced stream without shared-memory
6
- // staging or barriers.
7
  //
8
- // The output-channel accumulators are named and unrolled because dynamically
9
- // indexed local arrays may spill on some backends. Selection therefore limits
10
- // this route to cases whose complete accumulator set remains in registers.
 
 
11
  {% if usesF16 %}
12
  enable f16;
13
  {% endif %}
@@ -20,10 +21,10 @@ const COUNT: u32 = {{ batchCount }}u * PLANE;
20
  const WORKGROUP_SIZE: u32 = {{ channelReduceWorkgroupSize }}u;
21
 
22
  @compute @workgroup_size({{ channelReduceWorkgroupSize }}, 1, 1)
23
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
24
  // 2D-folded flat index: gid.y carries the high bits past
25
- // maxComputeWorkgroupsPerDimension and reduces to the 1D form at y=0.
26
- let idx = gid.x + gid.y * nwg.x * WORKGROUP_SIZE;
27
  if (idx >= COUNT) {
28
  return;
29
  }
 
1
  // Pointwise convolution with few output channels is evaluated as a per-position
2
  // channel reduction instead of a tiled GEMM. One invocation walks the input
3
+ // channels and accumulates every output channel in a statically named local.
4
  // Consecutive invocations read consecutive positions from each NCHW channel
5
+ // plane without shared-memory staging or barriers.
 
6
  //
7
+ // The output-channel accumulators are named explicitly. Selection caps the
8
+ // output-channel count, bounding the invocation-local accumulator storage.
9
+ {% set usesF16 = usesF16 is defined and usesF16 %}
10
+ {% set batchCount = batchCount | default(0) %}
11
+ {% set yScalar = yScalar | default("f32") %}
12
  {% if usesF16 %}
13
  enable f16;
14
  {% endif %}
 
21
  const WORKGROUP_SIZE: u32 = {{ channelReduceWorkgroupSize }}u;
22
 
23
  @compute @workgroup_size({{ channelReduceWorkgroupSize }}, 1, 1)
24
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
25
  // 2D-folded flat index: gid.y carries the high bits past
26
+ // the per-axis dispatch fold width and reduces to the 1D form at y=0.
27
+ let idx = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WORKGROUP_SIZE;
28
  if (idx >= COUNT) {
29
  return;
30
  }
build/webgpu/conv-1x1-gemm-tiled-reg.wgsl.jinja CHANGED
@@ -1,11 +1,10 @@
1
- // 1x1 / im2col Conv routed as a batched GEMM:
2
- // Y[b] (M x N) = W (M x K) * X[b] (K x N), with M = outChannels,
3
- // K = inChannels * kh * kw after im2col, and N = H * W. Register-blocked
4
- // workgroup lanes compute TM x TN micro-tiles. The defaults use 16x16 lanes with
5
- // an 8x4 micro-tile over a 64x64 output tile. Weight A is shared across the batch;
6
- // input B and the output use the dispatch.z batch offset. Accumulation is f32;
7
- // the optional epilogue applies bias, residual Z, and activation in the
8
- // accumulator domain on store. Every dimension is bounds-checked.
9
  // f16 operands remain packed in workgroup memory and widen only at the FMA site;
10
  // the f32 accumulation order is retained while shared traffic is halved.
11
  //
@@ -14,16 +13,35 @@
14
  // N columns for one K. One micro-tile step therefore reads TM + 4 vector words
15
  // instead of 4 * (TM + TN) scalars, so a shared word feeds four times as many
16
  // FMAs and the K loop runs four accumulation steps per iteration.
17
- {% if usesF16 %}
18
- enable f16;
19
- {% endif %}
20
  {{ env.wgsl.resourceDeclarations }}
21
  {% set hasActivation = hasActivation is defined and hasActivation %}
 
 
 
22
  {% if hasActivation %}
23
  // Apply the fused activation in the f32 accumulator before the single output
24
  // cast, avoiding an intermediate convolution tensor.
25
- fn fused_act(v: f32) -> f32 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
 
 
 
 
27
  }
28
  {% endif %}
29
 
@@ -50,19 +68,19 @@ const BN_VECS: u32 = BN / 4u;
50
  // implicitIm2col leaves B unmaterialized. While staging a tile, the kernel
51
  // decodes each (k, n) column-matrix coordinate into a raw NCHW input address.
52
  // Overlapping windows may reread input values, trading address arithmetic and
53
- // cache traffic for the storage and bandwidth of the expanded column matrix.
54
  {% set implicitIm2col = implicitIm2col is defined and implicitIm2col %}
55
  {% set fusedNarrowProjection = fusedNarrowProjection is defined and fusedNarrowProjection %}
56
- {% if fusedNarrowProjection %}
57
- {% set projectionChannels = projectionOutChannels %}
58
- {% set projectionInputAct = inputActivation %}
59
- {% set projectionOutputAct = outputActivation %}
60
- {% set projectionScaled = hasOutputScale %}
61
- {% set projectionBiased = hasProjectionBias %}
62
- {% endif %}
63
  {% set narrowProjectionTile = "tileB" if GEMM_BK >= GEMM_BM else "projectionTile" %}
64
  {% set splitKValue = splitK if splitK is defined else 1 %}
65
  {% set splitKPartial = splitKValue > 1 %}
 
 
 
 
66
  {% set gemmKLimit = "kEnd" if splitKPartial else "K" %}
67
  {% set implicitGatherMode = tunables.IMPLICIT_TILED_GATHER_MODE if tunables.IMPLICIT_TILED_GATHER_MODE is defined else 0 %}
68
  {% set implicitGatherWgSize = GEMM_WG_X * GEMM_WG_Y %}
@@ -91,7 +109,7 @@ const CONV_IN_CHANNELS: u32 = {{ convInChannels }}u;
91
  {% if splitKPartial %}
92
  // Split-K publishes raw f32 partials. A second pass owns the bias, optional
93
  // epilogue, output cast, and logical-shape store.
94
- const SPLIT_K: u32 = {{ splitK }}u;
95
  const K_TILES: u32 = {{ kTiles }}u;
96
  const PARTIAL_ROWS: u32 = {{ mPadded }}u;
97
  const PARTIAL_COLS: u32 = {{ nPadded }}u;
@@ -100,9 +118,36 @@ const PARTIAL_SLICE_STRIDE: u32 = {{ batchCount }}u * PARTIAL_BATCH_STRIDE;
100
  {% endif %}
101
 
102
  {% if fusedNarrowProjection %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  const PROJECTION_OUT_C: u32 = {{ projectionChannels }}u;
105
- {% else %}{% set emitConvStoreOut = not splitKPartial %}{% if emitConvStoreOut | default(true) %}fn store_out(m: u32, n: u32, yBase: u32, raw: f32) {
 
106
  if (m >= params.M || n >= params.N) {
107
  return;
108
  }
@@ -110,6 +155,9 @@ const PROJECTION_OUT_C: u32 = {{ projectionChannels }}u;
110
  {% if hasBias %}
111
  v = v + f32(bias[m]);
112
  {% endif %}
 
 
 
113
  {% if hasActivation %}
114
  v = fused_act(v);
115
  {% endif %}
@@ -191,7 +239,7 @@ var<workgroup> projectionTile: array<array<vec4<f32>, BN_VECS>, BM>;
191
  let bk = {{ rowBase }} + br;
192
  var bvec = vec4<{{ tileT }}>({{ tileT }}(0.0));
193
  if (bk < {{ rowLimit }}) {
194
- // k is the OIHW weight flattening (ic * KH + kh) * KW + kw; n is the output position.
195
  let ic = bk / CONV_KSIZE;
196
  let kq = bk % CONV_KSIZE;
197
  let kh = kq / CONV_KERNEL_W;
@@ -322,8 +370,7 @@ fn main(
322
  {% endif %}
323
  let li = lid.y * WG_X + lid.x;
324
 
325
- // Explicit vectors give the backend fixed register indices and expose TM
326
- // independent FMA chains.
327
  {% for row in range(GEMM_TM) %}
328
  var acc{{ row }} = vec{{ GEMM_TN }}<f32>(0.0);
329
  {% endfor %}
@@ -415,7 +462,7 @@ fn main(
415
 
416
  let projectionYBase = batch * PROJECTION_OUT_C * N;
417
  {% for oc in range(projectionChannels) %}
418
- let projected{{ oc }} = projectionAcc{{ oc }}{% if projectionBiased %} + vec4<f32>(f32(projectionBias[{{ oc }}u])){% endif %};
419
  {% endfor %}
420
  {% for column in range(4) %}
421
  let projectionN{{ column }} = projectionNBase + {{ column }}u;
@@ -428,11 +475,7 @@ fn main(
428
  {% else %}
429
  let activated{{ oc }}_{{ column }} = projected{{ oc }}.{{ components[column] }};
430
  {% endif %}
431
- {% if projectionScaled %}
432
- y[projectionYBase + {{ oc }}u * N + projectionN{{ column }}] = activated{{ oc }}_{{ column }} * params.outputScale;
433
- {% else %}
434
  y[projectionYBase + {{ oc }}u * N + projectionN{{ column }}] = activated{{ oc }}_{{ column }};
435
- {% endif %}
436
  {% endfor %}
437
  }
438
  {% endfor %}
 
1
+ // Register-blocked batched matrix product:
2
+ // C[b] (M x N) = A (M x K) * B[b] (K x N). Workgroup lanes compute TM x TN
3
+ // micro-tiles. The defaults use a 16x8 lane grid with an 8x4 micro-tile over a
4
+ // 64x64 output tile. A is shared across the batch; B and C use the dispatch.z
5
+ // batch offset. Accumulation is f32; an optional epilogue applies configured
6
+ // bias, residual, and activation terms in the accumulator domain on store.
7
+ // Every matrix dimension is bounds-checked.
 
8
  // f16 operands remain packed in workgroup memory and widen only at the FMA site;
9
  // the f32 accumulation order is retained while shared traffic is halved.
10
  //
 
13
  // N columns for one K. One micro-tile step therefore reads TM + 4 vector words
14
  // instead of 4 * (TM + TN) scalars, so a shared word feeds four times as many
15
  // FMAs and the K loop runs four accumulation steps per iteration.
 
 
 
16
  {{ env.wgsl.resourceDeclarations }}
17
  {% set hasActivation = hasActivation is defined and hasActivation %}
18
+ {% set activation = activation | default("") %}
19
+ {% set actAlpha = actAlpha | default(0.0) %}
20
+ {% set actBeta = actBeta | default(0.0) %}
21
  {% if hasActivation %}
22
  // Apply the fused activation in the f32 accumulator before the single output
23
  // cast, avoiding an intermediate convolution tensor.
24
+ {% macro fused_act_return(mode, alpha, beta) -%}
25
+ {% if mode == "Relu" %}
26
+ return max(v, 0.0);
27
+ {% elif mode == "Clip" %}
28
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
29
+ {% elif mode == "LeakyRelu" %}
30
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
31
+ {% elif mode == "Sigmoid" %}
32
+ return 1.0 / (1.0 + exp(-v));
33
+ {% elif mode == "Tanh" %}
34
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
35
+ // the builtin preserves that saturated result for larger accumulators.
36
+ return tanh(clamp(v, -10.0, 10.0));
37
+ {% elif mode == "HardSigmoid" %}
38
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
39
+ {% else %}
40
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
41
+ {% endif %}
42
+ {%- endmacro -%}
43
+ fn fused_act(v: f32) -> f32 {
44
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
45
  }
46
  {% endif %}
47
 
 
68
  // implicitIm2col leaves B unmaterialized. While staging a tile, the kernel
69
  // decodes each (k, n) column-matrix coordinate into a raw NCHW input address.
70
  // Overlapping windows may reread input values, trading address arithmetic and
71
+ // repeated input loads for writing and rereading an expanded column matrix.
72
  {% set implicitIm2col = implicitIm2col is defined and implicitIm2col %}
73
  {% set fusedNarrowProjection = fusedNarrowProjection is defined and fusedNarrowProjection %}
74
+ {% set projectionChannels = projectionOutChannels | default(0) %}
75
+ {% set projectionInputAct = inputActivation | default("none") %}
76
+ {% set projectionOutputAct = outputActivation | default("none") %}
 
 
 
 
77
  {% set narrowProjectionTile = "tileB" if GEMM_BK >= GEMM_BM else "projectionTile" %}
78
  {% set splitKValue = splitK if splitK is defined else 1 %}
79
  {% set splitKPartial = splitKValue > 1 %}
80
+ {% set kTiles = kTiles | default(0) %}
81
+ {% set mPadded = mPadded | default(0) %}
82
+ {% set nPadded = nPadded | default(0) %}
83
+ {% set batchCount = batchCount | default(0) %}
84
  {% set gemmKLimit = "kEnd" if splitKPartial else "K" %}
85
  {% set implicitGatherMode = tunables.IMPLICIT_TILED_GATHER_MODE if tunables.IMPLICIT_TILED_GATHER_MODE is defined else 0 %}
86
  {% set implicitGatherWgSize = GEMM_WG_X * GEMM_WG_Y %}
 
109
  {% if splitKPartial %}
110
  // Split-K publishes raw f32 partials. A second pass owns the bias, optional
111
  // epilogue, output cast, and logical-shape store.
112
+ const SPLIT_K: u32 = {{ splitKValue }}u;
113
  const K_TILES: u32 = {{ kTiles }}u;
114
  const PARTIAL_ROWS: u32 = {{ mPadded }}u;
115
  const PARTIAL_COLS: u32 = {{ nPadded }}u;
 
118
  {% endif %}
119
 
120
  {% if fusedNarrowProjection %}
121
+ {% set inputActivation = inputActivation | default("none") %}
122
+ {% set outputActivation = outputActivation | default("none") %}
123
+ {% if inputActivation == "relu" or outputActivation == "relu" %}
124
+ fn is_nan_f32(value: f32) -> bool {
125
+ let bits = bitcast<u32>(value);
126
+ return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
127
+ }
128
+
129
+ fn projection_relu(value: f32) -> f32 {
130
+ var out = max(value, 0.0);
131
+ if (is_nan_f32(value)) {
132
+ out = value;
133
+ }
134
+ return out;
135
+ }
136
+ {% endif %}
137
+ {% if outputActivation == "sigmoid" %}
138
+ fn sigmoid_safe(x: f32) -> f32 {
139
+ if (x >= 0.0) {
140
+ let z = exp(-x);
141
+ return 1.0 / (1.0 + z);
142
+ }
143
+ let z = exp(x);
144
+ return z / (1.0 + z);
145
+ }
146
+ {% endif %}
147
 
148
  const PROJECTION_OUT_C: u32 = {{ projectionChannels }}u;
149
+ {% else %}{% set emitConvStoreOut = not splitKPartial %}{% set hasZ = hasZ is defined and hasZ %}
150
+ {% if emitConvStoreOut | default(true) %}fn store_out(m: u32, n: u32, yBase: u32, raw: f32) {
151
  if (m >= params.M || n >= params.N) {
152
  return;
153
  }
 
155
  {% if hasBias %}
156
  v = v + f32(bias[m]);
157
  {% endif %}
158
+ {% if hasZ %}
159
+ v = v + f32(zResidual[yBase + m * params.N + n]);
160
+ {% endif %}
161
  {% if hasActivation %}
162
  v = fused_act(v);
163
  {% endif %}
 
239
  let bk = {{ rowBase }} + br;
240
  var bvec = vec4<{{ tileT }}>({{ tileT }}(0.0));
241
  if (bk < {{ rowLimit }}) {
242
+ // k flattens the input-channel and spatial-tap axes; n is the output position.
243
  let ic = bk / CONV_KSIZE;
244
  let kq = bk % CONV_KSIZE;
245
  let kh = kq / CONV_KERNEL_W;
 
370
  {% endif %}
371
  let li = lid.y * WG_X + lid.x;
372
 
373
+ // The statically named accumulator vectors form TM independent FMA chains.
 
374
  {% for row in range(GEMM_TM) %}
375
  var acc{{ row }} = vec{{ GEMM_TN }}<f32>(0.0);
376
  {% endfor %}
 
462
 
463
  let projectionYBase = batch * PROJECTION_OUT_C * N;
464
  {% for oc in range(projectionChannels) %}
465
+ let projected{{ oc }} = projectionAcc{{ oc }};
466
  {% endfor %}
467
  {% for column in range(4) %}
468
  let projectionN{{ column }} = projectionNBase + {{ column }}u;
 
475
  {% else %}
476
  let activated{{ oc }}_{{ column }} = projected{{ oc }}.{{ components[column] }};
477
  {% endif %}
 
 
 
478
  y[projectionYBase + {{ oc }}u * N + projectionN{{ column }}] = activated{{ oc }}_{{ column }};
 
479
  {% endfor %}
480
  }
481
  {% endfor %}
build/webgpu/conv-1x1-gemm-tiled.wgsl.jinja CHANGED
@@ -1,29 +1,39 @@
1
- // 1x1 Conv routed as a batched GEMM: Y[b] (M x N) = W (M x K) * X[b] (K x N)
2
- // with M = outChannels, K = inChannels, N = H*W. For a 1x1 kernel with
3
- // group == 1, stride 1, no padding, and dilation 1, the NCHW input is already
4
- // the [K, N] matrix and the OIHW weight is already the [M, K] matrix — no
5
- // data movement, only index arithmetic.
6
- //
7
- // Register-blocked 32x32 shared-memory tile (16x16 threads, each computes a 2x2
8
- // micro-tile, with a selectable K tile (16 by default), the same structure as
9
- // the general tiled matmul path — each shared element feeds 2 FMAs
10
- // and a 32-wide column tile is staged once instead of re-reading W per output
11
- // column. The weight matrix (A) is shared across the batch (no batch stride);
12
- // the input (B) and output carry the batch offset via dispatch.z. f32
13
- // accumulation, cast to the output element type on store; the optional bias is
14
- // per output channel (M row), added after the full K accumulation per ONNX Conv
15
- // semantics. f16 operands stay packed in workgroup memory and widen only when
16
- // consumed. Fully bounds-checked (any M/N/K).
17
- {% if usesF16 %}
18
- enable f16;
19
- {% endif %}
20
  {{ env.wgsl.resourceDeclarations }}
21
  {% set hasActivation = hasActivation is defined and hasActivation %}
 
 
 
22
  {% if hasActivation %}
23
  // Apply the fused activation in the f32 accumulator before the single output
24
  // cast, avoiding an intermediate convolution tensor.
25
- fn fused_act(v: f32) -> f32 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
 
 
 
 
27
  }
28
  {% endif %}
29
 
@@ -32,8 +42,9 @@ const BK: u32 = 16u;
32
  const BM: u32 = 32u;
33
  const BN: u32 = 32u;
34
 
35
- // Store one output element with the optional bias/residual/activation epilogue in the
36
- // f32 accumulator domain: Y = activation(conv + bias + Z).
 
37
  fn store_out(m: u32, n: u32, yBase: u32, raw: f32) {
38
  if (m >= params.M || n >= params.N) {
39
  return;
@@ -42,6 +53,9 @@ fn store_out(m: u32, n: u32, yBase: u32, raw: f32) {
42
  {% if hasBias %}
43
  v = v + f32(bias[m]);
44
  {% endif %}
 
 
 
45
  {% if hasActivation %}
46
  v = fused_act(v);
47
  {% endif %}
 
1
+ // Tiled batched matrix product C[b] (M x N) = A (M x K) * B[b] (K x N).
2
+ // A 16x16 workgroup computes a 32x32 output tile with a 2x2 micro-tile per
3
+ // thread and a 16-element K tile. A is shared across the batch; B and C use the
4
+ // dispatch.z batch offset. Accumulation is f32 and is cast to the output type on
5
+ // store. A configured epilogue may add bias, residual, and activation terms.
6
+ // f16 operands remain packed in workgroup memory and widen when consumed. All
7
+ // M, N, and K accesses are bounds-checked.
 
 
 
 
 
 
 
 
 
 
 
 
8
  {{ env.wgsl.resourceDeclarations }}
9
  {% set hasActivation = hasActivation is defined and hasActivation %}
10
+ {% set activation = activation | default("") %}
11
+ {% set actAlpha = actAlpha | default(0.0) %}
12
+ {% set actBeta = actBeta | default(0.0) %}
13
  {% if hasActivation %}
14
  // Apply the fused activation in the f32 accumulator before the single output
15
  // cast, avoiding an intermediate convolution tensor.
16
+ {% macro fused_act_return(mode, alpha, beta) -%}
17
+ {% if mode == "Relu" %}
18
+ return max(v, 0.0);
19
+ {% elif mode == "Clip" %}
20
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
21
+ {% elif mode == "LeakyRelu" %}
22
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
23
+ {% elif mode == "Sigmoid" %}
24
+ return 1.0 / (1.0 + exp(-v));
25
+ {% elif mode == "Tanh" %}
26
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
27
+ // the builtin preserves that saturated result for larger accumulators.
28
+ return tanh(clamp(v, -10.0, 10.0));
29
+ {% elif mode == "HardSigmoid" %}
30
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
31
+ {% else %}
32
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
33
+ {% endif %}
34
+ {%- endmacro -%}
35
+ fn fused_act(v: f32) -> f32 {
36
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
37
  }
38
  {% endif %}
39
 
 
42
  const BM: u32 = 32u;
43
  const BN: u32 = 32u;
44
 
45
+ // Store one output element with the configured bias/residual/activation
46
+ // epilogue in the f32 accumulator domain.
47
+ {% set hasZ = hasZ is defined and hasZ %}
48
  fn store_out(m: u32, n: u32, yBase: u32, raw: f32) {
49
  if (m >= params.M || n >= params.N) {
50
  return;
 
53
  {% if hasBias %}
54
  v = v + f32(bias[m]);
55
  {% endif %}
56
+ {% if hasZ %}
57
+ v = v + f32(zResidual[yBase + m * params.N + n]);
58
+ {% endif %}
59
  {% if hasActivation %}
60
  v = fused_act(v);
61
  {% endif %}
build/webgpu/conv-1x1-subgroup-matrix.wgsl.jinja CHANGED
@@ -1,6 +1,7 @@
1
- // Convolution as subgroup-matrix GEMM. OIHW weights form row-major [M,K]; `xm`
2
- // is either an NCHW 1x1 view [K,N] or a materialized im2col matrix. Bias is
3
- // applied per output channel after the full f32 accumulation.
 
4
  //
5
  // Full 8x8 matrix loads require complete K/N tiles. The aligned path enforces
6
  // that geometry; `padded` zero-fills tails, and `implicitIm2col` gathers and
@@ -10,6 +11,7 @@
10
  // sums to tile-padded [splitK,batch,M_PAD,N_PAD] scratch; a later pass combines
11
  // the slices and applies bias or an epilogue. Padded tail cells remain zero and
12
  // are never copied to the logical output.
 
13
  {% set fusedNarrowProjection = fusedNarrowProjection if fusedNarrowProjection is defined else false %}
14
  {% set polyphase = polyphaseConvTranspose is defined and polyphaseConvTranspose %}
15
  {% set implicit = implicitIm2col is defined and implicitIm2col %}
@@ -18,9 +20,32 @@
18
  {% set splitKPartial = splitKValue > 1 %}
19
  {% set kLoopVar = "K_LOOP" if padded else "K" %}
20
  {% set nColsVar = "N_COLS" if padded else "N" %}
21
- {% if usesF16 %}
22
- enable f16;
23
- {% endif %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  enable subgroups;
25
  {% if pinSubgroupSize32 %}
26
  enable subgroup_size_control;
@@ -28,7 +53,35 @@ enable subgroup_size_control;
28
  enable chromium_experimental_subgroup_matrix;
29
  diagnostic(off, chromium.subgroup_matrix_uniformity);
30
 
 
31
  {{ env.wgsl.resourceDeclarations }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  {% set operandScalar = fScalar %}
34
  {% set accScalar = "f32" %}
@@ -92,7 +145,7 @@ const B_BATCH_STRIDE: u32 = {{ kLoopVar }} * {{ nColsVar }};
92
  const C_BATCH_STRIDE: u32 = M * N;
93
  {% endif %}
94
  {% if splitKPartial %}
95
- const SPLIT_K: u32 = {{ splitK }}u;
96
  // Whole 32-wide K tiles per slice, rounded up, with the last slice clamped to
97
  // K_LOOP. This avoids padding K to a multiple of SPLIT_K times the tile width.
98
  // A slice entirely past K_LOOP runs zero iterations and stores zero
@@ -108,12 +161,15 @@ const TILE_K: u32 = 32u;
108
  const SUB_COLS: u32 = {{ subColsValue }}u;
109
  const SUB_ROWS: u32 = {{ subRowsValue }}u;
110
 
 
111
  var<workgroup> tile_A: array<{{ operandScalar }}, {{ tileRowsValue }} * 32>;
112
  var<workgroup> tile_B: array<{{ operandScalar }}, {{ tileColsValue }} * 32>;
 
113
  {% if (not useDirectMatrixStore or padded) and not splitKPartial and not fusedNarrowProjection %}
114
  var<workgroup> scratch: array<array<array<{{ accScalar }}, 64>, 4>, {{ (workgroupThreadsValue / 32)|int }}>;
115
 
116
  {% endif %}
 
117
  fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
118
  let a_global = tile_base + row;
119
  let col = c_idx * 8u;
@@ -270,12 +326,35 @@ fn loadSHMB(b_base: u32, tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
270
  {% endif %}
271
  }
272
 
 
273
  {% set hasActivation = hasActivation is defined and hasActivation %}
 
 
 
274
  {% if hasActivation %}
275
  // Apply the fused activation in the f32 accumulator before the single output
276
  // cast, avoiding an intermediate convolution tensor.
277
- fn fused_act(v: f32) -> f32 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
 
 
 
 
279
  }
280
  {% endif %}
281
 
@@ -283,8 +362,11 @@ fn fused_act(v: f32) -> f32 {
283
  {% if hasActivation or hasZ %}
284
  // Fused epilogue: Y = activation(conv + bias + Z), applied at the output store.
285
  // `raw` is the f32 convolution-plus-bias accumulator; Z shares Y's NCHW layout.
286
- fn epi(raw: f32) -> {{ T }} {
287
  var r = raw;
 
 
 
288
  {% if hasActivation %}
289
  r = fused_act(r);
290
  {% endif %}
@@ -292,7 +374,7 @@ fn epi(raw: f32) -> {{ T }} {
292
  }
293
  {% endif %}
294
  {% macro store_val(valExpr, idxExpr) %}
295
- {% if hasActivation or hasZ %}epi({{ valExpr }})
296
  {%- else %}{{ T }}({{ valExpr }})
297
  {%- endif %}
298
  {% endmacro %}
@@ -377,20 +459,23 @@ fn main(
377
  {% else %}
378
  for (var kidx = 0u; kidx < {{ kLoopVar }}; kidx = kidx + TILE_K) {
379
  {% endif %}
 
380
  loadSHMA(a_global_base, kidx, local_idx / 4u, local_idx % 4u);
381
  loadSHMB(b_base, b_global_base, kidx, local_idx / {{ bKChunks }}u, local_idx % {{ bKChunks }}u);
382
  workgroupBarrier();
 
383
 
384
  for (var step = 0u; step < TILE_K; step = step + 8u) {
385
- let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + step;
386
- var matA0: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset, TILE_K);
387
- var matA1: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset + 8u * TILE_K, TILE_K);
 
 
388
 
389
- let matrix_b_offset = subtile_idx * SUB_COLS * TILE_K + step;
390
- var matB0: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset, TILE_K);
391
- var matB1: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 8u * TILE_K, TILE_K);
392
- var matB2: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 16u * TILE_K, TILE_K);
393
- var matB3: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 24u * TILE_K, TILE_K);
394
 
395
  matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
396
  matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
@@ -401,7 +486,9 @@ fn main(
401
  matC12 = subgroupMatrixMultiplyAccumulate(matA1, matB2, matC12);
402
  matC13 = subgroupMatrixMultiplyAccumulate(matA1, matB3, matC13);
403
  }
 
404
  workgroupBarrier();
 
405
  }
406
 
407
  {% if fusedNarrowProjection %}
@@ -431,7 +518,11 @@ fn main(
431
  {% endfor %}
432
  for (var channel = 0u; channel < M; channel = channel + 1u) {
433
  let producer_value = tile_B[channel * TILE_COLS + local_idx]{% if hasBias %} + f32(bias[channel]){% endif %};
 
 
 
434
  let activated_value = producer_value;
 
435
  {% for oc in range(projectionOutChannels) %}
436
  projected_acc{{ oc }} = projected_acc{{ oc }} + activated_value * f32(projectionW[{{ oc }}u * M + channel]);
437
  {% endfor %}
@@ -439,9 +530,15 @@ fn main(
439
 
440
  let fused_y_base = batch * {{ projectionOutChannels }}u * N + fused_global_col;
441
  {% for oc in range(projectionOutChannels) %}
442
- let projected{{ oc }} = projected_acc{{ oc }};
 
 
 
 
 
443
  let activated{{ oc }} = projected{{ oc }};
444
- y[fused_y_base + {{ oc }}u * N] = activated{{ oc }};
 
445
  {% endfor %}
446
  }
447
  {% elif splitKPartial %}
@@ -490,7 +587,7 @@ fn main(
490
  raw += f32(bias[global_row]);
491
  {% endif %}
492
  {% if hasActivation or hasZ %}
493
- y[y_index] = epi(raw);
494
  {% else %}
495
  y[y_index] = {{ T }}(raw);
496
  {% endif %}
 
1
+ // Batched subgroup-matrix product C[b] (M x N) = A (M x K) * B[b] (K x N).
2
+ // A is row-major; B may be a direct matrix view, a materialized column matrix,
3
+ // or an on-demand gather. Accumulation is f32. A configured epilogue may apply
4
+ // bias or other output transforms after the full K reduction.
5
  //
6
  // Full 8x8 matrix loads require complete K/N tiles. The aligned path enforces
7
  // that geometry; `padded` zero-fills tails, and `implicitIm2col` gathers and
 
11
  // sums to tile-padded [splitK,batch,M_PAD,N_PAD] scratch; a later pass combines
12
  // the slices and applies bias or an epilogue. Padded tail cells remain zero and
13
  // are never copied to the logical output.
14
+ {% set directMatrixInputs = directMatrixInputs is defined and directMatrixInputs %}
15
  {% set fusedNarrowProjection = fusedNarrowProjection if fusedNarrowProjection is defined else false %}
16
  {% set polyphase = polyphaseConvTranspose is defined and polyphaseConvTranspose %}
17
  {% set implicit = implicitIm2col is defined and implicitIm2col %}
 
20
  {% set splitKPartial = splitKValue > 1 %}
21
  {% set kLoopVar = "K_LOOP" if padded else "K" %}
22
  {% set nColsVar = "N_COLS" if padded else "N" %}
23
+ {% set hasZ = hasZ is defined and hasZ %}
24
+ {% set projectionOutChannels = projectionOutChannels | default(0) %}
25
+ {% set hasProjectionBias = hasProjectionBias is defined and hasProjectionBias %}
26
+ {% set hasOutputScale = hasOutputScale is defined and hasOutputScale %}
27
+ {% set convKernelH = convKernelH | default(0) %}
28
+ {% set convKernelW = convKernelW | default(0) %}
29
+ {% set convStrideH = convStrideH | default(0) %}
30
+ {% set convStrideW = convStrideW | default(0) %}
31
+ {% set convDilationH = convDilationH | default(0) %}
32
+ {% set convDilationW = convDilationW | default(0) %}
33
+ {% set convPadTop = convPadTop | default(0) %}
34
+ {% set convPadLeft = convPadLeft | default(0) %}
35
+ {% set convInH = convInH | default(0) %}
36
+ {% set convInW = convInW | default(0) %}
37
+ {% set convOutW = convOutW | default(0) %}
38
+ {% set convInChannels = convInChannels | default(0) %}
39
+ {% set convKernelD = convKernelD | default(0) %}
40
+ {% set convStrideD = convStrideD | default(0) %}
41
+ {% set convDilationD = convDilationD | default(0) %}
42
+ {% set convPadFront = convPadFront | default(0) %}
43
+ {% set convInD = convInD | default(0) %}
44
+ {% set convOutH = convOutH | default(0) %}
45
+ {% set nPadded = nPadded | default(0) %}
46
+ {% set mPadded = mPadded | default(0) %}
47
+ {% set kChunk = kChunk | default(0) %}
48
+ {% set batchCount = batchCount | default(0) %}
49
  enable subgroups;
50
  {% if pinSubgroupSize32 %}
51
  enable subgroup_size_control;
 
53
  enable chromium_experimental_subgroup_matrix;
54
  diagnostic(off, chromium.subgroup_matrix_uniformity);
55
 
56
+
57
  {{ env.wgsl.resourceDeclarations }}
58
+ {% if fusedNarrowProjection %}{% set inputActivation = inputActivation | default("none") %}
59
+ {% set outputActivation = outputActivation | default("none") %}
60
+ {% if inputActivation == "relu" or outputActivation == "relu" %}
61
+ fn is_nan_f32(value: f32) -> bool {
62
+ let bits = bitcast<u32>(value);
63
+ return (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0u;
64
+ }
65
+
66
+ fn projection_relu(value: f32) -> f32 {
67
+ var out = max(value, 0.0);
68
+ if (is_nan_f32(value)) {
69
+ out = value;
70
+ }
71
+ return out;
72
+ }
73
+ {% endif %}
74
+ {% if outputActivation == "sigmoid" %}
75
+ fn sigmoid_safe(x: f32) -> f32 {
76
+ if (x >= 0.0) {
77
+ let z = exp(-x);
78
+ return 1.0 / (1.0 + z);
79
+ }
80
+ let z = exp(x);
81
+ return z / (1.0 + z);
82
+ }
83
+ {% endif %}
84
+ {% endif %}
85
 
86
  {% set operandScalar = fScalar %}
87
  {% set accScalar = "f32" %}
 
145
  const C_BATCH_STRIDE: u32 = M * N;
146
  {% endif %}
147
  {% if splitKPartial %}
148
+ const SPLIT_K: u32 = {{ splitKValue }}u;
149
  // Whole 32-wide K tiles per slice, rounded up, with the last slice clamped to
150
  // K_LOOP. This avoids padding K to a multiple of SPLIT_K times the tile width.
151
  // A slice entirely past K_LOOP runs zero iterations and stores zero
 
161
  const SUB_COLS: u32 = {{ subColsValue }}u;
162
  const SUB_ROWS: u32 = {{ subRowsValue }}u;
163
 
164
+ {% if not directMatrixInputs %}
165
  var<workgroup> tile_A: array<{{ operandScalar }}, {{ tileRowsValue }} * 32>;
166
  var<workgroup> tile_B: array<{{ operandScalar }}, {{ tileColsValue }} * 32>;
167
+ {% endif %}
168
  {% if (not useDirectMatrixStore or padded) and not splitKPartial and not fusedNarrowProjection %}
169
  var<workgroup> scratch: array<array<array<{{ accScalar }}, 64>, 4>, {{ (workgroupThreadsValue / 32)|int }}>;
170
 
171
  {% endif %}
172
+ {% if not directMatrixInputs %}
173
  fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
174
  let a_global = tile_base + row;
175
  let col = c_idx * 8u;
 
326
  {% endif %}
327
  }
328
 
329
+ {% endif %}
330
  {% set hasActivation = hasActivation is defined and hasActivation %}
331
+ {% set activation = activation | default("") %}
332
+ {% set actAlpha = actAlpha | default(0.0) %}
333
+ {% set actBeta = actBeta | default(0.0) %}
334
  {% if hasActivation %}
335
  // Apply the fused activation in the f32 accumulator before the single output
336
  // cast, avoiding an intermediate convolution tensor.
337
+ {% macro fused_act_return(mode, alpha, beta) -%}
338
+ {% if mode == "Relu" %}
339
+ return max(v, 0.0);
340
+ {% elif mode == "Clip" %}
341
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
342
+ {% elif mode == "LeakyRelu" %}
343
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
344
+ {% elif mode == "Sigmoid" %}
345
+ return 1.0 / (1.0 + exp(-v));
346
+ {% elif mode == "Tanh" %}
347
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
348
+ // the builtin preserves that saturated result for larger accumulators.
349
+ return tanh(clamp(v, -10.0, 10.0));
350
+ {% elif mode == "HardSigmoid" %}
351
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
352
+ {% else %}
353
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
354
+ {% endif %}
355
+ {%- endmacro -%}
356
+ fn fused_act(v: f32) -> f32 {
357
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
358
  }
359
  {% endif %}
360
 
 
362
  {% if hasActivation or hasZ %}
363
  // Fused epilogue: Y = activation(conv + bias + Z), applied at the output store.
364
  // `raw` is the f32 convolution-plus-bias accumulator; Z shares Y's NCHW layout.
365
+ fn epi(raw: f32{% if hasZ %}, yIndex: u32{% endif %}) -> {{ T }} {
366
  var r = raw;
367
+ {% if hasZ %}
368
+ r = r + f32(zResidual[yIndex]);
369
+ {% endif %}
370
  {% if hasActivation %}
371
  r = fused_act(r);
372
  {% endif %}
 
374
  }
375
  {% endif %}
376
  {% macro store_val(valExpr, idxExpr) %}
377
+ {% if hasActivation or hasZ %}epi({{ valExpr }}{% if hasZ %}, {{ idxExpr }}{% endif %})
378
  {%- else %}{{ T }}({{ valExpr }})
379
  {%- endif %}
380
  {% endmacro %}
 
459
  {% else %}
460
  for (var kidx = 0u; kidx < {{ kLoopVar }}; kidx = kidx + TILE_K) {
461
  {% endif %}
462
+ {% if not directMatrixInputs %}
463
  loadSHMA(a_global_base, kidx, local_idx / 4u, local_idx % 4u);
464
  loadSHMB(b_base, b_global_base, kidx, local_idx / {{ bKChunks }}u, local_idx % {{ bKChunks }}u);
465
  workgroupBarrier();
466
+ {% endif %}
467
 
468
  for (var step = 0u; step < TILE_K; step = step + 8u) {
469
+ {% set directInputs = directMatrixInputs is defined and directMatrixInputs %}
470
+ let matrix_a_offset = {% if directInputs %}(a_global_base + subtile_idy * SUB_ROWS) * K + kidx + step{% else %}subtile_idy * SUB_ROWS * TILE_K + step{% endif %};
471
+ {% for r in range(2) %}
472
+ var matA{{ r }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&{{ "w" if directInputs else "tile_A" }}, matrix_a_offset{% if r > 0 %} + 8u * {{ "K" if directInputs else "TILE_K" }}{% endif %}, {{ "K" if directInputs else "TILE_K" }});
473
+ {% endfor %}
474
 
475
+ let matrix_b_offset = {% if directInputs %}b_base + (kidx + step) * N + b_global_base + subtile_idx * SUB_COLS{% else %}subtile_idx * SUB_COLS * TILE_K + step{% endif %};
476
+ {% for c in range(4) %}
477
+ var matB{{ c }}: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, {% if directInputs %}row_major{% else %}col_major{% endif %}>(&{{ "xm" if directInputs else "tile_B" }}, matrix_b_offset{% if c > 0 %} + {{ c * 8 }}u{% if not directInputs %} * TILE_K{% endif %}{% endif %}, {{ "N" if directInputs else "TILE_K" }});
478
+ {% endfor %}
 
479
 
480
  matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
481
  matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
 
486
  matC12 = subgroupMatrixMultiplyAccumulate(matA1, matB2, matC12);
487
  matC13 = subgroupMatrixMultiplyAccumulate(matA1, matB3, matC13);
488
  }
489
+ {% if not directMatrixInputs %}
490
  workgroupBarrier();
491
+ {% endif %}
492
  }
493
 
494
  {% if fusedNarrowProjection %}
 
518
  {% endfor %}
519
  for (var channel = 0u; channel < M; channel = channel + 1u) {
520
  let producer_value = tile_B[channel * TILE_COLS + local_idx]{% if hasBias %} + f32(bias[channel]){% endif %};
521
+ {% if inputActivation == "relu" %}
522
+ let activated_value = projection_relu(producer_value);
523
+ {% else %}
524
  let activated_value = producer_value;
525
+ {% endif %}
526
  {% for oc in range(projectionOutChannels) %}
527
  projected_acc{{ oc }} = projected_acc{{ oc }} + activated_value * f32(projectionW[{{ oc }}u * M + channel]);
528
  {% endfor %}
 
530
 
531
  let fused_y_base = batch * {{ projectionOutChannels }}u * N + fused_global_col;
532
  {% for oc in range(projectionOutChannels) %}
533
+ let projected{{ oc }} = projected_acc{{ oc }}{% if hasProjectionBias %} + f32(projectionBias[{{ oc }}u]){% endif %};
534
+ {% if outputActivation == "relu" %}
535
+ let activated{{ oc }} = projection_relu(projected{{ oc }});
536
+ {% elif outputActivation == "sigmoid" %}
537
+ let activated{{ oc }} = sigmoid_safe(projected{{ oc }});
538
+ {% else %}
539
  let activated{{ oc }} = projected{{ oc }};
540
+ {% endif %}
541
+ y[fused_y_base + {{ oc }}u * N] = activated{{ oc }}{% if hasOutputScale %} * params.outputScale{% endif %};
542
  {% endfor %}
543
  }
544
  {% elif splitKPartial %}
 
587
  raw += f32(bias[global_row]);
588
  {% endif %}
589
  {% if hasActivation or hasZ %}
590
+ y[y_index] = epi(raw{% if hasZ %}, y_index{% endif %});
591
  {% else %}
592
  y[y_index] = {{ T }}(raw);
593
  {% endif %}
build/webgpu/conv-direct-nd.wgsl.jinja CHANGED
@@ -1,36 +1,58 @@
1
- // Direct {{ source.spatialRank }}D convolution for channels-first tensors. Inputs are
2
  // accumulated in f32 and narrowed once at the output.
3
  // A fused epilogue may apply a residual input and activation before the store.
4
- {% if source.usesF16 %}
5
  enable f16;
6
  {% endif %}
7
  {{ env.wgsl.resourceDeclarations }}
8
  {% set hasActivation = hasActivation is defined and hasActivation %}
 
 
 
9
  {% if hasActivation %}
10
  // Apply the fused activation in the f32 accumulator before the single output
11
  // cast, avoiding an intermediate convolution tensor.
12
- fn fused_act(v: f32) -> f32 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
 
 
 
 
14
  }
15
  {% endif %}
16
 
 
17
  const WG: u32 = {{ convWorkgroupSize }}u;
18
 
19
  @compute @workgroup_size(WG)
20
- fn main(@builtin(global_invocation_id) gid: vec3<u32>,
21
- @builtin(num_workgroups) nwg: vec3<u32>) {
22
- let index = gid.x + gid.y * nwg.x * WG;
23
  if (index >= params.count) {
24
  return;
25
  }
26
 
27
  let ow = index % params.outW;
28
  var q = index / params.outW;
29
- {% if source.spatialRank >= 2 %}
30
  let oh = q % params.outH;
31
  q /= params.outH;
32
  {% endif %}
33
- {% if source.spatialRank == 3 %}
34
  let od = q % params.outD;
35
  q /= params.outD;
36
  {% endif %}
@@ -39,19 +61,19 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
39
  let group = oc / params.outChannelsPerGroup;
40
 
41
  var acc = 0.0;
42
- {% if source.hasBias %}
43
  acc = f32(bias[oc]);
44
  {% endif %}
45
  for (var ic = 0u; ic < params.weightInChannels; ic += 1u) {
46
  let inputChannel = group * params.inChannelsPerGroup + ic;
47
- {% if source.spatialRank == 3 %}
48
  for (var kd = 0u; kd < params.kernelD; kd += 1u) {
49
  let id = i32(od * params.strideD + kd * params.dilationD) - params.padD;
50
  if (id < 0 || id >= i32(params.inD)) {
51
  continue;
52
  }
53
  {% endif %}
54
- {% if source.spatialRank >= 2 %}
55
  for (var kh = 0u; kh < params.kernelH; kh += 1u) {
56
  let ih = i32(oh * params.strideH + kh * params.dilationH) - params.padH;
57
  if (ih < 0 || ih >= i32(params.inH)) {
@@ -63,10 +85,10 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
63
  if (iw < 0 || iw >= i32(params.inW)) {
64
  continue;
65
  }
66
- {% if source.spatialRank == 1 %}
67
  let xIndex = (batch * params.inChannels + inputChannel) * params.inW + u32(iw);
68
  let wIndex = (oc * params.weightInChannels + ic) * params.kernelW + kw;
69
- {% elif source.spatialRank == 2 %}
70
  let xIndex = ((batch * params.inChannels + inputChannel) * params.inH
71
  + u32(ih)) * params.inW + u32(iw);
72
  let wIndex = ((oc * params.weightInChannels + ic) * params.kernelH
@@ -79,12 +101,16 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
79
  {% endif %}
80
  acc += f32(x[xIndex]) * f32(w[wIndex]);
81
  }
82
- {% if source.spatialRank >= 2 %}
83
  }
84
  {% endif %}
85
- {% if source.spatialRank == 3 %}
86
  }
87
  {% endif %}
88
  }
 
 
 
 
89
  y[index] = {{ T }}({% if hasActivation %}fused_act(acc){% else %}acc{% endif %});
90
  }
 
1
+ // Direct N-dimensional convolution for channels-first tensors. Inputs are
2
  // accumulated in f32 and narrowed once at the output.
3
  // A fused epilogue may apply a residual input and activation before the store.
4
+ {% if usesF16Spec %}
5
  enable f16;
6
  {% endif %}
7
  {{ env.wgsl.resourceDeclarations }}
8
  {% set hasActivation = hasActivation is defined and hasActivation %}
9
+ {% set activation = activation | default("") %}
10
+ {% set actAlpha = actAlpha | default(0.0) %}
11
+ {% set actBeta = actBeta | default(0.0) %}
12
  {% if hasActivation %}
13
  // Apply the fused activation in the f32 accumulator before the single output
14
  // cast, avoiding an intermediate convolution tensor.
15
+ {% macro fused_act_return(mode, alpha, beta) -%}
16
+ {% if mode == "Relu" %}
17
+ return max(v, 0.0);
18
+ {% elif mode == "Clip" %}
19
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
20
+ {% elif mode == "LeakyRelu" %}
21
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
22
+ {% elif mode == "Sigmoid" %}
23
+ return 1.0 / (1.0 + exp(-v));
24
+ {% elif mode == "Tanh" %}
25
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
26
+ // the builtin preserves that saturated result for larger accumulators.
27
+ return tanh(clamp(v, -10.0, 10.0));
28
+ {% elif mode == "HardSigmoid" %}
29
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
30
+ {% else %}
31
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
32
+ {% endif %}
33
+ {%- endmacro -%}
34
+ fn fused_act(v: f32) -> f32 {
35
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
36
  }
37
  {% endif %}
38
 
39
+ {% set hasZ = hasZ is defined and hasZ %}
40
  const WG: u32 = {{ convWorkgroupSize }}u;
41
 
42
  @compute @workgroup_size(WG)
43
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
44
+ let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
 
45
  if (index >= params.count) {
46
  return;
47
  }
48
 
49
  let ow = index % params.outW;
50
  var q = index / params.outW;
51
+ {% if spatialRankSpec >= 2 %}
52
  let oh = q % params.outH;
53
  q /= params.outH;
54
  {% endif %}
55
+ {% if spatialRankSpec == 3 %}
56
  let od = q % params.outD;
57
  q /= params.outD;
58
  {% endif %}
 
61
  let group = oc / params.outChannelsPerGroup;
62
 
63
  var acc = 0.0;
64
+ {% if hasBias %}
65
  acc = f32(bias[oc]);
66
  {% endif %}
67
  for (var ic = 0u; ic < params.weightInChannels; ic += 1u) {
68
  let inputChannel = group * params.inChannelsPerGroup + ic;
69
+ {% if spatialRankSpec == 3 %}
70
  for (var kd = 0u; kd < params.kernelD; kd += 1u) {
71
  let id = i32(od * params.strideD + kd * params.dilationD) - params.padD;
72
  if (id < 0 || id >= i32(params.inD)) {
73
  continue;
74
  }
75
  {% endif %}
76
+ {% if spatialRankSpec >= 2 %}
77
  for (var kh = 0u; kh < params.kernelH; kh += 1u) {
78
  let ih = i32(oh * params.strideH + kh * params.dilationH) - params.padH;
79
  if (ih < 0 || ih >= i32(params.inH)) {
 
85
  if (iw < 0 || iw >= i32(params.inW)) {
86
  continue;
87
  }
88
+ {% if spatialRankSpec == 1 %}
89
  let xIndex = (batch * params.inChannels + inputChannel) * params.inW + u32(iw);
90
  let wIndex = (oc * params.weightInChannels + ic) * params.kernelW + kw;
91
+ {% elif spatialRankSpec == 2 %}
92
  let xIndex = ((batch * params.inChannels + inputChannel) * params.inH
93
  + u32(ih)) * params.inW + u32(iw);
94
  let wIndex = ((oc * params.weightInChannels + ic) * params.kernelH
 
101
  {% endif %}
102
  acc += f32(x[xIndex]) * f32(w[wIndex]);
103
  }
104
+ {% if spatialRankSpec >= 2 %}
105
  }
106
  {% endif %}
107
+ {% if spatialRankSpec == 3 %}
108
  }
109
  {% endif %}
110
  }
111
+ {% if hasZ %}
112
+ // Optional residual epilogue: Y = activation(conv + B + Z); Z shares Y's layout.
113
+ acc = acc + f32(zResidual[index]);
114
+ {% endif %}
115
  y[index] = {{ T }}({% if hasActivation %}fused_act(acc){% else %}acc{% endif %});
116
  }
build/webgpu/conv-direct-unrolled.wgsl.jinja CHANGED
@@ -1,36 +1,55 @@
1
  // Direct NCHW 2D convolution with kernel extent, strides, dilations, and
2
  // top/left pads compiled into the fully unrolled window below.
3
- // One thread per output element, same flat index unpack and the same
4
- // accumulation order (ic outer, then kh, kw ascending) as the
5
- // scalar NCHW fallback, so f32 results are bit-identical to it.
6
  // The kh/kw window offsets, kh*KW+kw weight offsets, and the per-channel
7
  // strides fold to literals; the per-row bounds check is hoisted per kh.
8
- // Supports grouped convolution exactly like the scalar fallback. f16 inputs
9
- // are widened to an f32 accumulator and narrowed once at store.
10
- {% if usesF16 %}
11
- enable f16;
12
- {% endif %}
13
  {{ env.wgsl.resourceDeclarations }}
14
  {% set hasActivation = hasActivation is defined and hasActivation %}
 
 
 
15
  {% if hasActivation %}
16
  // Apply the fused activation in the f32 accumulator before the single output
17
  // cast, avoiding an intermediate convolution tensor.
18
- fn fused_act(v: f32) -> f32 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
 
 
 
 
20
  }
21
  {% endif %}
22
 
23
- const KERNEL_AREA: u32 = {{ source.kernelH * source.kernelW }}u;
24
- const STRIDE_H: u32 = {{ source.strideH }}u;
25
- const STRIDE_W: u32 = {{ source.strideW }}u;
26
- const PAD_TOP: i32 = {{ source.padTop }};
27
- const PAD_LEFT: i32 = {{ source.padLeft }};
 
28
 
29
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
30
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
31
  // 2D-folded flat index: gid.y carries the high bits past the
32
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
33
- let index = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
34
  if (index >= params.count) {
35
  return;
36
  }
@@ -54,16 +73,16 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
54
  acc = f32(bias[oc]);
55
  {% endif %}
56
  for (var ic = 0u; ic < params.weightInChannels; ic = ic + 1u) {
57
- {% for kh in range(source.kernelH) %}
58
  {
59
- let ih = ihBase + {{ kh * source.dilationH }}i;
60
  if (ih >= 0 && ih < i32(params.inH)) {
61
  let xRow = xBase + u32(ih) * params.inW;
62
- {% for kw in range(source.kernelW) %}
63
  {
64
- let iw = iwBase + {{ kw * source.dilationW }}i;
65
  if (iw >= 0 && iw < i32(params.inW)) {
66
- acc = acc + f32(x[xRow + u32(iw)]) * f32(w[wBase + {{ kh * source.kernelW + kw }}u]);
67
  }
68
  }
69
  {% endfor %}
@@ -73,5 +92,9 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
73
  xBase = xBase + xChannelStride;
74
  wBase = wBase + KERNEL_AREA;
75
  }
 
 
 
 
76
  y[index] = {{ T }}({% if hasActivation %}fused_act(acc){% else %}acc{% endif %});
77
  }
 
1
  // Direct NCHW 2D convolution with kernel extent, strides, dilations, and
2
  // top/left pads compiled into the fully unrolled window below.
3
+ // One thread owns each output element and accumulates with input channels outer,
4
+ // then kernel rows and columns in ascending order.
 
5
  // The kh/kw window offsets, kh*KW+kw weight offsets, and the per-channel
6
  // strides fold to literals; the per-row bounds check is hoisted per kh.
7
+ // Group offsets are applied to both input channels and weights. f16 inputs are
8
+ // widened to an f32 accumulator and narrowed once at store.
 
 
 
9
  {{ env.wgsl.resourceDeclarations }}
10
  {% set hasActivation = hasActivation is defined and hasActivation %}
11
+ {% set activation = activation | default("") %}
12
+ {% set actAlpha = actAlpha | default(0.0) %}
13
+ {% set actBeta = actBeta | default(0.0) %}
14
  {% if hasActivation %}
15
  // Apply the fused activation in the f32 accumulator before the single output
16
  // cast, avoiding an intermediate convolution tensor.
17
+ {% macro fused_act_return(mode, alpha, beta) -%}
18
+ {% if mode == "Relu" %}
19
+ return max(v, 0.0);
20
+ {% elif mode == "Clip" %}
21
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
22
+ {% elif mode == "LeakyRelu" %}
23
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
24
+ {% elif mode == "Sigmoid" %}
25
+ return 1.0 / (1.0 + exp(-v));
26
+ {% elif mode == "Tanh" %}
27
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
28
+ // the builtin preserves that saturated result for larger accumulators.
29
+ return tanh(clamp(v, -10.0, 10.0));
30
+ {% elif mode == "HardSigmoid" %}
31
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
32
+ {% else %}
33
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
34
+ {% endif %}
35
+ {%- endmacro -%}
36
+ fn fused_act(v: f32) -> f32 {
37
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
38
  }
39
  {% endif %}
40
 
41
+ {% set hasZ = hasZ is defined and hasZ %}
42
+ const KERNEL_AREA: u32 = {{ kernelHSpec * kernelWSpec }}u;
43
+ const STRIDE_H: u32 = {{ strideHSpec }}u;
44
+ const STRIDE_W: u32 = {{ strideWSpec }}u;
45
+ const PAD_TOP: i32 = {{ padTopSpec }};
46
+ const PAD_LEFT: i32 = {{ padLeftSpec }};
47
 
48
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
49
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
50
  // 2D-folded flat index: gid.y carries the high bits past the
51
+ // per-axis dispatch fold width (outputs > 16.7M elements).
52
+ let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
53
  if (index >= params.count) {
54
  return;
55
  }
 
73
  acc = f32(bias[oc]);
74
  {% endif %}
75
  for (var ic = 0u; ic < params.weightInChannels; ic = ic + 1u) {
76
+ {% for kh in range(kernelHSpec) %}
77
  {
78
+ let ih = ihBase + {{ kh * dilationHSpec }}i;
79
  if (ih >= 0 && ih < i32(params.inH)) {
80
  let xRow = xBase + u32(ih) * params.inW;
81
+ {% for kw in range(kernelWSpec) %}
82
  {
83
+ let iw = iwBase + {{ kw * dilationWSpec }}i;
84
  if (iw >= 0 && iw < i32(params.inW)) {
85
+ acc = acc + f32(x[xRow + u32(iw)]) * f32(w[wBase + {{ kh * kernelWSpec + kw }}u]);
86
  }
87
  }
88
  {% endfor %}
 
92
  xBase = xBase + xChannelStride;
93
  wBase = wBase + KERNEL_AREA;
94
  }
95
+ {% if hasZ %}
96
+ // Optional residual epilogue: Y = activation(conv + B + Z); Z shares Y's layout.
97
+ acc = acc + f32(zResidual[index]);
98
+ {% endif %}
99
  y[index] = {{ T }}({% if hasActivation %}fused_act(acc){% else %}acc{% endif %});
100
  }
build/webgpu/conv-im2col-ncdhw.wgsl.jinja CHANGED
@@ -6,25 +6,22 @@
6
  // Geometry comes from tensor shapes and Conv attributes. Compiling it into
7
  // constants removes invariant division and address work from
8
  // the inner GEMM while retaining the complete rank-5 Conv contract.
9
- {% if usesF16 %}
10
- enable f16;
11
- {% endif %}
12
  {{ env.wgsl.resourceDeclarations }}
13
 
14
- const KERNEL_D: u32 = {{ source.kernelD }}u;
15
- const KERNEL_H: u32 = {{ source.kernelH }}u;
16
- const KERNEL_W: u32 = {{ source.kernelW }}u;
17
  const KERNEL_PLANE: u32 = KERNEL_H * KERNEL_W;
18
  const KERNEL_VOLUME: u32 = KERNEL_D * KERNEL_PLANE;
19
- const STRIDE_D: u32 = {{ source.strideD }}u;
20
- const STRIDE_H: u32 = {{ source.strideH }}u;
21
- const STRIDE_W: u32 = {{ source.strideW }}u;
22
- const DILATION_D: u32 = {{ source.dilationD }}u;
23
- const DILATION_H: u32 = {{ source.dilationH }}u;
24
- const DILATION_W: u32 = {{ source.dilationW }}u;
25
- const PAD_FRONT: i32 = {{ source.padFront }};
26
- const PAD_TOP: i32 = {{ source.padTop }};
27
- const PAD_LEFT: i32 = {{ source.padLeft }};
28
 
29
  @compute @workgroup_size({{ convWorkgroupSize }})
30
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
 
6
  // Geometry comes from tensor shapes and Conv attributes. Compiling it into
7
  // constants removes invariant division and address work from
8
  // the inner GEMM while retaining the complete rank-5 Conv contract.
 
 
 
9
  {{ env.wgsl.resourceDeclarations }}
10
 
11
+ const KERNEL_D: u32 = {{ kernelDSpec }}u;
12
+ const KERNEL_H: u32 = {{ kernelHSpec }}u;
13
+ const KERNEL_W: u32 = {{ kernelWSpec }}u;
14
  const KERNEL_PLANE: u32 = KERNEL_H * KERNEL_W;
15
  const KERNEL_VOLUME: u32 = KERNEL_D * KERNEL_PLANE;
16
+ const STRIDE_D: u32 = {{ strideDSpec }}u;
17
+ const STRIDE_H: u32 = {{ strideHSpec }}u;
18
+ const STRIDE_W: u32 = {{ strideWSpec }}u;
19
+ const DILATION_D: u32 = {{ dilationDSpec }}u;
20
+ const DILATION_H: u32 = {{ dilationHSpec }}u;
21
+ const DILATION_W: u32 = {{ dilationWSpec }}u;
22
+ const PAD_FRONT: i32 = {{ padFrontSpec }};
23
+ const PAD_TOP: i32 = {{ padTopSpec }};
24
+ const PAD_LEFT: i32 = {{ padLeftSpec }};
25
 
26
  @compute @workgroup_size({{ convWorkgroupSize }})
27
  fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
build/webgpu/conv-im2col-nchw.wgsl.jinja CHANGED
@@ -6,20 +6,19 @@
6
  // gid.x grid-strides over positions for coalesced reads and writes; gid.y fixes
7
  // k, allowing its ic/kh/kw decomposition to remain loop-invariant. The f32
8
  // scratch round-trips f16 inputs exactly when the GEMM narrows its tile loads.
9
- {% if usesF16 %}
10
- enable f16;
11
- {% endif %}
12
  {{ env.wgsl.resourceDeclarations }}
13
 
14
- const KERNEL_H: u32 = {{ source.kernelH }}u;
15
- const KERNEL_W: u32 = {{ source.kernelW }}u;
16
  const KSIZE: u32 = KERNEL_H * KERNEL_W;
17
- const STRIDE_H: u32 = {{ source.strideH }}u;
18
- const STRIDE_W: u32 = {{ source.strideW }}u;
19
- const DILATION_H: u32 = {{ source.dilationH }}u;
20
- const DILATION_W: u32 = {{ source.dilationW }}u;
21
- const PAD_TOP: i32 = {{ source.padTop }};
22
- const PAD_LEFT: i32 = {{ source.padLeft }};
23
  {% if padded %}
24
  // Padded materialization for the subgroup-matrix GEMM: the cols buffer is
25
  // K -> ceil(K/32)*32 rows by N -> ceil(N/64)*64 columns, with rows >= kRows and
@@ -64,10 +63,10 @@ fn main(
64
  }
65
  {% if padded %}
66
  }
67
- cols[(batch * K_PADDED + k) * COLS_STRIDE + pos] = value;
68
- {% else %}
69
- cols[(batch * params.kRows + k) * params.outCount + pos] = value;
70
  {% endif %}
 
 
 
71
  pos = pos + step;
72
  }
73
  }
 
6
  // gid.x grid-strides over positions for coalesced reads and writes; gid.y fixes
7
  // k, allowing its ic/kh/kw decomposition to remain loop-invariant. The f32
8
  // scratch round-trips f16 inputs exactly when the GEMM narrows its tile loads.
9
+ // A plan for f16 inputs may retain f16 columns to avoid that widening.
10
+ {% set colsScalar = colsScalar | default("f32") %}
 
11
  {{ env.wgsl.resourceDeclarations }}
12
 
13
+ const KERNEL_H: u32 = {{ kernelHSpec }}u;
14
+ const KERNEL_W: u32 = {{ kernelWSpec }}u;
15
  const KSIZE: u32 = KERNEL_H * KERNEL_W;
16
+ const STRIDE_H: u32 = {{ strideHSpec }}u;
17
+ const STRIDE_W: u32 = {{ strideWSpec }}u;
18
+ const DILATION_H: u32 = {{ dilationHSpec }}u;
19
+ const DILATION_W: u32 = {{ dilationWSpec }}u;
20
+ const PAD_TOP: i32 = {{ padTopSpec }};
21
+ const PAD_LEFT: i32 = {{ padLeftSpec }};
22
  {% if padded %}
23
  // Padded materialization for the subgroup-matrix GEMM: the cols buffer is
24
  // K -> ceil(K/32)*32 rows by N -> ceil(N/64)*64 columns, with rows >= kRows and
 
63
  }
64
  {% if padded %}
65
  }
 
 
 
66
  {% endif %}
67
+ {% set columnRows = "K_PADDED" if padded else "params.kRows" %}
68
+ {% set columnStride = "COLS_STRIDE" if padded else "params.outCount" %}
69
+ cols[(batch * {{ columnRows }} + k) * {{ columnStride }} + pos] = {% if colsScalar == "f16" %}f16(value){% else %}value{% endif %};
70
  pos = pos + step;
71
  }
72
  }
build/webgpu/conv-splitk-reduce.wgsl.jinja CHANGED
@@ -8,15 +8,43 @@
8
  // contents never matter. One output element per thread, walking n fastest so
9
  // consecutive threads touch consecutive addresses in every slice.
10
  //
11
- // Summing the slices in index order gives a DIFFERENT rounding than the
12
- // single-pass kernel's one long K loop the same reassociation any tiled GEMM
13
- // already makes, not a precision change, but it is why split-K variants are
14
- // compared against the op's tolerance and not pinned bit-for-bit to the
15
- // un-split path.
16
- {% if usesF16 %}
17
- enable f16;
18
- {% endif %}
19
  {{ env.wgsl.resourceDeclarations }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  const M: u32 = {{ M }}u;
22
  const N: u32 = {{ N }}u;
@@ -28,10 +56,10 @@ const COUNT: u32 = {{ batchCount }}u * M * N;
28
  const WORKGROUP_SIZE: u32 = {{ reduceWorkgroupSize }}u;
29
 
30
  @compute @workgroup_size({{ reduceWorkgroupSize }}, 1, 1)
31
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
32
  // 2D-folded flat index: gid.y carries the high bits past
33
- // maxComputeWorkgroupsPerDimension and reduces to the 1D form at y=0.
34
- let idx = gid.x + gid.y * nwg.x * WORKGROUP_SIZE;
35
  if (idx >= COUNT) {
36
  return;
37
  }
@@ -47,6 +75,9 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
47
  }
48
  {% if hasBias %}
49
  acc = acc + f32(bias[row]);
 
 
 
50
  {% endif %}
51
  y[idx] = {{ T }}(acc);
52
  }
 
8
  // contents never matter. One output element per thread, walking n fastest so
9
  // consecutive threads touch consecutive addresses in every slice.
10
  //
11
+ // Summing slices in index order changes the f32 association relative to one
12
+ // uninterrupted K loop, so the two orders need not be bit-identical.
 
 
 
 
 
 
13
  {{ env.wgsl.resourceDeclarations }}
14
+ {% set applyActivation = hasActivation is defined and hasActivation %}
15
+ {% if applyActivation %}
16
+ {% set hasActivation = hasActivation is defined and hasActivation %}
17
+ {% set activation = activation | default("") %}
18
+ {% set actAlpha = actAlpha | default(0.0) %}
19
+ {% set actBeta = actBeta | default(0.0) %}
20
+ {% if hasActivation %}
21
+ // Apply the fused activation in the f32 accumulator before the single output
22
+ // cast, avoiding an intermediate convolution tensor.
23
+ {% macro fused_act_return(mode, alpha, beta) -%}
24
+ {% if mode == "Relu" %}
25
+ return max(v, 0.0);
26
+ {% elif mode == "Clip" %}
27
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
28
+ {% elif mode == "LeakyRelu" %}
29
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
30
+ {% elif mode == "Sigmoid" %}
31
+ return 1.0 / (1.0 + exp(-v));
32
+ {% elif mode == "Tanh" %}
33
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
34
+ // the builtin preserves that saturated result for larger accumulators.
35
+ return tanh(clamp(v, -10.0, 10.0));
36
+ {% elif mode == "HardSigmoid" %}
37
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
38
+ {% else %}
39
+ return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
40
+ {% endif %}
41
+ {%- endmacro -%}
42
+ fn fused_act(v: f32) -> f32 {
43
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
44
+ }
45
+ {% endif %}
46
+
47
+ {% endif %}
48
 
49
  const M: u32 = {{ M }}u;
50
  const N: u32 = {{ N }}u;
 
56
  const WORKGROUP_SIZE: u32 = {{ reduceWorkgroupSize }}u;
57
 
58
  @compute @workgroup_size({{ reduceWorkgroupSize }}, 1, 1)
59
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
60
  // 2D-folded flat index: gid.y carries the high bits past
61
+ // the per-axis dispatch fold width and reduces to the 1D form at y=0.
62
+ let idx = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WORKGROUP_SIZE;
63
  if (idx >= COUNT) {
64
  return;
65
  }
 
75
  }
76
  {% if hasBias %}
77
  acc = acc + f32(bias[row]);
78
+ {% endif %}
79
+ {% if applyActivation %}
80
+ acc = fused_act(acc);
81
  {% endif %}
82
  y[idx] = {{ T }}(acc);
83
  }
build/webgpu/conv1d-tiled-reg.wgsl.jinja CHANGED
@@ -1,31 +1,43 @@
1
- // Implicit-GEMM register-tiled 1-D convolution over NCW: Y[b] (M x N) =
2
- // W (M x K) @ im2col(X[b]) (K x N) with M = outChannels, K = inChannels *
3
- // kernelW, N = outW. The im2col operand is never materialized: the X staging
4
- // loop decodes each GEMM row into (ic, kw) and samples x with stride /
5
- // dilation / left-pad applied, so one program covers general stride, padding,
6
- // and dilation (group == 1 only). Workgroup geometry is tunable: a WG_X x WG_Y
7
- // workgroup computes a (WG_Y*TM) x (WG_X*TN) output tile from BK-deep staged
8
- // slices, each thread owning a TM x TN register micro-tile. Cooperative loads
9
- // and stores are fully bounds-checked, so partial tiles on any edge stay
10
- // correct for every tunable setting. f32 accumulation; bias and the optional
11
- // activation epilogue are applied in the accumulator domain at
12
- // store. f16 operands stay packed in workgroup memory and widen at the FMA
13
- // site. Both tiles are indexed by their own output axis and group four K values
14
- // per vector word, so the micro-tile accumulates through dot() and the implicit
15
- // gather decodes one (ic, kw) tap per word, walking the remaining three. The
16
- // micro-tile is unrolled into named registers: a dynamically indexed private
17
- // array of vector words is large enough at the wide-M geometry that it may fail
18
- // to scalarize and spill the accumulators.
19
- {% if usesF16 %}
20
- enable f16;
21
- {% endif %}
22
  {{ env.wgsl.resourceDeclarations }}
23
  {% set hasActivation = hasActivation is defined and hasActivation %}
 
 
 
24
  {% if hasActivation %}
25
  // Apply the fused activation in the f32 accumulator before the single output
26
  // cast, avoiding an intermediate convolution tensor.
27
- fn fused_act(v: f32) -> f32 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
 
 
 
 
29
  }
30
  {% endif %}
31
 
 
1
+ // Register-tiled implicit GEMM for group-1 NCW convolution:
2
+ // Y[b] (M x N) = W (M x K) @ im2col(X[b]) (K x N), where M is outChannels, K
3
+ // is inChannels * kernelW, and N is outW. The X tile is gathered directly from
4
+ // NCW with the configured stride, dilation, and left padding; no column matrix
5
+ // is materialized.
6
+ //
7
+ // A WG_X x WG_Y workgroup computes a (WG_Y * TM) x (WG_X * TN) output tile
8
+ // from BK-deep slices, with one TM x TN register micro-tile per thread. Weight
9
+ // and input tiles group four K values per workgroup-memory word and accumulate
10
+ // through dot(). Bounds checks cover every partial tile. Accumulation, bias,
11
+ // and the optional activation epilogue use f32; f16 operands widen at the FMA.
 
 
 
 
 
 
 
 
 
 
12
  {{ env.wgsl.resourceDeclarations }}
13
  {% set hasActivation = hasActivation is defined and hasActivation %}
14
+ {% set activation = activation | default("") %}
15
+ {% set actAlpha = actAlpha | default(0.0) %}
16
+ {% set actBeta = actBeta | default(0.0) %}
17
  {% if hasActivation %}
18
  // Apply the fused activation in the f32 accumulator before the single output
19
  // cast, avoiding an intermediate convolution tensor.
20
+ {% macro fused_act_return(mode, alpha, beta) -%}
21
+ {% if mode == "Relu" %}
22
+ return max(v, 0.0);
23
+ {% elif mode == "Clip" %}
24
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
25
+ {% elif mode == "LeakyRelu" %}
26
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
27
+ {% elif mode == "Sigmoid" %}
28
+ return 1.0 / (1.0 + exp(-v));
29
+ {% elif mode == "Tanh" %}
30
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
31
+ // the builtin preserves that saturated result for larger accumulators.
32
+ return tanh(clamp(v, -10.0, 10.0));
33
+ {% elif mode == "HardSigmoid" %}
34
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
35
+ {% else %}
36
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
37
+ {% endif %}
38
+ {%- endmacro -%}
39
+ fn fused_act(v: f32) -> f32 {
40
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
41
  }
42
  {% endif %}
43
 
build/webgpu/conv2d-grouped-large-w4.wgsl.jinja CHANGED
@@ -1,13 +1,32 @@
1
- {% if usesF16 %}
2
- enable f16;
3
- {% endif %}
4
  {{ env.wgsl.resourceDeclarations }}
5
  {% set hasActivation = hasActivation is defined and hasActivation %}
 
 
 
6
  {% if hasActivation %}
7
  // Apply the fused activation in the f32 accumulator before the single output
8
  // cast, avoiding an intermediate convolution tensor.
9
- fn fused_act(v: f32) -> f32 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
 
 
 
 
11
  }
12
  {% endif %}
13
 
@@ -15,86 +34,130 @@ fn fused_act(v: f32) -> f32 {
15
  // value for neighboring output columns and input window for neighboring output
16
  // channels. One invocation computes four adjacent columns for OC_TILE channels,
17
  // sharing each input load across those channels. Shape and window geometry are
18
- // static, so all indexing divisors and kernel offsets are strength-reduced and
19
- // unrolled by the shader compiler.
20
- const COUNT_TILES: u32 = {{ source.countTiles }}u;
21
- {% if source.tailOutput %}const OUT_W: u32 = max(1u, {{ source.outW }}u);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  {% endif %}
23
- const OUT_W4: u32 = max(1u, {{ source.outW4 }}u);
24
- const OUT_H: u32 = max(1u, {{ source.outH }}u);
25
- const OUT_C: u32 = max(1u, {{ source.outC }}u);
26
- const OC_TILE: u32 = {{ source.ocTile }}u;
27
  const OUT_C_TILES: u32 = OUT_C / OC_TILE;
28
- const OUT_CPG: u32 = {{ source.outCPerGroup }}u;
29
- const IN_C: u32 = {{ source.inC }}u;
30
- const IN_CPG: u32 = {{ source.inCPerGroup }}u;
31
- const IN_H: i32 = {{ source.inH }};
32
- const IN_W: i32 = {{ source.inW }};
33
- const IN_H_U: u32 = {{ source.inH }}u;
34
- const IN_W_U: u32 = {{ source.inW }}u;
35
  const IN_PLANE: u32 = IN_H_U * IN_W_U;
36
- {% if not source.tailOutput %}const OUT_PLANE4: u32 = OUT_H * OUT_W4;
37
  {% endif %}
38
- const KAREA: u32 = {{ source.kernelH * source.kernelW }}u;
39
- const STRIDE_H: u32 = {{ source.strideH }}u;
40
- const STRIDE_W: i32 = {{ source.strideW }};
41
- const PAD_TOP: i32 = {{ source.padTop }};
42
- const PAD_LEFT: i32 = {{ source.padLeft }};
43
- const WG: u32 = {{ source.workgroupSize }}u;
44
 
45
- @compute @workgroup_size({{ source.workgroupSize }})
46
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
47
- let q = gid.x + gid.y * nwg.x * WG;
48
  if (q >= COUNT_TILES) { return; }
49
 
 
 
 
 
50
  let ow4 = q % OUT_W4;
51
  var t = q / OUT_W4;
 
52
  let oh = t % OUT_H;
53
  t /= OUT_H;
54
  let ocBase = (t % OUT_C_TILES) * OC_TILE;
55
  let batch = t / OUT_C_TILES;
56
  let group = ocBase / OUT_CPG;
57
  let ihBase = i32(oh * STRIDE_H) - PAD_TOP;
 
 
 
 
 
 
58
  let iwBase = i32(ow4 * 4u) * STRIDE_W - PAD_LEFT;
 
59
  var xChannelBase = (batch * IN_C + group * IN_CPG) * IN_PLANE;
60
- {% for oct in range(source.ocTile) %}
61
  var wBase{{ oct }} = (ocBase + {{ oct }}u) * IN_CPG * KAREA;
62
- var acc{{ oct }} = vec4<f32>(0.0);
 
 
 
 
 
 
 
 
 
 
 
63
  {% endfor %}
64
 
65
  for (var ic = 0u; ic < IN_CPG; ic++) {
66
- {% for kh in range(source.kernelH) %}
67
  {
68
- let ih = ihBase + {{ kh * source.dilationH }};
69
  if (ih >= 0 && ih < IN_H) {
70
  let xRow = xChannelBase + u32(ih) * IN_W_U;
71
- {% if source.span <= source.spanCap %}
72
  // The four output columns and the kernel taps overlap heavily: tap kw of
73
  // lane l reads input column kw*dilationW + l*strideW, so kernelW*4 reads
74
  // land on only SPAN distinct columns. Load the row window once and let
75
- // every tap take its value from a register. Out-of-range columns hold
76
- // zero, which contributes nothing to the accumulation exactly as the
77
- // per-tap bounds test did.
78
  //
79
- // SPAN grows as (kernelW-1)*dilationW, so a widely dilated kernel would
80
- // want more registers than a thread has and spill. Past spanCap the
81
- // per-tap form below stays, where the register cost is fixed.
82
- {% for s in range(source.span) %}
83
  {% set used = namespace(value=false) %}
84
- {% for kw in range(source.kernelW) %}
85
- {% for lane in range(4) %}
86
- {% if s == kw * source.dilationW + lane * source.strideW %}{% set used.value = true %}{% endif %}
87
  {% endfor %}
88
  {% endfor %}
89
  {% if used.value %}
90
  var xw{{ s }} = 0.0;
91
  {% endif %}
92
  {% endfor %}
93
- {% for s in range(source.span) %}
94
  {% set used = namespace(value=false) %}
95
- {% for kw in range(source.kernelW) %}
96
- {% for lane in range(4) %}
97
- {% if s == kw * source.dilationW + lane * source.strideW %}{% set used.value = true %}{% endif %}
98
  {% endfor %}
99
  {% endfor %}
100
  {% if used.value %}
@@ -104,31 +167,31 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
104
  }
105
  {% endif %}
106
  {% endfor %}
107
- {% for kw in range(source.kernelW) %}
108
  {
109
- {% for oct in range(source.ocTile) %}
110
- let weight{{ oct }} = f32(w[wBase{{ oct }} + {{ kh * source.kernelW + kw }}u]);
111
  {% endfor %}
112
- {% for lane in range(4) %}
113
- {% for oct in range(source.ocTile) %}
114
- acc{{ oct }}.{{ ["x", "y", "z", "w"][lane] }} += xw{{ kw * source.dilationW + lane * source.strideW }} * weight{{ oct }};
115
  {% endfor %}
116
  {% endfor %}
117
  }
118
  {% endfor %}
119
  {% else %}
120
- {% for kw in range(source.kernelW) %}
121
  {
122
- {% for oct in range(source.ocTile) %}
123
- let weight{{ oct }} = f32(w[wBase{{ oct }} + {{ kh * source.kernelW + kw }}u]);
124
  {% endfor %}
125
- let iwK = iwBase + {{ kw * source.dilationW }};
126
- {% for lane in range(4) %}
127
- let iwLane{{ lane }} = iwK + {{ lane * source.strideW }};
128
  if (iwLane{{ lane }} >= 0 && iwLane{{ lane }} < IN_W) {
129
  let xValue = f32(x[xRow + u32(iwLane{{ lane }})]);
130
- {% for oct in range(source.ocTile) %}
131
- acc{{ oct }}.{{ ["x", "y", "z", "w"][lane] }} += xValue * weight{{ oct }};
132
  {% endfor %}
133
  }
134
  {% endfor %}
@@ -139,13 +202,23 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
139
  }
140
  {% endfor %}
141
  xChannelBase += IN_PLANE;
142
- {% for oct in range(source.ocTile) %}
143
  wBase{{ oct }} += KAREA;
144
  {% endfor %}
145
  }
146
- {% if source.tailOutput %}
 
 
 
 
 
 
 
 
 
 
147
  let outBase = ((batch * OUT_C + ocBase) * OUT_H + oh) * OUT_W + ow4 * 4u;
148
- {% for oct in range(source.ocTile) %}
149
  let outChannelBase{{ oct }} = outBase + {{ oct }}u * OUT_H * OUT_W;
150
  {% for lane in range(4) %}
151
  if (ow4 * 4u + {{ lane }}u < OUT_W) {
@@ -155,7 +228,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
155
  {% endfor %}
156
  {% else %}
157
  let outBase = ((batch * OUT_C + ocBase) * OUT_H + oh) * OUT_W4 + ow4;
158
- {% for oct in range(source.ocTile) %}
159
  y[outBase + {{ oct }}u * OUT_PLANE4] = {{ vectorScalar }}({% if hasActivation %}vec4<f32>(fused_act(acc{{ oct }}.x), fused_act(acc{{ oct }}.y), fused_act(acc{{ oct }}.z), fused_act(acc{{ oct }}.w)){% else %}acc{{ oct }}{% endif %});
160
  {% endfor %}
161
  {% endif %}
 
 
 
 
1
  {{ env.wgsl.resourceDeclarations }}
2
  {% set hasActivation = hasActivation is defined and hasActivation %}
3
+ {% set activation = activation | default("") %}
4
+ {% set actAlpha = actAlpha | default(0.0) %}
5
+ {% set actBeta = actBeta | default(0.0) %}
6
  {% if hasActivation %}
7
  // Apply the fused activation in the f32 accumulator before the single output
8
  // cast, avoiding an intermediate convolution tensor.
9
+ {% macro fused_act_return(mode, alpha, beta) -%}
10
+ {% if mode == "Relu" %}
11
+ return max(v, 0.0);
12
+ {% elif mode == "Clip" %}
13
+ return clamp(v, f32({{ alpha }}), f32({{ beta }}));
14
+ {% elif mode == "LeakyRelu" %}
15
+ return select(v * f32({{ alpha }}), v, v >= 0.0);
16
+ {% elif mode == "Sigmoid" %}
17
+ return 1.0 / (1.0 + exp(-v));
18
+ {% elif mode == "Tanh" %}
19
+ // tanh is already +/-1 to f32 precision at the clamp bounds. Clamping before
20
+ // the builtin preserves that saturated result for larger accumulators.
21
+ return tanh(clamp(v, -10.0, 10.0));
22
+ {% elif mode == "HardSigmoid" %}
23
+ return clamp(f32({{ alpha }}) * v + f32({{ beta }}), 0.0, 1.0);
24
+ {% else %}
25
  return v * clamp(v * 0.16666666666666666 + 0.5, 0.0, 1.0);
26
+ {% endif %}
27
+ {%- endmacro -%}
28
+ fn fused_act(v: f32) -> f32 {
29
+ {{ fused_act_return(activation, actAlpha, actBeta) -}}
30
  }
31
  {% endif %}
32
 
 
34
  // value for neighboring output columns and input window for neighboring output
35
  // channels. One invocation computes four adjacent columns for OC_TILE channels,
36
  // sharing each input load across those channels. Shape and window geometry are
37
+ // static, so indexing divisors and kernel offsets are shader constants.
38
+ {% set dilatedLanes = dilatedLanes | default(false) %}
39
+ {% set lanes = lanes | default(4) %}
40
+ {% set quads = quads | default(1) %}
41
+ {% set laneStep = dilationWSpec if dilatedLanes else strideWSpec %}
42
+ {% set comps = ["x", "y", "z", "w"] %}
43
+ {% macro acc_ref(oct, lane) %}{% if dilatedLanes %}acc{{ oct }}_{{ lane }}{% else %}acc{{ oct }}.{{ comps[lane] }}{% endif %}{% endmacro %}
44
+ {% set usedCount = namespace(value=0) %}
45
+ {% for s in range(span) %}
46
+ {% set used = namespace(value=false) %}
47
+ {% for kw in range(kernelWSpec) %}
48
+ {% for lane in range(lanes) %}
49
+ {% if s == kw * dilationWSpec + lane * laneStep %}{% set used.value = true %}{% endif %}
50
+ {% endfor %}
51
+ {% endfor %}
52
+ {% if used.value %}{% set usedCount.value = usedCount.value + 1 %}{% endif %}
53
+ {% endfor %}
54
+ {% set registerForm = (usedCount.value <= spanCap) if dilatedLanes else (span <= spanCap) %}
55
+ const COUNT_TILES: u32 = {{ countTiles }}u;
56
+ {% if tailOutput %}const OUT_W: u32 = max(1u, {{ outW }}u);
57
+ {% endif %}
58
+ {% if dilatedLanes %}
59
+ const OUT_W: u32 = max(1u, {{ outW }}u);
60
+ const LANES: u32 = {{ lanes }}u;
61
+ const RESIDUES: u32 = {{ dilationWSpec }}u;
62
+ const TILES_PER_ROW: u32 = RESIDUES * {{ quads }}u;
63
+ {% else %}
64
+ const OUT_W4: u32 = max(1u, {{ outW4 }}u);
65
  {% endif %}
66
+ const OUT_H: u32 = max(1u, {{ outH }}u);
67
+ const OUT_C: u32 = max(1u, {{ outC }}u);
68
+ const OC_TILE: u32 = {{ ocTile }}u;
 
69
  const OUT_C_TILES: u32 = OUT_C / OC_TILE;
70
+ const OUT_CPG: u32 = {{ outCPerGroup }}u;
71
+ const IN_C: u32 = {{ inC }}u;
72
+ const IN_CPG: u32 = {{ inCPerGroup }}u;
73
+ const IN_H: i32 = {{ inH }};
74
+ const IN_W: i32 = {{ inW }};
75
+ const IN_H_U: u32 = {{ inH }}u;
76
+ const IN_W_U: u32 = {{ inW }}u;
77
  const IN_PLANE: u32 = IN_H_U * IN_W_U;
78
+ {% if not tailOutput and not dilatedLanes %}const OUT_PLANE4: u32 = OUT_H * OUT_W4;
79
  {% endif %}
80
+ const KAREA: u32 = {{ kernelHSpec * kernelWSpec }}u;
81
+ const STRIDE_H: u32 = {{ strideHSpec }}u;
82
+ const STRIDE_W: i32 = {{ strideWSpec }};
83
+ const PAD_TOP: i32 = {{ padTopSpec }};
84
+ const PAD_LEFT: i32 = {{ padLeftSpec }};
85
+ const WG: u32 = {{ workgroupSizeSpec }}u;
86
 
87
+ @compute @workgroup_size({{ workgroupSizeSpec }})
88
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
89
+ let q = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
90
  if (q >= COUNT_TILES) { return; }
91
 
92
+ {% if dilatedLanes %}
93
+ let tile = q % TILES_PER_ROW;
94
+ var t = q / TILES_PER_ROW;
95
+ {% else %}
96
  let ow4 = q % OUT_W4;
97
  var t = q / OUT_W4;
98
+ {% endif %}
99
  let oh = t % OUT_H;
100
  t /= OUT_H;
101
  let ocBase = (t % OUT_C_TILES) * OC_TILE;
102
  let batch = t / OUT_C_TILES;
103
  let group = ocBase / OUT_CPG;
104
  let ihBase = i32(oh * STRIDE_H) - PAD_TOP;
105
+ {% if dilatedLanes %}
106
+ // Lane l owns output column ow0 + l * dilationW: the residue class first,
107
+ // then the LANES-wide run within it. Runs past the row store nothing.
108
+ let ow0 = tile % RESIDUES + (tile / RESIDUES) * (LANES * RESIDUES);
109
+ let iwBase = i32(ow0) * STRIDE_W - PAD_LEFT;
110
+ {% else %}
111
  let iwBase = i32(ow4 * 4u) * STRIDE_W - PAD_LEFT;
112
+ {% endif %}
113
  var xChannelBase = (batch * IN_C + group * IN_CPG) * IN_PLANE;
114
+ {% for oct in range(ocTile) %}
115
  var wBase{{ oct }} = (ocBase + {{ oct }}u) * IN_CPG * KAREA;
116
+ {% if hasBias %}
117
+ // Seeding the accumulator with the channel bias replaces the zero seed and
118
+ // incorporates the bias before the reduction.
119
+ let bias{{ oct }} = f32(bias[ocBase + {{ oct }}u]);
120
+ {% endif %}
121
+ {% if dilatedLanes %}
122
+ {% for lane in range(lanes) %}
123
+ var acc{{ oct }}_{{ lane }} = {% if hasBias %}bias{{ oct }}{% else %}0.0{% endif %};
124
+ {% endfor %}
125
+ {% else %}
126
+ var acc{{ oct }} = vec4<f32>({% if hasBias %}bias{{ oct }}{% else %}0.0{% endif %});
127
+ {% endif %}
128
  {% endfor %}
129
 
130
  for (var ic = 0u; ic < IN_CPG; ic++) {
131
+ {% for kh in range(kernelHSpec) %}
132
  {
133
+ let ih = ihBase + {{ kh * dilationHSpec }};
134
  if (ih >= 0 && ih < IN_H) {
135
  let xRow = xChannelBase + u32(ih) * IN_W_U;
136
+ {% if registerForm %}
137
  // The four output columns and the kernel taps overlap heavily: tap kw of
138
  // lane l reads input column kw*dilationW + l*strideW, so kernelW*4 reads
139
  // land on only SPAN distinct columns. Load the row window once and let
140
+ // every tap take its value from a statically named local. Out-of-range
141
+ // columns contain zero and contribute nothing to the accumulation.
 
142
  //
143
+ // SPAN grows as (kernelW-1)*dilationW. `spanCap` bounds the number of
144
+ // staged row-window values; wider spans use the fixed-size per-tap form below.
145
+ {% for s in range(span) %}
 
146
  {% set used = namespace(value=false) %}
147
+ {% for kw in range(kernelWSpec) %}
148
+ {% for lane in range(lanes) %}
149
+ {% if s == kw * dilationWSpec + lane * laneStep %}{% set used.value = true %}{% endif %}
150
  {% endfor %}
151
  {% endfor %}
152
  {% if used.value %}
153
  var xw{{ s }} = 0.0;
154
  {% endif %}
155
  {% endfor %}
156
+ {% for s in range(span) %}
157
  {% set used = namespace(value=false) %}
158
+ {% for kw in range(kernelWSpec) %}
159
+ {% for lane in range(lanes) %}
160
+ {% if s == kw * dilationWSpec + lane * laneStep %}{% set used.value = true %}{% endif %}
161
  {% endfor %}
162
  {% endfor %}
163
  {% if used.value %}
 
167
  }
168
  {% endif %}
169
  {% endfor %}
170
+ {% for kw in range(kernelWSpec) %}
171
  {
172
+ {% for oct in range(ocTile) %}
173
+ let weight{{ oct }} = f32(w[wBase{{ oct }} + {{ kh * kernelWSpec + kw }}u]);
174
  {% endfor %}
175
+ {% for lane in range(lanes) %}
176
+ {% for oct in range(ocTile) %}
177
+ {{ acc_ref(oct, lane) }} += xw{{ kw * dilationWSpec + lane * laneStep }} * weight{{ oct }};
178
  {% endfor %}
179
  {% endfor %}
180
  }
181
  {% endfor %}
182
  {% else %}
183
+ {% for kw in range(kernelWSpec) %}
184
  {
185
+ {% for oct in range(ocTile) %}
186
+ let weight{{ oct }} = f32(w[wBase{{ oct }} + {{ kh * kernelWSpec + kw }}u]);
187
  {% endfor %}
188
+ let iwK = iwBase + {{ kw * dilationWSpec }};
189
+ {% for lane in range(lanes) %}
190
+ let iwLane{{ lane }} = iwK + {{ lane * laneStep }};
191
  if (iwLane{{ lane }} >= 0 && iwLane{{ lane }} < IN_W) {
192
  let xValue = f32(x[xRow + u32(iwLane{{ lane }})]);
193
+ {% for oct in range(ocTile) %}
194
+ {{ acc_ref(oct, lane) }} += xValue * weight{{ oct }};
195
  {% endfor %}
196
  }
197
  {% endfor %}
 
202
  }
203
  {% endfor %}
204
  xChannelBase += IN_PLANE;
205
+ {% for oct in range(ocTile) %}
206
  wBase{{ oct }} += KAREA;
207
  {% endfor %}
208
  }
209
+ {% if dilatedLanes %}
210
+ let outBase = ((batch * OUT_C + ocBase) * OUT_H + oh) * OUT_W;
211
+ {% for oct in range(ocTile) %}
212
+ let outChannelBase{{ oct }} = outBase + {{ oct }}u * OUT_H * OUT_W;
213
+ {% for lane in range(lanes) %}
214
+ if (ow0 + {{ lane * dilationWSpec }}u < OUT_W) {
215
+ y[outChannelBase{{ oct }} + ow0 + {{ lane * dilationWSpec }}u] = {{ scalar }}({% if hasActivation %}fused_act(acc{{ oct }}_{{ lane }}){% else %}acc{{ oct }}_{{ lane }}{% endif %});
216
+ }
217
+ {% endfor %}
218
+ {% endfor %}
219
+ {% elif tailOutput %}
220
  let outBase = ((batch * OUT_C + ocBase) * OUT_H + oh) * OUT_W + ow4 * 4u;
221
+ {% for oct in range(ocTile) %}
222
  let outChannelBase{{ oct }} = outBase + {{ oct }}u * OUT_H * OUT_W;
223
  {% for lane in range(4) %}
224
  if (ow4 * 4u + {{ lane }}u < OUT_W) {
 
228
  {% endfor %}
229
  {% else %}
230
  let outBase = ((batch * OUT_C + ocBase) * OUT_H + oh) * OUT_W4 + ow4;
231
+ {% for oct in range(ocTile) %}
232
  y[outBase + {{ oct }}u * OUT_PLANE4] = {{ vectorScalar }}({% if hasActivation %}vec4<f32>(fused_act(acc{{ oct }}.x), fused_act(acc{{ oct }}.y), fused_act(acc{{ oct }}.z), fused_act(acc{{ oct }}.w)){% else %}acc{{ oct }}{% endif %});
233
  {% endfor %}
234
  {% endif %}
build/webgpu/manifest.json CHANGED
The diff for this file is too large to render. See raw diff
 
build/webgpu/metadata.json CHANGED
@@ -1,28 +1,89 @@
1
  {
2
  "name": "ai.onnx.Conv",
3
- "id": "_ai_onnx_conv_webgpu_4822108",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "LNwpS8vDbGYX6SF0eYrHwA2PL9KohpIj3TEv6TF5iOQ=",
11
- "conv-1x1-channel-reduce.wgsl.jinja": "N0hmEYctrx1dFezCieFBMSZw1AJkB9k1jqWbqwY73yw=",
12
- "conv-1x1-gemm-tiled-reg.wgsl.jinja": "uhup4og6kzn9fGBicWDhp8u7tRMWOxV1FP4h4B+1QC4=",
13
- "conv-1x1-gemm-tiled.wgsl.jinja": "VeRKrzHV+9CvWm0Lrsgpt5C98Gkz5MubGVEnYApQhdk=",
14
- "conv-1x1-subgroup-matrix.wgsl.jinja": "CTPjuc+j4mBdgIKG/LMdUSNTO/qeyS0810Gvzyl9waU=",
15
- "conv-direct-nd.wgsl.jinja": "ANLdecurm5wdc72OIpjU8oiYtrMhL5a5z8XhDjYJXGg=",
16
- "conv-direct-unrolled.wgsl.jinja": "ILQ1vB/EtaZvB65S5LwNliPq91fcT7LCzVoPDRoDolY=",
17
- "conv-im2col-ncdhw.wgsl.jinja": "AsmaNPOjoGmh+YHO91mT5EIc7RSlGqgzm7uKASMS3vw=",
18
- "conv-im2col-nchw.wgsl.jinja": "7IUuXo33elZx0pnR9M2vUXzXDjFuw2bJODkZViV/WQg=",
19
- "conv-splitk-reduce.wgsl.jinja": "qAkIOXGzAj6HWYSscyL1cfNgM6A1Ym/1L/CjQ8ogCEA=",
20
- "conv1d-tiled-reg.wgsl.jinja": "8xjz6LBfrQDZDFGDSYo8aV1m+yeVDvHMngjSAbOAmk8=",
21
- "conv2d-grouped-large-w4.wgsl.jinja": "jt2Z54ENODhKgmScaR19Mq3Y3gAMybt2fP49zoZfwEA=",
22
- "manifest.json": "hVZagZKncZQkjXOpaqdBUrFx0HXtV5u0K7rEqSuus1Q=",
23
- "test.json": "ron7OZMUp/fWx5XDPPNom2Zg0q0lYcWgRzLyfgE4N5I="
24
  }
25
  },
26
- "provenance": { "kernel": { "sha": "c928d21e6cc1310861cba3bafb75f5f679ecf5f3", "dirty": false } },
27
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.Conv" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
 
1
  {
2
  "name": "ai.onnx.Conv",
3
+ "id": "_ai_onnx_conv_webgpu_010d4b0",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "H1K2v5/L5nbBfjqTwogslpt8SrxTH6se0gj31ONSJeU=",
11
+ "conv-1x1-channel-reduce.wgsl.jinja": "Zjbu1oi+w5guLw54Y9C6TVJo0SntZDjgtsiie3o5md0=",
12
+ "conv-1x1-gemm-tiled-reg.wgsl.jinja": "9yFpwXS1HzmWmm4W72n+aOIryLqc9+e88awZ1ogTe+Q=",
13
+ "conv-1x1-gemm-tiled.wgsl.jinja": "uqG5mRVvcHiUo0LDCUcVgqElVTvRNknx19Gm4oIALiU=",
14
+ "conv-1x1-subgroup-matrix.wgsl.jinja": "iDJs4RJk4iJarOZCjY7/5MFrHgR7CcqI6x5uwv2dYaA=",
15
+ "conv-direct-nd.wgsl.jinja": "FqwfAKVCF1BYBAtU7Y3rDcGrmZ5AYkH4FuTzdRlT2Rg=",
16
+ "conv-direct-unrolled.wgsl.jinja": "vrGUPwYkbYE1slII7nuK5wbERRcG5u0X1cW1kMc2sQs=",
17
+ "conv-im2col-ncdhw.wgsl.jinja": "vQtnzeEKC2wm23+kwcaWoX3VSaJSqno18ACe3w3trhI=",
18
+ "conv-im2col-nchw.wgsl.jinja": "IRpmtpeVQsLE6ud+Sf1230XaZVS1dxDtTZuSDwU3P8s=",
19
+ "conv-splitk-reduce.wgsl.jinja": "LbVU5DRL0Q51fvLjThmxJQZH8aPbZkvNzij4nY/cHmo=",
20
+ "conv1d-tiled-reg.wgsl.jinja": "ERqOPl+1RbnJ+CFqZvLSYw45qYMKgB7Je0teew5gyOM=",
21
+ "conv2d-grouped-large-w4.wgsl.jinja": "v8aAMQ2saiOgbQwLZAiVr3IeDT3h5YLO/v5tGL1x8pk=",
22
+ "manifest.json": "3/3/k9ZVj/vLw3xZ66XDMGWb86wzdZCoqDS0tKg9ziE=",
23
+ "test.json": "1OUEVPx0U/BTLtlxoSCFOWVgMfrlOfQb/TUY3VivfGw="
24
  }
25
  },
26
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
27
+ "webgpu": {
28
+ "manifestSpec": "2.0",
29
+ "variants": {
30
+ "ncw1d": ["conv-direct-nd.wgsl.jinja"],
31
+ "ncw1d_bias": ["conv-direct-nd.wgsl.jinja"],
32
+ "ncdhw3d": ["conv-direct-nd.wgsl.jinja"],
33
+ "ncdhw3d_bias": ["conv-direct-nd.wgsl.jinja"],
34
+ "nchw2d": ["conv-direct-nd.wgsl.jinja"],
35
+ "nchw2d_bias": ["conv-direct-nd.wgsl.jinja"],
36
+ "grouped_large_kernel_w4": ["conv2d-grouped-large-w4.wgsl.jinja"],
37
+ "grouped_large_kernel_w4_bias": ["conv2d-grouped-large-w4.wgsl.jinja"],
38
+ "grouped_large_kernel_w4_tail": ["conv2d-grouped-large-w4.wgsl.jinja"],
39
+ "grouped_large_kernel_w4_tail_bias": ["conv2d-grouped-large-w4.wgsl.jinja"],
40
+ "grouped_large_kernel_w4_dilated_lanes": ["conv2d-grouped-large-w4.wgsl.jinja"],
41
+ "grouped_large_kernel_w4_dilated_lanes_bias": ["conv2d-grouped-large-w4.wgsl.jinja"],
42
+ "pointwise_channel_reduce": ["conv-1x1-channel-reduce.wgsl.jinja"],
43
+ "pointwise_channel_reduce_bias": ["conv-1x1-channel-reduce.wgsl.jinja"],
44
+ "gemm_1x1_subgroup_matrix": ["conv-1x1-subgroup-matrix.wgsl.jinja"],
45
+ "gemm_1x1_subgroup_matrix_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja"],
46
+ "im2col_gemm_subgroup_matrix": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
47
+ "im2col_gemm_subgroup_matrix_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
48
+ "im2col_direct_f32_subgroup_matrix": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
49
+ "im2col_direct_f16_subgroup_matrix": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
50
+ "im2col_direct_f32_subgroup_matrix_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
51
+ "im2col_direct_f16_subgroup_matrix_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
52
+ "implicit_im2col_subgroup_matrix": ["conv-1x1-subgroup-matrix.wgsl.jinja"],
53
+ "implicit_im2col_subgroup_matrix_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja"],
54
+ "implicit_im2col3d_subgroup_matrix": ["conv-1x1-subgroup-matrix.wgsl.jinja"],
55
+ "implicit_im2col3d_subgroup_matrix_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja"],
56
+ "implicit_im2col_subgroup_matrix_splitk": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
57
+ "implicit_im2col_subgroup_matrix_bias_splitk": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
58
+ "im2col3d_gemm_subgroup_matrix": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-ncdhw.wgsl.jinja"],
59
+ "im2col3d_gemm_subgroup_matrix_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-ncdhw.wgsl.jinja"],
60
+ "im2col3d_gemm_tiled_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-im2col-ncdhw.wgsl.jinja"],
61
+ "im2col3d_gemm_tiled": ["conv-1x1-gemm-tiled.wgsl.jinja", "conv-im2col-ncdhw.wgsl.jinja"],
62
+ "im2col3d_gemm_tiled_bias_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-im2col-ncdhw.wgsl.jinja"],
63
+ "im2col3d_gemm_tiled_bias": ["conv-1x1-gemm-tiled.wgsl.jinja", "conv-im2col-ncdhw.wgsl.jinja"],
64
+ "im2col_gemm_subgroup_matrix_padded": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
65
+ "im2col_gemm_subgroup_matrix_padded_bias": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
66
+ "im2col_gemm_subgroup_matrix_padded_splitk": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
67
+ "im2col_gemm_subgroup_matrix_padded_bias_splitk": ["conv-1x1-subgroup-matrix.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
68
+ "implicit_im2col_tiled_bias_reg_m32": ["conv-1x1-gemm-tiled-reg.wgsl.jinja"],
69
+ "implicit_im2col_tiled_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja"],
70
+ "implicit_im2col_tiled_bias_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja"],
71
+ "implicit_im2col_tiled_reg_splitk": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
72
+ "implicit_im2col_tiled_bias_reg_splitk": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
73
+ "im2col_gemm_tiled_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
74
+ "im2col_gemm_tiled": ["conv-1x1-gemm-tiled.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
75
+ "im2col_gemm_tiled_bias_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
76
+ "im2col_gemm_tiled_bias": ["conv-1x1-gemm-tiled.wgsl.jinja", "conv-im2col-nchw.wgsl.jinja"],
77
+ "gemm_1x1_tiled_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja"],
78
+ "gemm_1x1_tiled_bias_reg": ["conv-1x1-gemm-tiled-reg.wgsl.jinja"],
79
+ "gemm_1x1_tiled": ["conv-1x1-gemm-tiled.wgsl.jinja"],
80
+ "gemm_1x1_tiled_bias": ["conv-1x1-gemm-tiled.wgsl.jinja"],
81
+ "gemm_1x1_tiled_reg_splitk": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
82
+ "gemm_1x1_tiled_bias_reg_splitk": ["conv-1x1-gemm-tiled-reg.wgsl.jinja", "conv-splitk-reduce.wgsl.jinja"],
83
+ "direct_unrolled": ["conv-direct-unrolled.wgsl.jinja"],
84
+ "direct_unrolled_bias": ["conv-direct-unrolled.wgsl.jinja"],
85
+ "conv1d_tiled_reg": ["conv1d-tiled-reg.wgsl.jinja"],
86
+ "conv1d_tiled_bias_reg": ["conv1d-tiled-reg.wgsl.jinja"]
87
+ }
88
+ }
89
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.Conv",
3
  "fixtureArrays": {
4
  "nchw_dilated_kernel_input_x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
5
  "backend_stride2_with_padding_input_x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34],
@@ -81,7 +80,7 @@
81
  "provenance": {
82
  "source": "onnxruntime/test/providers/cpu/nn/conv_op_test.cc",
83
  "test": "ConvTest",
84
- "notes": "Zero-bias companion for the 1x1 Conv-as-GEMM subgroup-matrix path; the bias epilogue must preserve finite subnormal outputs."
85
  },
86
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
87
  "inputs": {
@@ -118,7 +117,7 @@
118
  "provenance": {
119
  "source": "onnxruntime/test/providers/cpu/nn/conv_op_test.cc",
120
  "test": "ConvTest",
121
- "notes": "Zero-bias companion for the aligned im2col+subgroup-matrix path; the bias epilogue must preserve finite subnormal convolution outputs."
122
  },
123
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
124
  "inputs": {
@@ -155,7 +154,7 @@
155
  "provenance": {
156
  "source": "onnxruntime/test/providers/cpu/nn/conv_op_test.cc",
157
  "test": "ConvTest",
158
- "notes": "Zero-bias companion for the padded im2col+subgroup-matrix path; padded K/N lanes and the bias epilogue must preserve finite subnormal real outputs."
159
  },
160
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
161
  "inputs": {
@@ -668,6 +667,51 @@
668
  "outputs": { "y": { "dtype": "float32", "shape": [1, 64, 16, 1], "tolerance": 0.0001 } },
669
  "attrs": {}
670
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
  {
672
  "name": "ort_conv2d_1x1_splitk_with_bias",
673
  "provenance": {
@@ -879,8 +923,8 @@
879
  {
880
  "name": "conv1d_f16_audio_feature",
881
  "provenance": {
882
- "source": "Manifest/schema gap regression",
883
- "notes": "Compact half-precision audio-feature convolution covering the unified, dtype-generic NCW fallback."
884
  },
885
  "attrs": { "strides": [1], "dilations": [1], "pads": [1, 1] },
886
  "inputs": {
@@ -1210,19 +1254,19 @@
1210
  {
1211
  "name": "conv_3x3_im2col_gemm_dense_c256_8x8",
1212
  "provenance": {
1213
- "notes": "Compact companion for wide 3x3 Conv bench cases such as flux2vae-dec-mid-512c-64x64-k3 and flux2vae-dec-up1-512c-128x128-k3, preserving the large K/M im2col-subgroup regime."
1214
  },
1215
  "attrs": { "strides": [1, 1], "pads": [1, 1, 1, 1] },
1216
  "inputs": {
1217
  "x": {
1218
  "dtype": "float32",
1219
  "shape": [1, 256, 8, 8],
1220
- "data": { "kind": "fillFloat32", "scale": 0.16, "sinStep": 0.013, "cosStep": 0.031 }
1221
  },
1222
  "w": {
1223
  "dtype": "float32",
1224
  "shape": [256, 256, 3, 3],
1225
- "data": { "kind": "fillFloat32", "scale": 0.015, "sinStep": 0.007, "cosStep": 0.019 }
1226
  }
1227
  },
1228
  "outputs": { "y": { "dtype": "float32", "shape": [1, 256, 8, 8], "tolerance": 0.006, "relTolerance": 0.006 } }
@@ -1534,7 +1578,7 @@
1534
  "provenance": {
1535
  "source": "onnxruntime/test/providers/cpu/nn/conv_fp16_test.cc",
1536
  "test": "ConvFp16Test.Conv2D_Bias_2",
1537
- "notes": "Covers f16 bias with asymmetric explicit pads and non-unit strides from ORT's handwritten Conv fp16 suite."
1538
  },
1539
  "attrs": { "strides": [2, 3], "pads": [1, 2, 3, 1] },
1540
  "inputs": {
@@ -1828,7 +1872,7 @@
1828
  "name": "grouped_dilated_large_kernel_k11_oc4_nchw_fallback",
1829
  "provenance": {
1830
  "source": "Realistic grouped/dilated Conv stress geometry",
1831
- "notes": "Compact companion for conv-f32-group4-k11-dilated-128c-48x48; covers the K11 selection boundary with two OC4 tiles per group."
1832
  },
1833
  "attrs": { "group": 2, "strides": [1, 1], "dilations": [2, 2], "pads": [10, 10, 10, 10] },
1834
  "inputs": {
@@ -1867,7 +1911,7 @@
1867
  "name": "f16_grouped_large_kernel_k9_oc4_nchw_f16_fallback",
1868
  "provenance": {
1869
  "source": "Realistic grouped Conv stress geometry",
1870
- "notes": "Compact f16 OC4 companion for conv-f16-group8-k9-256c-64x64; two OC4 tiles per group exercise shared-input output-channel tiling."
1871
  },
1872
  "requires": { "features": ["shader-f16"] },
1873
  "attrs": { "group": 2, "strides": [1, 1], "pads": [4, 4, 4, 4] },
@@ -1889,7 +1933,7 @@
1889
  "name": "conv3d_depth_dilation2_compact",
1890
  "provenance": {
1891
  "source": "ONNX Conv-22 volumetric dilation semantics",
1892
- "notes": "Covers a non-default depth dilation. Existing 3D fixtures varied depth padding/stride but kept dilationD=1."
1893
  },
1894
  "attrs": { "strides": [1, 1, 1], "dilations": [2, 1, 1] },
1895
  "inputs": {
@@ -1933,10 +1977,10 @@
1933
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 17], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1934
  },
1935
  {
1936
- "name": "grouped_k3_group4_w4_below_prior_min_kernel",
1937
  "provenance": {
1938
- "source": "authored for route coverage",
1939
- "notes": "A 3x3 grouped convolution above the wide-workgroup spatial floor with a width divisible by four, so it takes grouped_large_kernel_w4. Kernel sizes below 7 could not reach this variant before, so nothing covered the w4 arm at k3."
1940
  },
1941
  "attrs": { "group": 4, "strides": [1, 1], "pads": [1, 1, 1, 1] },
1942
  "inputs": {
@@ -1946,10 +1990,10 @@
1946
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 20], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1947
  },
1948
  {
1949
- "name": "grouped_k3_depthwise_w4_below_prior_min_kernel",
1950
  "provenance": {
1951
- "source": "authored for route coverage",
1952
- "notes": "Depthwise 3x3, the most common grouped shape in mobile vision backbones, on the same w4 arm."
1953
  },
1954
  "attrs": { "group": 8, "strides": [1, 1], "pads": [1, 1, 1, 1] },
1955
  "inputs": {
@@ -1959,15 +2003,20 @@
1959
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 20], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1960
  },
1961
  {
1962
- "name": "grouped_k3_group4_odd_width_tail_below_prior_min_kernel",
1963
  "provenance": {
1964
- "source": "authored for route coverage",
1965
- "notes": "A 3x3 grouped convolution whose width is not divisible by four, covering the odd-width tail specialization at k3."
1966
  },
1967
  "attrs": { "group": 4, "strides": [1, 1], "pads": [1, 1, 1, 1] },
1968
  "inputs": {
1969
  "x": { "dtype": "float32", "shape": [1, 8, 16, 17] },
1970
- "w": { "dtype": "float32", "shape": [8, 2, 3, 3] }
 
 
 
 
 
1971
  },
1972
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 17], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1973
  },
@@ -1975,7 +2024,7 @@
1975
  "name": "grouped_large_kernel_k9_dilation8_span_over_cap",
1976
  "provenance": {
1977
  "source": "ONNX Conv-22 dilations semantics (per-axis dilations [1, 8])",
1978
- "notes": "Grouped 7x9 conv whose retained input-column span (kernelW-1)*dilationW + 1 + 3*strideW = 65 exceeds GROUPED_MAX_REGISTER_SPAN (64), so the grouped wide-output route must render its per-tap bounds-tested column form instead of the row-window register form; every pre-existing grouped fixture has span <= 24. Asymmetric dilations are valid ONNX (dilations is per spatial axis). Geometry keeps all nine width taps live while leaving kw=0 out of range at ow<2 and kw=8 out of range at ow>=14, and the 7-tall window off both height edges, so the per-tap column and row guards are exercised in both directions."
1979
  },
1980
  "attrs": { "group": 2, "strides": [1, 1], "dilations": [1, 8], "pads": [3, 2, 3, 2] },
1981
  "inputs": {
@@ -1996,7 +2045,7 @@
1996
  "name": "grouped_large_kernel_k9_dilation8_stride2_span_over_cap_tail",
1997
  "provenance": {
1998
  "source": "ONNX Conv-22 strides/dilations semantics (strides [1, 2], dilations [1, 8])",
1999
- "notes": "Same over-cap span regime as grouped_large_kernel_k9_dilation8_span_over_cap (span = 8*8 + 1 + 3*2 = 71 > 64) but with strideW = 2 and an output width of 15. The per-tap form spaces its four lanes by lane*strideW, which folds to lane whenever strideW is 1, so strideW = 2 makes a dropped factor observable; the non-multiple-of-four width also exercises the masked tail epilogue. Output 18x15 = 270 keeps the wide-output route's spatial minimum."
2000
  },
2001
  "attrs": { "group": 2, "strides": [1, 2], "dilations": [1, 8], "pads": [3, 2, 3, 2] },
2002
  "inputs": {
@@ -2016,7 +2065,7 @@
2016
  {
2017
  "name": "conv1d_aligned_sgmat_k3_s1_p1_c32m128_w1024",
2018
  "provenance": {
2019
- "notes": "Whisper-stem-shaped NCW conv with K=96 and N=1024; covers aligned materialized subgroup-matrix Conv1d on the full tier and the register-tiled/scalar NCW fallbacks."
2020
  },
2021
  "attrs": { "strides": [1], "pads": [1, 1] },
2022
  "inputs": {
@@ -2106,7 +2155,7 @@
2106
  {
2107
  "name": "conv1d_tiled_reg_f16_bias_k3_s1_p1_c16m128_w1024",
2108
  "provenance": {
2109
- "notes": "f16 companion covering materialized subgroup-matrix Conv1d and the tiled NCW fallback: operands accumulate in f32 and narrow once at store."
2110
  },
2111
  "attrs": { "strides": [1], "pads": [1, 1] },
2112
  "inputs": {
@@ -2151,7 +2200,7 @@
2151
  {
2152
  "name": "grouped_kernel12_wide_bias_forces_nchw_scalar",
2153
  "provenance": {
2154
- "notes": "Bias companion of the oversized-kernel grouped conv: the bias epilogue must be exercised on the scalar NCHW direct kernel, which is the only path left once kernel width 12 and group > 1 reject the unrolled and GEMM variants."
2155
  },
2156
  "attrs": { "group": 2, "strides": [1, 1], "pads": [0, 0, 0, 0] },
2157
  "inputs": {
@@ -2196,7 +2245,7 @@
2196
  {
2197
  "name": "conv_8x8_im2col_gemm_tiled_bias_reg_m1_n8192",
2198
  "provenance": {
2199
- "notes": "Bias companion of the N-axis register-tiled im2col GEMM: the 8x8 kernel keeps the 1x1 register-tiled shortcut ineligible, so the bias epilogue runs on the im2col register-tiled GEMM."
2200
  },
2201
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2202
  "inputs": {
@@ -2221,7 +2270,7 @@
2221
  {
2222
  "name": "conv3d_im2col_subgroup_matrix_aligned_f32",
2223
  "provenance": {
2224
- "notes": "Rank-5 aligned matrix-path coverage. Shape-derived K=C*KD*KH*KW=32 and N=OD*OH*OW=64 exercise the NCDHW im2col producer and f32 subgroup-matrix consumer without relying on a model-specific size."
2225
  },
2226
  "attrs": { "strides": [1, 1, 1] },
2227
  "inputs": {
@@ -2241,7 +2290,7 @@
2241
  {
2242
  "name": "conv3d_im2col_subgroup_matrix_aligned_bias_f16",
2243
  "provenance": {
2244
- "notes": "F16+bias companion for the general aligned rank-5 matrix path. The geometry is derived from the tensor contract and also validates the scratch-store narrowing epilogue."
2245
  },
2246
  "attrs": { "strides": [1, 1, 1] },
2247
  "inputs": {
@@ -2282,7 +2331,7 @@
2282
  {
2283
  "name": "conv3d_im2col_tiled_bias_reg_large_spatial_f32",
2284
  "provenance": {
2285
- "notes": "Bias companion for the portable register-tiled rank-5 path, using the same shape-derived tile eligibility and a separately checkable epilogue."
2286
  },
2287
  "attrs": { "strides": [1, 1, 1] },
2288
  "inputs": {
@@ -2348,7 +2397,7 @@
2348
  {
2349
  "name": "conv_3x3_implicit_im2col_m32_bk16_f16_bias_batch2",
2350
  "provenance": {
2351
- "notes": "Portable M=32 carried-gather coverage for the remaining specialization edges: K=72 selects BK16, batch=2 exercises the z dispatch and batch base, and f16 X/W/Y plus bias exercise the half-storage path. The 128x256 plane is the conservative N=32768 specialization boundary established by the f32/f16 A/B controls."
2352
  },
2353
  "attrs": { "strides": [1, 1], "pads": [1, 1, 1, 1] },
2354
  "inputs": {
@@ -2459,7 +2508,7 @@
2459
  "name": "conv_3x3_splitk_im2col_padded_k_n_tails",
2460
  "tunables": { "TILED_SPLIT_K_MODE": 1 },
2461
  "provenance": {
2462
- "notes": "Split-K over a materialized padded im2col on subgroup-matrix tiers and the portable implicit gather otherwise: 5x7 output is 35 positions, so the un-split GEMM would dispatch ONE workgroup and leave 39 of 40 cores idle. K = 432 and N = 35 exercise the reduction tails in both implementations."
2463
  },
2464
  "attrs": { "strides": [1, 1], "pads": [1, 1, 1, 1] },
2465
  "inputs": {
@@ -2651,7 +2700,7 @@
2651
  {
2652
  "name": "conv_1x1_implicit_im2col_splitk_aligned_starved",
2653
  "provenance": {
2654
- "notes": "An ALIGNED 1x1 that is nonetheless starved: 32x32 is 16 column tiles over one 64-row tile, so the split outranks the dedicated aligned 1x1 kernel here even though that kernel is eligible and wins at any well-fed size."
2655
  },
2656
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2657
  "inputs": {
@@ -2790,6 +2839,1571 @@
2790
  }
2791
  },
2792
  "outputs": { "y": { "dtype": "float32", "shape": [1, 1, 91, 91], "tolerance": 0.0001, "relTolerance": 0.0001 } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2793
  }
2794
  ]
2795
  }
 
1
  {
 
2
  "fixtureArrays": {
3
  "nchw_dilated_kernel_input_x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
4
  "backend_stride2_with_padding_input_x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34],
 
80
  "provenance": {
81
  "source": "onnxruntime/test/providers/cpu/nn/conv_op_test.cc",
82
  "test": "ConvTest",
83
+ "notes": "A zero channel-bias tensor exercises the 1x1 matrix epilogue; finite subnormal convolution outputs must remain unchanged."
84
  },
85
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
86
  "inputs": {
 
117
  "provenance": {
118
  "source": "onnxruntime/test/providers/cpu/nn/conv_op_test.cc",
119
  "test": "ConvTest",
120
+ "notes": "A zero channel-bias tensor exercises the aligned column-matrix epilogue; finite subnormal convolution outputs must remain unchanged."
121
  },
122
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
123
  "inputs": {
 
154
  "provenance": {
155
  "source": "onnxruntime/test/providers/cpu/nn/conv_op_test.cc",
156
  "test": "ConvTest",
157
+ "notes": "A zero channel-bias tensor exercises the padded column-matrix epilogue; padded K/N lanes must not alter finite subnormal real outputs."
158
  },
159
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
160
  "inputs": {
 
667
  "outputs": { "y": { "dtype": "float32", "shape": [1, 64, 16, 1], "tolerance": 0.0001 } },
668
  "attrs": {}
669
  },
670
+ {
671
+ "name": "gemm_1x1_tiled_reg_splitk_c128_16x16",
672
+ "provenance": {
673
+ "notes": "Route lock for the split-K 1x1 register tile: four 64x64 output tiles over K=128 take two K slices each, and the reduce sums the f32 partials."
674
+ },
675
+ "inputs": {
676
+ "x": {
677
+ "dtype": "float32",
678
+ "shape": [1, 128, 16, 16],
679
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.031 }
680
+ },
681
+ "w": {
682
+ "dtype": "float32",
683
+ "shape": [64, 128, 1, 1],
684
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.011, "cosStep": 0.023 }
685
+ }
686
+ },
687
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 64, 16, 16], "tolerance": 0.0001 } },
688
+ "attrs": {}
689
+ },
690
+ {
691
+ "name": "gemm_1x1_tiled_reg_splitk_bias_c128_16x16",
692
+ "provenance": {
693
+ "notes": "The split-K 1x1 register tile produces two f32 partials per output tile; the reduce pass sums them before adding per-channel bias."
694
+ },
695
+ "inputs": {
696
+ "x": {
697
+ "dtype": "float32",
698
+ "shape": [1, 128, 16, 16],
699
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.019, "cosStep": 0.029 }
700
+ },
701
+ "w": {
702
+ "dtype": "float32",
703
+ "shape": [64, 128, 1, 1],
704
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.013, "cosStep": 0.021 }
705
+ },
706
+ "bias": {
707
+ "dtype": "float32",
708
+ "shape": [64],
709
+ "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.05 }
710
+ }
711
+ },
712
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 64, 16, 16], "tolerance": 0.0001 } },
713
+ "attrs": {}
714
+ },
715
  {
716
  "name": "ort_conv2d_1x1_splitk_with_bias",
717
  "provenance": {
 
923
  {
924
  "name": "conv1d_f16_audio_feature",
925
  "provenance": {
926
+ "source": "local fixture",
927
+ "notes": "A compact half-precision audio-feature convolution exercises the dtype-generic NCW implementation."
928
  },
929
  "attrs": { "strides": [1], "dilations": [1], "pads": [1, 1] },
930
  "inputs": {
 
1254
  {
1255
  "name": "conv_3x3_im2col_gemm_dense_c256_8x8",
1256
  "provenance": {
1257
+ "notes": "A compact 3x3 convolution retains a large K/M column-matrix regime while keeping the correctness tensor smaller than the model benchmarks. Offset operands keep each 2,304-tap accumulation proportional to its reduction length instead of cancelling toward zero, so a dropped tap or tile is observable."
1258
  },
1259
  "attrs": { "strides": [1, 1], "pads": [1, 1, 1, 1] },
1260
  "inputs": {
1261
  "x": {
1262
  "dtype": "float32",
1263
  "shape": [1, 256, 8, 8],
1264
+ "data": { "kind": "fillFloat32", "scale": 0.16, "sinStep": 0.013, "cosStep": 0.031, "offset": 0.2 }
1265
  },
1266
  "w": {
1267
  "dtype": "float32",
1268
  "shape": [256, 256, 3, 3],
1269
+ "data": { "kind": "fillFloat32", "scale": 0.015, "sinStep": 0.007, "cosStep": 0.019, "offset": 0.02 }
1270
  }
1271
  },
1272
  "outputs": { "y": { "dtype": "float32", "shape": [1, 256, 8, 8], "tolerance": 0.006, "relTolerance": 0.006 } }
 
1578
  "provenance": {
1579
  "source": "onnxruntime/test/providers/cpu/nn/conv_fp16_test.cc",
1580
  "test": "ConvFp16Test.Conv2D_Bias_2",
1581
+ "notes": "This half-precision case combines channel bias, asymmetric explicit padding, and non-unit strides from the cited upstream Conv fixture."
1582
  },
1583
  "attrs": { "strides": [2, 3], "pads": [1, 2, 3, 1] },
1584
  "inputs": {
 
1872
  "name": "grouped_dilated_large_kernel_k11_oc4_nchw_fallback",
1873
  "provenance": {
1874
  "source": "Realistic grouped/dilated Conv stress geometry",
1875
+ "notes": "A grouped, dilated 11x11 convolution produces two four-channel output tiles per group at the K11 selection boundary."
1876
  },
1877
  "attrs": { "group": 2, "strides": [1, 1], "dilations": [2, 2], "pads": [10, 10, 10, 10] },
1878
  "inputs": {
 
1911
  "name": "f16_grouped_large_kernel_k9_oc4_nchw_f16_fallback",
1912
  "provenance": {
1913
  "source": "Realistic grouped Conv stress geometry",
1914
+ "notes": "A compact half-precision grouped 9x9 convolution produces two four-channel output tiles per group, exercising shared-input output-channel tiling."
1915
  },
1916
  "requires": { "features": ["shader-f16"] },
1917
  "attrs": { "group": 2, "strides": [1, 1], "pads": [4, 4, 4, 4] },
 
1933
  "name": "conv3d_depth_dilation2_compact",
1934
  "provenance": {
1935
  "source": "ONNX Conv-22 volumetric dilation semantics",
1936
+ "notes": "Depth dilation 2 separates the two volumetric kernel planes while height and width dilation remain one."
1937
  },
1938
  "attrs": { "strides": [1, 1, 1], "dilations": [2, 1, 1] },
1939
  "inputs": {
 
1977
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 17], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1978
  },
1979
  {
1980
+ "name": "grouped_k3_group4_w4",
1981
  "provenance": {
1982
+ "source": "repository-authored fixture",
1983
+ "notes": "A grouped 3x3 convolution produces 20 columns and enough spatial work to select the four-column grouped specialization."
1984
  },
1985
  "attrs": { "group": 4, "strides": [1, 1], "pads": [1, 1, 1, 1] },
1986
  "inputs": {
 
1990
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 20], "tolerance": 0.0001, "relTolerance": 0.0001 } }
1991
  },
1992
  {
1993
+ "name": "grouped_k3_depthwise_w4",
1994
  "provenance": {
1995
+ "source": "repository-authored fixture",
1996
+ "notes": "A depthwise 3x3 convolution produces 20 columns and selects the four-column grouped specialization with one input channel per group."
1997
  },
1998
  "attrs": { "group": 8, "strides": [1, 1], "pads": [1, 1, 1, 1] },
1999
  "inputs": {
 
2003
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 20], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2004
  },
2005
  {
2006
+ "name": "grouped_k3_group4_odd_width_tail_bias",
2007
  "provenance": {
2008
+ "source": "repository-authored fixture",
2009
+ "notes": "A grouped 3x3 convolution produces 17 columns, so the final four-lane output group contains one valid column. Mixed-sign channel bias exercises the biased masked-tail epilogue."
2010
  },
2011
  "attrs": { "group": 4, "strides": [1, 1], "pads": [1, 1, 1, 1] },
2012
  "inputs": {
2013
  "x": { "dtype": "float32", "shape": [1, 8, 16, 17] },
2014
+ "w": { "dtype": "float32", "shape": [8, 2, 3, 3] },
2015
+ "bias": {
2016
+ "dtype": "float32",
2017
+ "shape": [8],
2018
+ "data": { "kind": "values", "values": [-0.5, 0.25, 0.75, -1.0, 1.25, -0.125, 0.375, -0.875] }
2019
+ }
2020
  },
2021
  "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 17], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2022
  },
 
2024
  "name": "grouped_large_kernel_k9_dilation8_span_over_cap",
2025
  "provenance": {
2026
  "source": "ONNX Conv-22 dilations semantics (per-axis dilations [1, 8])",
2027
+ "notes": "For this grouped 7x9 convolution, (kernelW-1)*dilationW + 1 + 3*strideW = 65 exceeds GROUPED_MAX_REGISTER_SPAN (64), selecting per-tap bounds-tested column gathers. Asymmetric dilation keeps all nine width taps live while kw=0 is out of range at ow<2, kw=8 is out of range at ow>=14, and the seven-row window crosses both height edges."
2028
  },
2029
  "attrs": { "group": 2, "strides": [1, 1], "dilations": [1, 8], "pads": [3, 2, 3, 2] },
2030
  "inputs": {
 
2045
  "name": "grouped_large_kernel_k9_dilation8_stride2_span_over_cap_tail",
2046
  "provenance": {
2047
  "source": "ONNX Conv-22 strides/dilations semantics (strides [1, 2], dilations [1, 8])",
2048
+ "notes": "The retained column span is 8*8 + 1 + 3*2 = 71, exceeding GROUPED_MAX_REGISTER_SPAN (64). strideW=2 makes each four-lane gather advance by twice the lane index, while output width 15 exercises the masked tail epilogue and 18x15 outputs satisfy the spatial work floor."
2049
  },
2050
  "attrs": { "group": 2, "strides": [1, 2], "dilations": [1, 8], "pads": [3, 2, 3, 2] },
2051
  "inputs": {
 
2065
  {
2066
  "name": "conv1d_aligned_sgmat_k3_s1_p1_c32m128_w1024",
2067
  "provenance": {
2068
+ "notes": "A Whisper-stem-shaped NCW convolution with K=96 and N=1024 exercises aligned materialized matrix multiplication and the register-tiled and scalar NCW implementations."
2069
  },
2070
  "attrs": { "strides": [1], "pads": [1, 1] },
2071
  "inputs": {
 
2155
  {
2156
  "name": "conv1d_tiled_reg_f16_bias_k3_s1_p1_c16m128_w1024",
2157
  "provenance": {
2158
+ "notes": "Half-precision operands exercise materialized matrix multiplication and tiled NCW paths; both accumulate in f32 and narrow once at the output store."
2159
  },
2160
  "attrs": { "strides": [1], "pads": [1, 1] },
2161
  "inputs": {
 
2200
  {
2201
  "name": "grouped_kernel12_wide_bias_forces_nchw_scalar",
2202
  "provenance": {
2203
+ "notes": "Kernel width 12 with group > 1 selects the scalar NCHW implementation. Nonzero channel bias exercises its output epilogue."
2204
  },
2205
  "attrs": { "group": 2, "strides": [1, 1], "pads": [0, 0, 0, 0] },
2206
  "inputs": {
 
2245
  {
2246
  "name": "conv_8x8_im2col_gemm_tiled_bias_reg_m1_n8192",
2247
  "provenance": {
2248
+ "notes": "An 8x8 kernel excludes the 1x1 shortcut and selects the N-axis register-tiled column-matrix product. Nonzero channel bias exercises its output epilogue."
2249
  },
2250
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2251
  "inputs": {
 
2270
  {
2271
  "name": "conv3d_im2col_subgroup_matrix_aligned_f32",
2272
  "provenance": {
2273
+ "notes": "For this rank-5 tensor, K=C*KD*KH*KW=32 and N=OD*OH*OW=64. The aligned geometry exercises the NCDHW column producer and f32 matrix consumer without a model-specific size."
2274
  },
2275
  "attrs": { "strides": [1, 1, 1] },
2276
  "inputs": {
 
2290
  {
2291
  "name": "conv3d_im2col_subgroup_matrix_aligned_bias_f16",
2292
  "provenance": {
2293
+ "notes": "Half-precision rank-5 input with channel bias exercises the aligned matrix path and its f32-to-f16 scratch-store epilogue."
2294
  },
2295
  "attrs": { "strides": [1, 1, 1] },
2296
  "inputs": {
 
2331
  {
2332
  "name": "conv3d_im2col_tiled_bias_reg_large_spatial_f32",
2333
  "provenance": {
2334
+ "notes": "Rank-5 geometry selects the portable register-tiled path, and nonzero channel bias makes its epilogue independently observable."
2335
  },
2336
  "attrs": { "strides": [1, 1, 1] },
2337
  "inputs": {
 
2397
  {
2398
  "name": "conv_3x3_implicit_im2col_m32_bk16_f16_bias_batch2",
2399
  "provenance": {
2400
+ "notes": "M=32 and K=72 select the BK16 carried-gather specialization. batch=2 exercises the z dispatch and batch offset; half-precision X, W, Y, and bias exercise packed storage at the N=128*256=32768 boundary."
2401
  },
2402
  "attrs": { "strides": [1, 1], "pads": [1, 1, 1, 1] },
2403
  "inputs": {
 
2508
  "name": "conv_3x3_splitk_im2col_padded_k_n_tails",
2509
  "tunables": { "TILED_SPLIT_K_MODE": 1 },
2510
  "provenance": {
2511
+ "notes": "A 5x7 output has 35 positions, so split-K adds independent workgroups to the single output tile. K=432 and N=35 exercise reduction tails in both the subgroup-matrix materialized-im2col route and the portable implicit-gather route."
2512
  },
2513
  "attrs": { "strides": [1, 1], "pads": [1, 1, 1, 1] },
2514
  "inputs": {
 
2700
  {
2701
  "name": "conv_1x1_implicit_im2col_splitk_aligned_starved",
2702
  "provenance": {
2703
+ "notes": "An aligned 1x1 convolution with a 32x32 output produces 16 column tiles over one 64-row tile, exercising split-K despite satisfying the dedicated aligned-kernel geometry."
2704
  },
2705
  "attrs": { "strides": [1, 1], "pads": [0, 0, 0, 0] },
2706
  "inputs": {
 
2839
  }
2840
  },
2841
  "outputs": { "y": { "dtype": "float32", "shape": [1, 1, 91, 91], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2842
+ },
2843
+ {
2844
+ "name": "grouped_dilated_lanes_k9_d4_g2_16x40",
2845
+ "provenance": {
2846
+ "notes": "Dilated-lane grouped route: four columns per invocation spaced by the dilation, so every tap reads a 12-register row window; width 40 is a whole number of lane runs in every residue class."
2847
+ },
2848
+ "attrs": { "group": 2, "strides": [1, 1], "dilations": [4, 4], "pads": [16, 16, 16, 16] },
2849
+ "inputs": {
2850
+ "x": {
2851
+ "dtype": "float32",
2852
+ "shape": [1, 8, 16, 40],
2853
+ "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.037, "cosStep": 0.013 }
2854
+ },
2855
+ "w": {
2856
+ "dtype": "float32",
2857
+ "shape": [8, 4, 9, 9],
2858
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.017, "cosStep": 0.029 }
2859
+ }
2860
+ },
2861
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 40], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2862
+ },
2863
+ {
2864
+ "name": "grouped_dilated_lanes_k9_d4_g2_odd_width_41_lane_tail",
2865
+ "provenance": {
2866
+ "notes": "Dilated-lane route with a width that is not a whole number of runs: residue classes hold 11, 10, 10 and 10 columns, so the last run of every class stores only its leading lanes."
2867
+ },
2868
+ "attrs": { "group": 2, "strides": [1, 1], "dilations": [4, 4], "pads": [16, 16, 16, 16] },
2869
+ "inputs": {
2870
+ "x": {
2871
+ "dtype": "float32",
2872
+ "shape": [1, 8, 16, 41],
2873
+ "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.031, "cosStep": 0.019 }
2874
+ },
2875
+ "w": {
2876
+ "dtype": "float32",
2877
+ "shape": [8, 4, 9, 9],
2878
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.023, "cosStep": 0.011 }
2879
+ }
2880
+ },
2881
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 41], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2882
+ },
2883
+ {
2884
+ "name": "f16_grouped_dilated_lanes_k9_d5_g4_oc2_12x40",
2885
+ "provenance": {
2886
+ "notes": "f16 dilated-lane route at a two-channel output tile (four groups of two) and the smallest width the gate admits at dilation 5."
2887
+ },
2888
+ "attrs": { "group": 4, "strides": [1, 1], "dilations": [5, 5], "pads": [20, 20, 20, 20] },
2889
+ "inputs": {
2890
+ "x": {
2891
+ "dtype": "float16",
2892
+ "shape": [1, 8, 12, 40],
2893
+ "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.041, "cosStep": 0.017 }
2894
+ },
2895
+ "w": {
2896
+ "dtype": "float16",
2897
+ "shape": [8, 2, 9, 9],
2898
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.019, "cosStep": 0.031 }
2899
+ }
2900
+ },
2901
+ "outputs": { "y": { "dtype": "float16", "shape": [1, 8, 12, 40], "tolerance": 0.01, "relTolerance": 0.01 } }
2902
+ },
2903
+ {
2904
+ "name": "grouped_dilated_lanes_k9_d4_g2_16x40_bias",
2905
+ "provenance": {
2906
+ "notes": "The dilated-lanes grouped route carrying a bias: the accumulator is seeded per output channel before any tap is read, and the lane registers are the ones that must pick it up."
2907
+ },
2908
+ "attrs": { "group": 2, "strides": [1, 1], "dilations": [4, 4], "pads": [16, 16, 16, 16] },
2909
+ "inputs": {
2910
+ "x": {
2911
+ "dtype": "float32",
2912
+ "shape": [1, 8, 16, 40],
2913
+ "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.037, "cosStep": 0.013 }
2914
+ },
2915
+ "w": {
2916
+ "dtype": "float32",
2917
+ "shape": [8, 4, 9, 9],
2918
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.017, "cosStep": 0.029 }
2919
+ },
2920
+ "bias": {
2921
+ "dtype": "float32",
2922
+ "shape": [8],
2923
+ "data": { "kind": "values", "values": [0.5, -0.25, 1.0, -0.75, 0.125, 2.0, -1.5, 0.375] }
2924
+ }
2925
+ },
2926
+ "outputs": { "y": { "dtype": "float32", "shape": [1, 8, 16, 40], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2927
+ },
2928
+ {
2929
+ "name": "direct_columns_float32_m32_c32_rank1_n128_bias0",
2930
+ "provenance": {
2931
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
2932
+ },
2933
+ "attrs": { "pads": [1, 1] },
2934
+ "inputs": {
2935
+ "x": {
2936
+ "dtype": "float32",
2937
+ "shape": [2, 32, 128],
2938
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
2939
+ },
2940
+ "w": {
2941
+ "dtype": "float32",
2942
+ "shape": [32, 32, 3],
2943
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
2944
+ }
2945
+ },
2946
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2947
+ },
2948
+ {
2949
+ "name": "direct_columns_float32_m32_c32_rank1_n128_bias1",
2950
+ "provenance": {
2951
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
2952
+ },
2953
+ "attrs": { "pads": [1, 1] },
2954
+ "inputs": {
2955
+ "x": {
2956
+ "dtype": "float32",
2957
+ "shape": [2, 32, 128],
2958
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
2959
+ },
2960
+ "w": {
2961
+ "dtype": "float32",
2962
+ "shape": [32, 32, 3],
2963
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
2964
+ },
2965
+ "bias": {
2966
+ "dtype": "float32",
2967
+ "shape": [32],
2968
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
2969
+ }
2970
+ },
2971
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2972
+ },
2973
+ {
2974
+ "name": "direct_columns_float32_m32_c32_rank2_n8x8_bias0",
2975
+ "provenance": {
2976
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
2977
+ },
2978
+ "attrs": { "pads": [0, 1, 0, 1] },
2979
+ "inputs": {
2980
+ "x": {
2981
+ "dtype": "float32",
2982
+ "shape": [2, 32, 8, 8],
2983
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
2984
+ },
2985
+ "w": {
2986
+ "dtype": "float32",
2987
+ "shape": [32, 32, 1, 3],
2988
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
2989
+ }
2990
+ },
2991
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
2992
+ },
2993
+ {
2994
+ "name": "direct_columns_float32_m32_c32_rank2_n8x8_bias1",
2995
+ "provenance": {
2996
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
2997
+ },
2998
+ "attrs": { "pads": [0, 1, 0, 1] },
2999
+ "inputs": {
3000
+ "x": {
3001
+ "dtype": "float32",
3002
+ "shape": [2, 32, 8, 8],
3003
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3004
+ },
3005
+ "w": {
3006
+ "dtype": "float32",
3007
+ "shape": [32, 32, 1, 3],
3008
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3009
+ },
3010
+ "bias": {
3011
+ "dtype": "float32",
3012
+ "shape": [32],
3013
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3014
+ }
3015
+ },
3016
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3017
+ },
3018
+ {
3019
+ "name": "direct_columns_float32_m64_c32_rank1_n128_bias0",
3020
+ "provenance": {
3021
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3022
+ },
3023
+ "attrs": { "pads": [1, 1] },
3024
+ "inputs": {
3025
+ "x": {
3026
+ "dtype": "float32",
3027
+ "shape": [2, 32, 128],
3028
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3029
+ },
3030
+ "w": {
3031
+ "dtype": "float32",
3032
+ "shape": [64, 32, 3],
3033
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3034
+ }
3035
+ },
3036
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3037
+ },
3038
+ {
3039
+ "name": "direct_columns_float32_m64_c32_rank1_n128_bias1",
3040
+ "provenance": {
3041
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3042
+ },
3043
+ "attrs": { "pads": [1, 1] },
3044
+ "inputs": {
3045
+ "x": {
3046
+ "dtype": "float32",
3047
+ "shape": [2, 32, 128],
3048
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3049
+ },
3050
+ "w": {
3051
+ "dtype": "float32",
3052
+ "shape": [64, 32, 3],
3053
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3054
+ },
3055
+ "bias": {
3056
+ "dtype": "float32",
3057
+ "shape": [64],
3058
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3059
+ }
3060
+ },
3061
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3062
+ },
3063
+ {
3064
+ "name": "direct_columns_float32_m64_c32_rank2_n8x8_bias0",
3065
+ "provenance": {
3066
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3067
+ },
3068
+ "attrs": { "pads": [0, 1, 0, 1] },
3069
+ "inputs": {
3070
+ "x": {
3071
+ "dtype": "float32",
3072
+ "shape": [2, 32, 8, 8],
3073
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3074
+ },
3075
+ "w": {
3076
+ "dtype": "float32",
3077
+ "shape": [64, 32, 1, 3],
3078
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3079
+ }
3080
+ },
3081
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3082
+ },
3083
+ {
3084
+ "name": "direct_columns_float32_m64_c32_rank2_n8x8_bias1",
3085
+ "provenance": {
3086
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3087
+ },
3088
+ "attrs": { "pads": [0, 1, 0, 1] },
3089
+ "inputs": {
3090
+ "x": {
3091
+ "dtype": "float32",
3092
+ "shape": [2, 32, 8, 8],
3093
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3094
+ },
3095
+ "w": {
3096
+ "dtype": "float32",
3097
+ "shape": [64, 32, 1, 3],
3098
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3099
+ },
3100
+ "bias": {
3101
+ "dtype": "float32",
3102
+ "shape": [64],
3103
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3104
+ }
3105
+ },
3106
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3107
+ },
3108
+ {
3109
+ "name": "direct_columns_float32_m96_c32_rank1_n128_bias0",
3110
+ "provenance": {
3111
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3112
+ },
3113
+ "attrs": { "pads": [1, 1] },
3114
+ "inputs": {
3115
+ "x": {
3116
+ "dtype": "float32",
3117
+ "shape": [2, 32, 128],
3118
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3119
+ },
3120
+ "w": {
3121
+ "dtype": "float32",
3122
+ "shape": [96, 32, 3],
3123
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3124
+ }
3125
+ },
3126
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 96, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3127
+ },
3128
+ {
3129
+ "name": "direct_columns_float32_m96_c32_rank1_n128_bias1",
3130
+ "provenance": {
3131
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3132
+ },
3133
+ "attrs": { "pads": [1, 1] },
3134
+ "inputs": {
3135
+ "x": {
3136
+ "dtype": "float32",
3137
+ "shape": [2, 32, 128],
3138
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3139
+ },
3140
+ "w": {
3141
+ "dtype": "float32",
3142
+ "shape": [96, 32, 3],
3143
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3144
+ },
3145
+ "bias": {
3146
+ "dtype": "float32",
3147
+ "shape": [96],
3148
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3149
+ }
3150
+ },
3151
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 96, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3152
+ },
3153
+ {
3154
+ "name": "direct_columns_float32_m96_c32_rank2_n8x8_bias0",
3155
+ "provenance": {
3156
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3157
+ },
3158
+ "attrs": { "pads": [0, 1, 0, 1] },
3159
+ "inputs": {
3160
+ "x": {
3161
+ "dtype": "float32",
3162
+ "shape": [2, 32, 8, 8],
3163
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3164
+ },
3165
+ "w": {
3166
+ "dtype": "float32",
3167
+ "shape": [96, 32, 1, 3],
3168
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3169
+ }
3170
+ },
3171
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 96, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3172
+ },
3173
+ {
3174
+ "name": "direct_columns_float32_m96_c32_rank2_n8x8_bias1",
3175
+ "provenance": {
3176
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3177
+ },
3178
+ "attrs": { "pads": [0, 1, 0, 1] },
3179
+ "inputs": {
3180
+ "x": {
3181
+ "dtype": "float32",
3182
+ "shape": [2, 32, 8, 8],
3183
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3184
+ },
3185
+ "w": {
3186
+ "dtype": "float32",
3187
+ "shape": [96, 32, 1, 3],
3188
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3189
+ },
3190
+ "bias": {
3191
+ "dtype": "float32",
3192
+ "shape": [96],
3193
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3194
+ }
3195
+ },
3196
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 96, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3197
+ },
3198
+ {
3199
+ "name": "direct_columns_float32_m128_c32_rank1_n128_bias0",
3200
+ "provenance": {
3201
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3202
+ },
3203
+ "attrs": { "pads": [1, 1] },
3204
+ "inputs": {
3205
+ "x": {
3206
+ "dtype": "float32",
3207
+ "shape": [2, 32, 128],
3208
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3209
+ },
3210
+ "w": {
3211
+ "dtype": "float32",
3212
+ "shape": [128, 32, 3],
3213
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3214
+ }
3215
+ },
3216
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 128, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3217
+ },
3218
+ {
3219
+ "name": "direct_columns_float32_m128_c32_rank1_n128_bias1",
3220
+ "provenance": {
3221
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3222
+ },
3223
+ "attrs": { "pads": [1, 1] },
3224
+ "inputs": {
3225
+ "x": {
3226
+ "dtype": "float32",
3227
+ "shape": [2, 32, 128],
3228
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3229
+ },
3230
+ "w": {
3231
+ "dtype": "float32",
3232
+ "shape": [128, 32, 3],
3233
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3234
+ },
3235
+ "bias": {
3236
+ "dtype": "float32",
3237
+ "shape": [128],
3238
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3239
+ }
3240
+ },
3241
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 128, 128], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3242
+ },
3243
+ {
3244
+ "name": "direct_columns_float32_m128_c32_rank2_n8x8_bias0",
3245
+ "provenance": {
3246
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3247
+ },
3248
+ "attrs": { "pads": [0, 1, 0, 1] },
3249
+ "inputs": {
3250
+ "x": {
3251
+ "dtype": "float32",
3252
+ "shape": [2, 32, 8, 8],
3253
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3254
+ },
3255
+ "w": {
3256
+ "dtype": "float32",
3257
+ "shape": [128, 32, 1, 3],
3258
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3259
+ }
3260
+ },
3261
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 128, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3262
+ },
3263
+ {
3264
+ "name": "direct_columns_float32_m128_c32_rank2_n8x8_bias1",
3265
+ "provenance": {
3266
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3267
+ },
3268
+ "attrs": { "pads": [0, 1, 0, 1] },
3269
+ "inputs": {
3270
+ "x": {
3271
+ "dtype": "float32",
3272
+ "shape": [2, 32, 8, 8],
3273
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3274
+ },
3275
+ "w": {
3276
+ "dtype": "float32",
3277
+ "shape": [128, 32, 1, 3],
3278
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3279
+ },
3280
+ "bias": {
3281
+ "dtype": "float32",
3282
+ "shape": [128],
3283
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3284
+ }
3285
+ },
3286
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 128, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3287
+ },
3288
+ {
3289
+ "name": "direct_columns_float32_m31_c32_rank2_n8x8_bias0",
3290
+ "provenance": {
3291
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3292
+ },
3293
+ "attrs": { "pads": [0, 1, 0, 1] },
3294
+ "inputs": {
3295
+ "x": {
3296
+ "dtype": "float32",
3297
+ "shape": [2, 32, 8, 8],
3298
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3299
+ },
3300
+ "w": {
3301
+ "dtype": "float32",
3302
+ "shape": [31, 32, 1, 3],
3303
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3304
+ }
3305
+ },
3306
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 31, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3307
+ },
3308
+ {
3309
+ "name": "direct_columns_float32_m33_c32_rank2_n8x8_bias0",
3310
+ "provenance": {
3311
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3312
+ },
3313
+ "attrs": { "pads": [0, 1, 0, 1] },
3314
+ "inputs": {
3315
+ "x": {
3316
+ "dtype": "float32",
3317
+ "shape": [2, 32, 8, 8],
3318
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3319
+ },
3320
+ "w": {
3321
+ "dtype": "float32",
3322
+ "shape": [33, 32, 1, 3],
3323
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3324
+ }
3325
+ },
3326
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 33, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3327
+ },
3328
+ {
3329
+ "name": "direct_columns_float32_m65_c32_rank2_n8x8_bias0",
3330
+ "provenance": {
3331
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3332
+ },
3333
+ "attrs": { "pads": [0, 1, 0, 1] },
3334
+ "inputs": {
3335
+ "x": {
3336
+ "dtype": "float32",
3337
+ "shape": [2, 32, 8, 8],
3338
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3339
+ },
3340
+ "w": {
3341
+ "dtype": "float32",
3342
+ "shape": [65, 32, 1, 3],
3343
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3344
+ }
3345
+ },
3346
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 65, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3347
+ },
3348
+ {
3349
+ "name": "direct_columns_float32_m64_c31_rank2_n8x8_bias0",
3350
+ "provenance": {
3351
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3352
+ },
3353
+ "attrs": { "pads": [0, 1, 0, 1] },
3354
+ "inputs": {
3355
+ "x": {
3356
+ "dtype": "float32",
3357
+ "shape": [2, 31, 8, 8],
3358
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3359
+ },
3360
+ "w": {
3361
+ "dtype": "float32",
3362
+ "shape": [64, 31, 1, 3],
3363
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3364
+ }
3365
+ },
3366
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 8, 8], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3367
+ },
3368
+ {
3369
+ "name": "direct_columns_float32_m64_c32_rank2_n7x9_bias0",
3370
+ "provenance": {
3371
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3372
+ },
3373
+ "attrs": { "pads": [0, 1, 0, 1] },
3374
+ "inputs": {
3375
+ "x": {
3376
+ "dtype": "float32",
3377
+ "shape": [2, 32, 7, 9],
3378
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3379
+ },
3380
+ "w": {
3381
+ "dtype": "float32",
3382
+ "shape": [64, 32, 1, 3],
3383
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3384
+ }
3385
+ },
3386
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 7, 9], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3387
+ },
3388
+ {
3389
+ "name": "direct_columns_float16_m32_c32_rank1_n128_bias0",
3390
+ "provenance": {
3391
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3392
+ },
3393
+ "attrs": { "pads": [1, 1] },
3394
+ "inputs": {
3395
+ "x": {
3396
+ "dtype": "float16",
3397
+ "shape": [2, 32, 128],
3398
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3399
+ },
3400
+ "w": {
3401
+ "dtype": "float16",
3402
+ "shape": [32, 32, 3],
3403
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3404
+ }
3405
+ },
3406
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3407
+ },
3408
+ {
3409
+ "name": "direct_columns_float16_m32_c32_rank1_n128_bias1",
3410
+ "provenance": {
3411
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3412
+ },
3413
+ "attrs": { "pads": [1, 1] },
3414
+ "inputs": {
3415
+ "x": {
3416
+ "dtype": "float16",
3417
+ "shape": [2, 32, 128],
3418
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3419
+ },
3420
+ "w": {
3421
+ "dtype": "float16",
3422
+ "shape": [32, 32, 3],
3423
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3424
+ },
3425
+ "bias": {
3426
+ "dtype": "float16",
3427
+ "shape": [32],
3428
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3429
+ }
3430
+ },
3431
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3432
+ },
3433
+ {
3434
+ "name": "direct_columns_float16_m32_c32_rank2_n8x8_bias0",
3435
+ "provenance": {
3436
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3437
+ },
3438
+ "attrs": { "pads": [0, 1, 0, 1] },
3439
+ "inputs": {
3440
+ "x": {
3441
+ "dtype": "float16",
3442
+ "shape": [2, 32, 8, 8],
3443
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3444
+ },
3445
+ "w": {
3446
+ "dtype": "float16",
3447
+ "shape": [32, 32, 1, 3],
3448
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3449
+ }
3450
+ },
3451
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3452
+ },
3453
+ {
3454
+ "name": "direct_columns_float16_m32_c32_rank2_n8x8_bias1",
3455
+ "provenance": {
3456
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3457
+ },
3458
+ "attrs": { "pads": [0, 1, 0, 1] },
3459
+ "inputs": {
3460
+ "x": {
3461
+ "dtype": "float16",
3462
+ "shape": [2, 32, 8, 8],
3463
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3464
+ },
3465
+ "w": {
3466
+ "dtype": "float16",
3467
+ "shape": [32, 32, 1, 3],
3468
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3469
+ },
3470
+ "bias": {
3471
+ "dtype": "float16",
3472
+ "shape": [32],
3473
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3474
+ }
3475
+ },
3476
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3477
+ },
3478
+ {
3479
+ "name": "direct_columns_float16_m64_c32_rank1_n128_bias0",
3480
+ "provenance": {
3481
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3482
+ },
3483
+ "attrs": { "pads": [1, 1] },
3484
+ "inputs": {
3485
+ "x": {
3486
+ "dtype": "float16",
3487
+ "shape": [2, 32, 128],
3488
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3489
+ },
3490
+ "w": {
3491
+ "dtype": "float16",
3492
+ "shape": [64, 32, 3],
3493
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3494
+ }
3495
+ },
3496
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3497
+ },
3498
+ {
3499
+ "name": "direct_columns_float16_m64_c32_rank1_n128_bias1",
3500
+ "provenance": {
3501
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3502
+ },
3503
+ "attrs": { "pads": [1, 1] },
3504
+ "inputs": {
3505
+ "x": {
3506
+ "dtype": "float16",
3507
+ "shape": [2, 32, 128],
3508
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3509
+ },
3510
+ "w": {
3511
+ "dtype": "float16",
3512
+ "shape": [64, 32, 3],
3513
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3514
+ },
3515
+ "bias": {
3516
+ "dtype": "float16",
3517
+ "shape": [64],
3518
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3519
+ }
3520
+ },
3521
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3522
+ },
3523
+ {
3524
+ "name": "direct_columns_float16_m64_c32_rank2_n8x8_bias0",
3525
+ "provenance": {
3526
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3527
+ },
3528
+ "attrs": { "pads": [0, 1, 0, 1] },
3529
+ "inputs": {
3530
+ "x": {
3531
+ "dtype": "float16",
3532
+ "shape": [2, 32, 8, 8],
3533
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3534
+ },
3535
+ "w": {
3536
+ "dtype": "float16",
3537
+ "shape": [64, 32, 1, 3],
3538
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3539
+ }
3540
+ },
3541
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3542
+ },
3543
+ {
3544
+ "name": "direct_columns_float16_m64_c32_rank2_n8x8_bias1",
3545
+ "provenance": {
3546
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3547
+ },
3548
+ "attrs": { "pads": [0, 1, 0, 1] },
3549
+ "inputs": {
3550
+ "x": {
3551
+ "dtype": "float16",
3552
+ "shape": [2, 32, 8, 8],
3553
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3554
+ },
3555
+ "w": {
3556
+ "dtype": "float16",
3557
+ "shape": [64, 32, 1, 3],
3558
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3559
+ },
3560
+ "bias": {
3561
+ "dtype": "float16",
3562
+ "shape": [64],
3563
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3564
+ }
3565
+ },
3566
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3567
+ },
3568
+ {
3569
+ "name": "direct_columns_float16_m96_c32_rank1_n128_bias0",
3570
+ "provenance": {
3571
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3572
+ },
3573
+ "attrs": { "pads": [1, 1] },
3574
+ "inputs": {
3575
+ "x": {
3576
+ "dtype": "float16",
3577
+ "shape": [2, 32, 128],
3578
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3579
+ },
3580
+ "w": {
3581
+ "dtype": "float16",
3582
+ "shape": [96, 32, 3],
3583
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3584
+ }
3585
+ },
3586
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 96, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3587
+ },
3588
+ {
3589
+ "name": "direct_columns_float16_m96_c32_rank1_n128_bias1",
3590
+ "provenance": {
3591
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3592
+ },
3593
+ "attrs": { "pads": [1, 1] },
3594
+ "inputs": {
3595
+ "x": {
3596
+ "dtype": "float16",
3597
+ "shape": [2, 32, 128],
3598
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3599
+ },
3600
+ "w": {
3601
+ "dtype": "float16",
3602
+ "shape": [96, 32, 3],
3603
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3604
+ },
3605
+ "bias": {
3606
+ "dtype": "float16",
3607
+ "shape": [96],
3608
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3609
+ }
3610
+ },
3611
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 96, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3612
+ },
3613
+ {
3614
+ "name": "direct_columns_float16_m96_c32_rank2_n8x8_bias0",
3615
+ "provenance": {
3616
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3617
+ },
3618
+ "attrs": { "pads": [0, 1, 0, 1] },
3619
+ "inputs": {
3620
+ "x": {
3621
+ "dtype": "float16",
3622
+ "shape": [2, 32, 8, 8],
3623
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3624
+ },
3625
+ "w": {
3626
+ "dtype": "float16",
3627
+ "shape": [96, 32, 1, 3],
3628
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3629
+ }
3630
+ },
3631
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 96, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3632
+ },
3633
+ {
3634
+ "name": "direct_columns_float16_m96_c32_rank2_n8x8_bias1",
3635
+ "provenance": {
3636
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3637
+ },
3638
+ "attrs": { "pads": [0, 1, 0, 1] },
3639
+ "inputs": {
3640
+ "x": {
3641
+ "dtype": "float16",
3642
+ "shape": [2, 32, 8, 8],
3643
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3644
+ },
3645
+ "w": {
3646
+ "dtype": "float16",
3647
+ "shape": [96, 32, 1, 3],
3648
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3649
+ },
3650
+ "bias": {
3651
+ "dtype": "float16",
3652
+ "shape": [96],
3653
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3654
+ }
3655
+ },
3656
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 96, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3657
+ },
3658
+ {
3659
+ "name": "direct_columns_float16_m128_c32_rank1_n128_bias0",
3660
+ "provenance": {
3661
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3662
+ },
3663
+ "attrs": { "pads": [1, 1] },
3664
+ "inputs": {
3665
+ "x": {
3666
+ "dtype": "float16",
3667
+ "shape": [2, 32, 128],
3668
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3669
+ },
3670
+ "w": {
3671
+ "dtype": "float16",
3672
+ "shape": [128, 32, 3],
3673
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3674
+ }
3675
+ },
3676
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 128, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3677
+ },
3678
+ {
3679
+ "name": "direct_columns_float16_m128_c32_rank1_n128_bias1",
3680
+ "provenance": {
3681
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3682
+ },
3683
+ "attrs": { "pads": [1, 1] },
3684
+ "inputs": {
3685
+ "x": {
3686
+ "dtype": "float16",
3687
+ "shape": [2, 32, 128],
3688
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3689
+ },
3690
+ "w": {
3691
+ "dtype": "float16",
3692
+ "shape": [128, 32, 3],
3693
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3694
+ },
3695
+ "bias": {
3696
+ "dtype": "float16",
3697
+ "shape": [128],
3698
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3699
+ }
3700
+ },
3701
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 128, 128], "tolerance": 0.001, "relTolerance": 0.001 } }
3702
+ },
3703
+ {
3704
+ "name": "direct_columns_float16_m128_c32_rank2_n8x8_bias0",
3705
+ "provenance": {
3706
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3707
+ },
3708
+ "attrs": { "pads": [0, 1, 0, 1] },
3709
+ "inputs": {
3710
+ "x": {
3711
+ "dtype": "float16",
3712
+ "shape": [2, 32, 8, 8],
3713
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3714
+ },
3715
+ "w": {
3716
+ "dtype": "float16",
3717
+ "shape": [128, 32, 1, 3],
3718
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3719
+ }
3720
+ },
3721
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 128, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3722
+ },
3723
+ {
3724
+ "name": "direct_columns_float16_m128_c32_rank2_n8x8_bias1",
3725
+ "provenance": {
3726
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3727
+ },
3728
+ "attrs": { "pads": [0, 1, 0, 1] },
3729
+ "inputs": {
3730
+ "x": {
3731
+ "dtype": "float16",
3732
+ "shape": [2, 32, 8, 8],
3733
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3734
+ },
3735
+ "w": {
3736
+ "dtype": "float16",
3737
+ "shape": [128, 32, 1, 3],
3738
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3739
+ },
3740
+ "bias": {
3741
+ "dtype": "float16",
3742
+ "shape": [128],
3743
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3744
+ }
3745
+ },
3746
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 128, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3747
+ },
3748
+ {
3749
+ "name": "direct_columns_float16_m31_c32_rank2_n8x8_bias0",
3750
+ "provenance": {
3751
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3752
+ },
3753
+ "attrs": { "pads": [0, 1, 0, 1] },
3754
+ "inputs": {
3755
+ "x": {
3756
+ "dtype": "float16",
3757
+ "shape": [2, 32, 8, 8],
3758
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3759
+ },
3760
+ "w": {
3761
+ "dtype": "float16",
3762
+ "shape": [31, 32, 1, 3],
3763
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3764
+ }
3765
+ },
3766
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 31, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3767
+ },
3768
+ {
3769
+ "name": "direct_columns_float16_m33_c32_rank2_n8x8_bias0",
3770
+ "provenance": {
3771
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3772
+ },
3773
+ "attrs": { "pads": [0, 1, 0, 1] },
3774
+ "inputs": {
3775
+ "x": {
3776
+ "dtype": "float16",
3777
+ "shape": [2, 32, 8, 8],
3778
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3779
+ },
3780
+ "w": {
3781
+ "dtype": "float16",
3782
+ "shape": [33, 32, 1, 3],
3783
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3784
+ }
3785
+ },
3786
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 33, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3787
+ },
3788
+ {
3789
+ "name": "direct_columns_float16_m65_c32_rank2_n8x8_bias0",
3790
+ "provenance": {
3791
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3792
+ },
3793
+ "attrs": { "pads": [0, 1, 0, 1] },
3794
+ "inputs": {
3795
+ "x": {
3796
+ "dtype": "float16",
3797
+ "shape": [2, 32, 8, 8],
3798
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3799
+ },
3800
+ "w": {
3801
+ "dtype": "float16",
3802
+ "shape": [65, 32, 1, 3],
3803
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3804
+ }
3805
+ },
3806
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 65, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3807
+ },
3808
+ {
3809
+ "name": "direct_columns_float16_m64_c31_rank2_n8x8_bias0",
3810
+ "provenance": {
3811
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3812
+ },
3813
+ "attrs": { "pads": [0, 1, 0, 1] },
3814
+ "inputs": {
3815
+ "x": {
3816
+ "dtype": "float16",
3817
+ "shape": [2, 31, 8, 8],
3818
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3819
+ },
3820
+ "w": {
3821
+ "dtype": "float16",
3822
+ "shape": [64, 31, 1, 3],
3823
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3824
+ }
3825
+ },
3826
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 8, 8], "tolerance": 0.001, "relTolerance": 0.001 } }
3827
+ },
3828
+ {
3829
+ "name": "direct_columns_float16_m64_c32_rank2_n7x9_bias0",
3830
+ "provenance": {
3831
+ "notes": "Direct column matrix inputs cover both input scalar widths, full row bands, batched NCW/NCHW indexing and bias. A shallow reduction retains the unsplit accumulation order; incomplete rows/K/N exercise guarded fallbacks."
3832
+ },
3833
+ "attrs": { "pads": [0, 1, 0, 1] },
3834
+ "inputs": {
3835
+ "x": {
3836
+ "dtype": "float16",
3837
+ "shape": [2, 32, 7, 9],
3838
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3839
+ },
3840
+ "w": {
3841
+ "dtype": "float16",
3842
+ "shape": [64, 32, 1, 3],
3843
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3844
+ }
3845
+ },
3846
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 7, 9], "tolerance": 0.001, "relTolerance": 0.001 } }
3847
+ },
3848
+ {
3849
+ "name": "direct_columns_float32_m128_c32_rank2_n32x32_bias1",
3850
+ "provenance": {
3851
+ "notes": "Full spatial tiles on each side of the output-channel reuse preference: retain implicit gathering at low reuse and select direct materialization at high reuse. Batched bias outputs retain the CPU oracle and default dtype tolerance."
3852
+ },
3853
+ "attrs": { "pads": [0, 1, 0, 1] },
3854
+ "inputs": {
3855
+ "x": {
3856
+ "dtype": "float32",
3857
+ "shape": [2, 32, 32, 32],
3858
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3859
+ },
3860
+ "w": {
3861
+ "dtype": "float32",
3862
+ "shape": [128, 32, 1, 3],
3863
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3864
+ },
3865
+ "bias": {
3866
+ "dtype": "float32",
3867
+ "shape": [128],
3868
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3869
+ }
3870
+ },
3871
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 128, 32, 32], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3872
+ },
3873
+ {
3874
+ "name": "direct_columns_float32_m512_c32_rank2_n32x32_bias1",
3875
+ "provenance": {
3876
+ "notes": "Full spatial tiles on each side of the output-channel reuse preference: retain implicit gathering at low reuse and select direct materialization at high reuse. Batched bias outputs retain the CPU oracle and default dtype tolerance."
3877
+ },
3878
+ "attrs": { "pads": [0, 1, 0, 1] },
3879
+ "inputs": {
3880
+ "x": {
3881
+ "dtype": "float32",
3882
+ "shape": [2, 32, 32, 32],
3883
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3884
+ },
3885
+ "w": {
3886
+ "dtype": "float32",
3887
+ "shape": [512, 32, 1, 3],
3888
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3889
+ },
3890
+ "bias": {
3891
+ "dtype": "float32",
3892
+ "shape": [512],
3893
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3894
+ }
3895
+ },
3896
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 512, 32, 32], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3897
+ },
3898
+ {
3899
+ "name": "direct_columns_float16_m64_c32_rank2_n32x32_bias1",
3900
+ "provenance": {
3901
+ "notes": "Full spatial tiles on each side of the output-channel reuse preference: retain implicit gathering at low reuse and select direct materialization at high reuse. Batched bias outputs retain the CPU oracle and default dtype tolerance."
3902
+ },
3903
+ "attrs": { "pads": [0, 1, 0, 1] },
3904
+ "inputs": {
3905
+ "x": {
3906
+ "dtype": "float16",
3907
+ "shape": [2, 32, 32, 32],
3908
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3909
+ },
3910
+ "w": {
3911
+ "dtype": "float16",
3912
+ "shape": [64, 32, 1, 3],
3913
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3914
+ },
3915
+ "bias": {
3916
+ "dtype": "float16",
3917
+ "shape": [64],
3918
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3919
+ }
3920
+ },
3921
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 32, 32], "tolerance": 0.001, "relTolerance": 0.001 } }
3922
+ },
3923
+ {
3924
+ "name": "direct_columns_float16_m256_c32_rank2_n32x32_bias1",
3925
+ "provenance": {
3926
+ "notes": "Full spatial tiles on each side of the output-channel reuse preference: retain implicit gathering at low reuse and select direct materialization at high reuse. Batched bias outputs retain the CPU oracle and default dtype tolerance."
3927
+ },
3928
+ "attrs": { "pads": [0, 1, 0, 1] },
3929
+ "inputs": {
3930
+ "x": {
3931
+ "dtype": "float16",
3932
+ "shape": [2, 32, 32, 32],
3933
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.037, "cosStep": 0.013 }
3934
+ },
3935
+ "w": {
3936
+ "dtype": "float16",
3937
+ "shape": [256, 32, 1, 3],
3938
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.017, "cosStep": 0.029 }
3939
+ },
3940
+ "bias": {
3941
+ "dtype": "float16",
3942
+ "shape": [256],
3943
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.19, "cosStep": 0.31 }
3944
+ }
3945
+ },
3946
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 256, 32, 32], "tolerance": 0.001, "relTolerance": 0.001 } }
3947
+ },
3948
+ {
3949
+ "name": "direct_1x1_float32_m32_k32_n64_mode0",
3950
+ "provenance": {
3951
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
3952
+ },
3953
+ "attrs": { "pads": [0, 0, 0, 0] },
3954
+ "inputs": {
3955
+ "x": {
3956
+ "dtype": "float32",
3957
+ "shape": [2, 32, 1, 64],
3958
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
3959
+ },
3960
+ "w": {
3961
+ "dtype": "float32",
3962
+ "shape": [32, 32, 1, 1],
3963
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
3964
+ }
3965
+ },
3966
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3967
+ },
3968
+ {
3969
+ "name": "direct_1x1_float32_m32_k32_n64_mode1",
3970
+ "provenance": {
3971
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
3972
+ },
3973
+ "attrs": { "pads": [0, 0, 0, 0] },
3974
+ "inputs": {
3975
+ "x": {
3976
+ "dtype": "float32",
3977
+ "shape": [2, 32, 1, 64],
3978
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
3979
+ },
3980
+ "w": {
3981
+ "dtype": "float32",
3982
+ "shape": [32, 32, 1, 1],
3983
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
3984
+ },
3985
+ "bias": {
3986
+ "dtype": "float32",
3987
+ "shape": [32],
3988
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.23, "cosStep": 0.391 }
3989
+ }
3990
+ },
3991
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 32, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
3992
+ },
3993
+ {
3994
+ "name": "direct_1x1_float32_m64_k32_n64_mode0",
3995
+ "provenance": {
3996
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
3997
+ },
3998
+ "attrs": { "pads": [0, 0, 0, 0] },
3999
+ "inputs": {
4000
+ "x": {
4001
+ "dtype": "float32",
4002
+ "shape": [2, 32, 1, 64],
4003
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4004
+ },
4005
+ "w": {
4006
+ "dtype": "float32",
4007
+ "shape": [64, 32, 1, 1],
4008
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4009
+ }
4010
+ },
4011
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4012
+ },
4013
+ {
4014
+ "name": "direct_1x1_float32_m64_k32_n64_mode1",
4015
+ "provenance": {
4016
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4017
+ },
4018
+ "attrs": { "pads": [0, 0, 0, 0] },
4019
+ "inputs": {
4020
+ "x": {
4021
+ "dtype": "float32",
4022
+ "shape": [2, 32, 1, 64],
4023
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4024
+ },
4025
+ "w": {
4026
+ "dtype": "float32",
4027
+ "shape": [64, 32, 1, 1],
4028
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4029
+ },
4030
+ "bias": {
4031
+ "dtype": "float32",
4032
+ "shape": [64],
4033
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.23, "cosStep": 0.391 }
4034
+ }
4035
+ },
4036
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4037
+ },
4038
+ {
4039
+ "name": "direct_1x1_float32_m33_k32_n64_mode0",
4040
+ "provenance": {
4041
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4042
+ },
4043
+ "attrs": { "pads": [0, 0, 0, 0] },
4044
+ "inputs": {
4045
+ "x": {
4046
+ "dtype": "float32",
4047
+ "shape": [2, 32, 1, 64],
4048
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4049
+ },
4050
+ "w": {
4051
+ "dtype": "float32",
4052
+ "shape": [33, 32, 1, 1],
4053
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4054
+ }
4055
+ },
4056
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 33, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4057
+ },
4058
+ {
4059
+ "name": "direct_1x1_float32_m65_k32_n64_mode0",
4060
+ "provenance": {
4061
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4062
+ },
4063
+ "attrs": { "pads": [0, 0, 0, 0] },
4064
+ "inputs": {
4065
+ "x": {
4066
+ "dtype": "float32",
4067
+ "shape": [2, 32, 1, 64],
4068
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4069
+ },
4070
+ "w": {
4071
+ "dtype": "float32",
4072
+ "shape": [65, 32, 1, 1],
4073
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4074
+ }
4075
+ },
4076
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 65, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4077
+ },
4078
+ {
4079
+ "name": "direct_1x1_float32_m96_k32_n64_mode0",
4080
+ "provenance": {
4081
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4082
+ },
4083
+ "attrs": { "pads": [0, 0, 0, 0] },
4084
+ "inputs": {
4085
+ "x": {
4086
+ "dtype": "float32",
4087
+ "shape": [2, 32, 1, 64],
4088
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4089
+ },
4090
+ "w": {
4091
+ "dtype": "float32",
4092
+ "shape": [96, 32, 1, 1],
4093
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4094
+ }
4095
+ },
4096
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 96, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4097
+ },
4098
+ {
4099
+ "name": "direct_1x1_float32_m64_k31_n64_mode0",
4100
+ "provenance": {
4101
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4102
+ },
4103
+ "attrs": { "pads": [0, 0, 0, 0] },
4104
+ "inputs": {
4105
+ "x": {
4106
+ "dtype": "float32",
4107
+ "shape": [2, 31, 1, 64],
4108
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4109
+ },
4110
+ "w": {
4111
+ "dtype": "float32",
4112
+ "shape": [64, 31, 1, 1],
4113
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4114
+ }
4115
+ },
4116
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4117
+ },
4118
+ {
4119
+ "name": "direct_1x1_float32_m64_k33_n64_mode0",
4120
+ "provenance": {
4121
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4122
+ },
4123
+ "attrs": { "pads": [0, 0, 0, 0] },
4124
+ "inputs": {
4125
+ "x": {
4126
+ "dtype": "float32",
4127
+ "shape": [2, 33, 1, 64],
4128
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4129
+ },
4130
+ "w": {
4131
+ "dtype": "float32",
4132
+ "shape": [64, 33, 1, 1],
4133
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4134
+ }
4135
+ },
4136
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 1, 64], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4137
+ },
4138
+ {
4139
+ "name": "direct_1x1_float32_m64_k32_n63_mode0",
4140
+ "provenance": {
4141
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4142
+ },
4143
+ "attrs": { "pads": [0, 0, 0, 0] },
4144
+ "inputs": {
4145
+ "x": {
4146
+ "dtype": "float32",
4147
+ "shape": [2, 32, 1, 63],
4148
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4149
+ },
4150
+ "w": {
4151
+ "dtype": "float32",
4152
+ "shape": [64, 32, 1, 1],
4153
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4154
+ }
4155
+ },
4156
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 1, 63], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4157
+ },
4158
+ {
4159
+ "name": "direct_1x1_float32_m64_k32_n65_mode0",
4160
+ "provenance": {
4161
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4162
+ },
4163
+ "attrs": { "pads": [0, 0, 0, 0] },
4164
+ "inputs": {
4165
+ "x": {
4166
+ "dtype": "float32",
4167
+ "shape": [2, 32, 1, 65],
4168
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4169
+ },
4170
+ "w": {
4171
+ "dtype": "float32",
4172
+ "shape": [64, 32, 1, 1],
4173
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4174
+ }
4175
+ },
4176
+ "outputs": { "y": { "dtype": "float32", "shape": [2, 64, 1, 65], "tolerance": 0.0001, "relTolerance": 0.0001 } }
4177
+ },
4178
+ {
4179
+ "name": "direct_1x1_float16_m32_k32_n64_mode0",
4180
+ "provenance": {
4181
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4182
+ },
4183
+ "attrs": { "pads": [0, 0, 0, 0] },
4184
+ "inputs": {
4185
+ "x": {
4186
+ "dtype": "float16",
4187
+ "shape": [2, 32, 1, 64],
4188
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4189
+ },
4190
+ "w": {
4191
+ "dtype": "float16",
4192
+ "shape": [32, 32, 1, 1],
4193
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4194
+ }
4195
+ },
4196
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4197
+ },
4198
+ {
4199
+ "name": "direct_1x1_float16_m32_k32_n64_mode1",
4200
+ "provenance": {
4201
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4202
+ },
4203
+ "attrs": { "pads": [0, 0, 0, 0] },
4204
+ "inputs": {
4205
+ "x": {
4206
+ "dtype": "float16",
4207
+ "shape": [2, 32, 1, 64],
4208
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4209
+ },
4210
+ "w": {
4211
+ "dtype": "float16",
4212
+ "shape": [32, 32, 1, 1],
4213
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4214
+ },
4215
+ "bias": {
4216
+ "dtype": "float16",
4217
+ "shape": [32],
4218
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.23, "cosStep": 0.391 }
4219
+ }
4220
+ },
4221
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 32, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4222
+ },
4223
+ {
4224
+ "name": "direct_1x1_float16_m64_k32_n64_mode0",
4225
+ "provenance": {
4226
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4227
+ },
4228
+ "attrs": { "pads": [0, 0, 0, 0] },
4229
+ "inputs": {
4230
+ "x": {
4231
+ "dtype": "float16",
4232
+ "shape": [2, 32, 1, 64],
4233
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4234
+ },
4235
+ "w": {
4236
+ "dtype": "float16",
4237
+ "shape": [64, 32, 1, 1],
4238
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4239
+ }
4240
+ },
4241
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4242
+ },
4243
+ {
4244
+ "name": "direct_1x1_float16_m64_k32_n64_mode1",
4245
+ "provenance": {
4246
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4247
+ },
4248
+ "attrs": { "pads": [0, 0, 0, 0] },
4249
+ "inputs": {
4250
+ "x": {
4251
+ "dtype": "float16",
4252
+ "shape": [2, 32, 1, 64],
4253
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4254
+ },
4255
+ "w": {
4256
+ "dtype": "float16",
4257
+ "shape": [64, 32, 1, 1],
4258
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4259
+ },
4260
+ "bias": {
4261
+ "dtype": "float16",
4262
+ "shape": [64],
4263
+ "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.23, "cosStep": 0.391 }
4264
+ }
4265
+ },
4266
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4267
+ },
4268
+ {
4269
+ "name": "direct_1x1_float16_m33_k32_n64_mode0",
4270
+ "provenance": {
4271
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4272
+ },
4273
+ "attrs": { "pads": [0, 0, 0, 0] },
4274
+ "inputs": {
4275
+ "x": {
4276
+ "dtype": "float16",
4277
+ "shape": [2, 32, 1, 64],
4278
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4279
+ },
4280
+ "w": {
4281
+ "dtype": "float16",
4282
+ "shape": [33, 32, 1, 1],
4283
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4284
+ }
4285
+ },
4286
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 33, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4287
+ },
4288
+ {
4289
+ "name": "direct_1x1_float16_m65_k32_n64_mode0",
4290
+ "provenance": {
4291
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4292
+ },
4293
+ "attrs": { "pads": [0, 0, 0, 0] },
4294
+ "inputs": {
4295
+ "x": {
4296
+ "dtype": "float16",
4297
+ "shape": [2, 32, 1, 64],
4298
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4299
+ },
4300
+ "w": {
4301
+ "dtype": "float16",
4302
+ "shape": [65, 32, 1, 1],
4303
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4304
+ }
4305
+ },
4306
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 65, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4307
+ },
4308
+ {
4309
+ "name": "direct_1x1_float16_m96_k32_n64_mode0",
4310
+ "provenance": {
4311
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4312
+ },
4313
+ "attrs": { "pads": [0, 0, 0, 0] },
4314
+ "inputs": {
4315
+ "x": {
4316
+ "dtype": "float16",
4317
+ "shape": [2, 32, 1, 64],
4318
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4319
+ },
4320
+ "w": {
4321
+ "dtype": "float16",
4322
+ "shape": [96, 32, 1, 1],
4323
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4324
+ }
4325
+ },
4326
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 96, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4327
+ },
4328
+ {
4329
+ "name": "direct_1x1_float16_m64_k31_n64_mode0",
4330
+ "provenance": {
4331
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4332
+ },
4333
+ "attrs": { "pads": [0, 0, 0, 0] },
4334
+ "inputs": {
4335
+ "x": {
4336
+ "dtype": "float16",
4337
+ "shape": [2, 31, 1, 64],
4338
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4339
+ },
4340
+ "w": {
4341
+ "dtype": "float16",
4342
+ "shape": [64, 31, 1, 1],
4343
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4344
+ }
4345
+ },
4346
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4347
+ },
4348
+ {
4349
+ "name": "direct_1x1_float16_m64_k33_n64_mode0",
4350
+ "provenance": {
4351
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4352
+ },
4353
+ "attrs": { "pads": [0, 0, 0, 0] },
4354
+ "inputs": {
4355
+ "x": {
4356
+ "dtype": "float16",
4357
+ "shape": [2, 33, 1, 64],
4358
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4359
+ },
4360
+ "w": {
4361
+ "dtype": "float16",
4362
+ "shape": [64, 33, 1, 1],
4363
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4364
+ }
4365
+ },
4366
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 1, 64], "tolerance": 0.001, "relTolerance": 0.001 } }
4367
+ },
4368
+ {
4369
+ "name": "direct_1x1_float16_m64_k32_n63_mode0",
4370
+ "provenance": {
4371
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4372
+ },
4373
+ "attrs": { "pads": [0, 0, 0, 0] },
4374
+ "inputs": {
4375
+ "x": {
4376
+ "dtype": "float16",
4377
+ "shape": [2, 32, 1, 63],
4378
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4379
+ },
4380
+ "w": {
4381
+ "dtype": "float16",
4382
+ "shape": [64, 32, 1, 1],
4383
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4384
+ }
4385
+ },
4386
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 1, 63], "tolerance": 0.001, "relTolerance": 0.001 } }
4387
+ },
4388
+ {
4389
+ "name": "direct_1x1_float16_m64_k32_n65_mode0",
4390
+ "provenance": {
4391
+ "notes": "Batched direct 1x1 matrix loads cover both scalar widths and fused epilogues. Neighboring incomplete M/K/N tiles retain guarded staging or the existing fallback; device limits determine the row tile."
4392
+ },
4393
+ "attrs": { "pads": [0, 0, 0, 0] },
4394
+ "inputs": {
4395
+ "x": {
4396
+ "dtype": "float16",
4397
+ "shape": [2, 32, 1, 65],
4398
+ "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.013, "cosStep": 0.022099999999999998 }
4399
+ },
4400
+ "w": {
4401
+ "dtype": "float16",
4402
+ "shape": [64, 32, 1, 1],
4403
+ "data": { "kind": "fillFloat32", "scale": 0.05, "sinStep": 0.031, "cosStep": 0.0527 }
4404
+ }
4405
+ },
4406
+ "outputs": { "y": { "dtype": "float16", "shape": [2, 64, 1, 65], "tolerance": 0.001, "relTolerance": 0.001 } }
4407
  }
4408
  ]
4409
  }