Xenova HF Staff commited on
Commit
14456f5
·
verified ·
1 Parent(s): 26fd85e

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,17 +18,17 @@ See the [ONNX Runtime `GemmFastGelu` contrib-operator spec](https://github.com/m
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
- | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `X` | `T` | — | — | Left operand of rank 2 or greater with shape `(..., K)`; every leading-axis coordinate identifies a row of the product. | required |
24
- | `W` | `W` | `T` | `2` | — | Right operand with shape `(K, N)`. | required |
25
- | `bias` | `bias` | `T` | `1` | — | Optional bias with shape `(N)`, added before the activation. | optional |
26
 
27
  ## Outputs
28
 
29
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
30
- | --- | --- | --- | --- | --- | --- | --- |
31
- | `Y` | `Y` | `T` | same as `X` | ONNX MatMul result of `X` and `W` | `FastGelu(X @ W + bias)`, with the same rank and leading dimensions as `X` and a trailing `N`. | required |
32
 
33
  ## Type constraints
34
 
@@ -36,13 +36,26 @@ See the [ONNX Runtime `GemmFastGelu` contrib-operator spec](https://github.com/m
36
  | --- | --- |
37
  | `T` | `float32`, `float16` |
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  ## Device requirements
40
 
41
  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.
42
 
43
  ## Files
44
 
45
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
46
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
47
  - [`test.json`](build/webgpu/test.json) — correctness cases
48
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -51,10 +64,14 @@ Some implementation variants require `subgroup-matrix` and `subgroups`. These ar
51
 
52
  ## Use with `@huggingface/kernels`
53
 
54
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
55
- It then allocates the result tensors automatically.
 
 
 
56
 
57
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
58
 
59
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
60
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- |
23
+ | `X` | `T` | — | — | Left operand of rank 2 or greater with shape `(..., K)`; every leading-axis coordinate identifies a row of the product. | required |
24
+ | `W` | `T` | `2` | — | Right operand with shape `(K, N)`. | required |
25
+ | `bias` | `T` | `1` | — | Optional bias with shape `(N)`, added before the activation. | optional |
26
 
27
  ## Outputs
28
 
29
+ | Name | Logical dtype | Rank | Shape | Description | Presence |
30
+ | --- | --- | --- | --- | --- | --- |
31
+ | `Y` | `T` | same as `X` | ONNX MatMul result of `X` and `W` | `FastGelu(X @ W + bias)`, with the same rank and leading dimensions as `X` and a trailing `N`. | required |
32
 
33
  ## Type constraints
34
 
 
36
  | --- | --- |
37
  | `T` | `float32`, `float16` |
38
 
39
+ ## Implementation variants
40
+
41
+ One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
42
+
43
+ - `sgmat_direct_bias` — Uses supported subgroup matrices with direct activation loads and double-buffered weights. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
44
+ - `sgmat_direct` — Uses supported subgroup matrices with direct activation loads and double-buffered weights. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
45
+ - `sgmat_direct_bias_f16` — Uses supported subgroup matrices with direct activation loads and double-buffered weights. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
46
+ - `sgmat_direct_f16` — Uses supported subgroup matrices with direct activation loads and double-buffered weights. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
47
+ - `sgmat_bias` — Uses supported subgroup matrices with staged operands. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
48
+ - `sgmat` — Uses supported subgroup matrices with staged operands. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
49
+ - `sgmat_bias_f16` — Uses supported subgroup matrices with staged operands. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
50
+ - `sgmat_f16` — Uses supported subgroup matrices with staged operands. Bias and FastGelu run on the f32 accumulator before the single output cast. Resource, alignment and matrix-type guards retain the portable path on other devices.
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, per-variant templates, 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
 
64
 
65
  ## Use with `@huggingface/kernels`
66
 
67
+ ```sh
68
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
69
+ ```
70
+
71
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
72
 
73
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
74
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
75
 
76
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
77
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "com.microsoft.GemmFastGelu",
3
  "cases": [
4
  {
5
  "name": "gemmfastgelu-bert-base-b8-s384-h768-i3072",
@@ -47,6 +46,74 @@
47
  },
48
  "outputs": { "Y": { "shape": [8, 384, 3072], "dtype": "float32" } },
49
  "bench": { "metrics": [{ "type": "gflops", "value": "2 * 3072 * 768 * 3072" }] }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
  ]
52
  }
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "gemmfastgelu-bert-base-b8-s384-h768-i3072",
 
46
  },
47
  "outputs": { "Y": { "shape": [8, 384, 3072], "dtype": "float32" } },
48
  "bench": { "metrics": [{ "type": "gflops", "value": "2 * 3072 * 768 * 3072" }] }
49
+ },
50
+ {
51
+ "name": "gemmfastgelu-f32-short-m1-h768-i3072-sgmat-floor-pathology",
52
+ "preset": "model",
53
+ "vars": { "dtype": "float32" },
54
+ "inputs": {
55
+ "X": { "shape": [1, 768], "dtype": "float32", "dist": "normal", "seed": 5250, "scale": 1 },
56
+ "W": { "shape": [768, 3072], "dtype": "float32", "dist": "normal", "seed": 5251, "scale": 1 },
57
+ "bias": { "shape": [3072], "dtype": "float32", "dist": "normal", "seed": 5252, "scale": 1 }
58
+ },
59
+ "outputs": { "Y": { "shape": [1, 3072], "dtype": "float32" } },
60
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 1 * 768 * 3072" }] }
61
+ },
62
+ {
63
+ "name": "gemmfastgelu-f32-short-m64-h768-i3072-control",
64
+ "preset": "model",
65
+ "vars": { "dtype": "float32" },
66
+ "inputs": {
67
+ "X": { "shape": [64, 768], "dtype": "float32", "dist": "normal", "seed": 5270, "scale": 1 },
68
+ "W": { "shape": [768, 3072], "dtype": "float32", "dist": "normal", "seed": 5271, "scale": 1 },
69
+ "bias": { "shape": [3072], "dtype": "float32", "dist": "normal", "seed": 5272, "scale": 1 }
70
+ },
71
+ "outputs": { "Y": { "shape": [64, 3072], "dtype": "float32" } },
72
+ "bench": { "metrics": [{ "type": "gflops", "value": "2 * 64 * 768 * 3072" }] }
73
+ },
74
+ {
75
+ "name": "gemmfastgelu-float32-bias-m128-k512-n96",
76
+ "preset": "model",
77
+ "inputs": {
78
+ "X": { "shape": [128, 512], "dtype": "float32", "dist": "normal", "seed": 7100, "scale": 0.3 },
79
+ "W": { "shape": [512, 96], "dtype": "float32", "dist": "normal", "seed": 7101, "scale": 0.2 },
80
+ "bias": { "shape": [96], "dtype": "float32", "dist": "normal", "seed": 7102, "scale": 0.3 }
81
+ },
82
+ "outputs": { "Y": { "shape": [128, 96], "dtype": "float32" } },
83
+ "bench": { "metrics": [{ "type": "gflops", "value": 12582912 }] }
84
+ },
85
+ {
86
+ "name": "gemmfastgelu-float32-bias-m128-k1024-n1024",
87
+ "preset": "model",
88
+ "inputs": {
89
+ "X": { "shape": [128, 1024], "dtype": "float32", "dist": "normal", "seed": 7100, "scale": 0.3 },
90
+ "W": { "shape": [1024, 1024], "dtype": "float32", "dist": "normal", "seed": 7101, "scale": 0.2 },
91
+ "bias": { "shape": [1024], "dtype": "float32", "dist": "normal", "seed": 7102, "scale": 0.3 }
92
+ },
93
+ "outputs": { "Y": { "shape": [128, 1024], "dtype": "float32" } },
94
+ "bench": { "metrics": [{ "type": "gflops", "value": 268435456 }] }
95
+ },
96
+ {
97
+ "name": "gemmfastgelu-float16-bias-m128-k512-n96",
98
+ "preset": "model",
99
+ "inputs": {
100
+ "X": { "shape": [128, 512], "dtype": "float16", "dist": "normal", "seed": 7100, "scale": 0.3 },
101
+ "W": { "shape": [512, 96], "dtype": "float16", "dist": "normal", "seed": 7101, "scale": 0.2 },
102
+ "bias": { "shape": [96], "dtype": "float16", "dist": "normal", "seed": 7102, "scale": 0.3 }
103
+ },
104
+ "outputs": { "Y": { "shape": [128, 96], "dtype": "float16" } },
105
+ "bench": { "metrics": [{ "type": "gflops", "value": 12582912 }] }
106
+ },
107
+ {
108
+ "name": "gemmfastgelu-float16-bias-m128-k1024-n1024",
109
+ "preset": "model",
110
+ "inputs": {
111
+ "X": { "shape": [128, 1024], "dtype": "float16", "dist": "normal", "seed": 7100, "scale": 0.3 },
112
+ "W": { "shape": [1024, 1024], "dtype": "float16", "dist": "normal", "seed": 7101, "scale": 0.2 },
113
+ "bias": { "shape": [1024], "dtype": "float16", "dist": "normal", "seed": 7102, "scale": 0.3 }
114
+ },
115
+ "outputs": { "Y": { "shape": [128, 1024], "dtype": "float16" } },
116
+ "bench": { "metrics": [{ "type": "gflops", "value": 268435456 }] }
117
  }
118
  ]
119
  }
build/webgpu/gemm-fast-gelu.wgsl.jinja CHANGED
@@ -10,7 +10,6 @@
10
  // vector word, so the micro-tile accumulates through dot() and one step reads
11
  // TM + TN words instead of 4 * (TM + TN) scalars.
12
  {% set gemmEpi = gemmEpilogue if gemmEpilogue is defined else "none" %}
13
- {% if gemmEpi == "fastgelu" %}
14
  fn tanh_safe(x: f32) -> f32 {
15
  if (x > 10.0) { return 1.0; }
16
  if (x < -10.0) { return -1.0; }
@@ -20,33 +19,6 @@ fn tanh_safe(x: f32) -> f32 {
20
  fn gelu_tanh(v: f32) -> f32 {
21
  return 0.5 * v * (1.0 + tanh_safe(0.7978845608028654 * (v + 0.044715 * v * v * v)));
22
  }
23
- {% elif gemmEpi == "activation" %}
24
- {% set actMode = gemmActivation | default("") %}
25
- {% set actA = gemmActAlpha | default(0.0) %}
26
- {% set actB = gemmActBeta | default(0.0) %}
27
- fn fused_act(v: f32) -> f32 {
28
- {% if actMode == "Relu" %}
29
- return max(v, 0.0);
30
- {% endif %}
31
- {% if actMode == "LeakyRelu" %}
32
- return select(v * f32({{ actA }}), v, v >= 0.0);
33
- {% endif %}
34
- {% if actMode == "Sigmoid" %}
35
- return 1.0 / (1.0 + exp(-v));
36
- {% endif %}
37
- {% if actMode == "Tanh" %}
38
- // Clamp first: the hardware tanh evaluates (e^2v - 1)/(e^2v + 1), which is
39
- // Inf/Inf = NaN once e^2v overflows (v > ~44). A GEMM accumulator reaches that
40
- // easily because it grows with K. tanh is already +/-1 to full f32 precision by
41
- // |v| ~ 9, so the clamp changes no representable result.
42
- return tanh(clamp(v, -10.0, 10.0));
43
- {% endif %}
44
- {% if actMode == "HardSigmoid" %}
45
- return clamp(f32({{ actA }}) * v + f32({{ actB }}), 0.0, 1.0);
46
- {% endif %}
47
- }
48
-
49
- {% endif %}
50
  {% set epiOpen = "gelu_tanh(" if gemmEpi == "fastgelu" else ("fused_act(" if gemmEpi == "activation" else "") %}
51
  {% set epiClose = ")" if gemmEpi != "none" else "" %}
52
  const BK: u32 = 16u;
@@ -89,71 +61,54 @@ fn main(
89
  // Cooperative load: one vector word per lane per pass. The lane grid is
90
  // laid out so the axis the operand stores contiguously stays coalesced.
91
  for (var idx: u32 = li; idx < BM * K_VECS; idx = idx + 256u) {
92
- {% if transA %}
93
- // A stored [K, M]: logical A'[m,k] = a[k*M + m]. Coalesce on m (contiguous).
94
- let ar = idx % BM;
95
- let ac4 = idx / BM;
96
- {% else %}
97
  // A stored [M, K]: logical A[m,k] = a[m*K + k]. Coalesce on k (contiguous).
98
  let ar = idx / K_VECS;
99
  let ac4 = idx % K_VECS;
100
- {% endif %}
101
  let am = mBase + ar;
102
  let ak = kBase + ac4 * 4u;
103
  var aWord = vec4<{{ tileT }}>({{ tileT }}(0.0));
104
  if (am < M) {
105
  {% for component in range(4) %}
106
  if (ak + {{ component }}u < K) {
107
- {% if transA %}
108
- aWord[{{ component }}u] = {{ tileT }}(a[(ak + {{ component }}u) * M + am]);
109
- {% else %}
110
  aWord[{{ component }}u] = {{ tileT }}(a[am * K + ak + {{ component }}u]);
111
- {% endif %}
112
  }
113
  {% endfor %}
114
  }
115
  tileA[ar][ac4] = aWord;
116
  }
117
  for (var idx: u32 = li; idx < BN * K_VECS; idx = idx + 256u) {
118
- {% if transB is defined and transB %}
119
- // B stored [N, K]: logical B[k,n] = b[n*K + k]. Coalesce on k (contiguous).
120
- let bc = idx / K_VECS;
121
- let br4 = idx % K_VECS;
122
- {% else %}
123
  // B stored [K, N]: logical B[k,n] = b[k*N + n]. Coalesce on n (contiguous).
124
  let bc = idx % BN;
125
  let br4 = idx / BN;
126
- {% endif %}
127
  let bn = nBase + bc;
128
  let bk = kBase + br4 * 4u;
129
  var bWord = vec4<{{ tileT }}>({{ tileT }}(0.0));
130
  if (bn < N) {
131
  {% for component in range(4) %}
132
  if (bk + {{ component }}u < K) {
133
- {% if transB is defined and transB %}
134
- bWord[{{ component }}u] = {{ tileT }}(b[bn * K + bk + {{ component }}u]);
135
- {% else %}
136
  bWord[{{ component }}u] = {{ tileT }}(b[(bk + {{ component }}u) * N + bn]);
137
- {% endif %}
138
  }
139
  {% endfor %}
140
  }
141
  tileB[bc][br4] = bWord;
142
  }
143
  workgroupBarrier();
144
- {% set regIndent = " " %}{% set regT = "f32" %}{% set regCast = 1 if tileT != "f32" else 0 %}{{ regIndent }}let aRow = lid.y * TM;
145
- {{ regIndent }}let bCol = lid.x * TN;
146
- {{ regIndent }}for (var kv: u32 = 0u; kv < BK / 4u; kv = kv + 1u) {
147
- {{ regIndent }} var av: array<vec4<{{ regT }}>, TM>;
148
- {{ regIndent }} var bv: array<vec4<{{ regT }}>, TN>;
149
- {{ regIndent }} for (var i: u32 = 0u; i < TM; i = i + 1u) { av[i] = {% if regCast %}vec4<{{ regT }}>(tileA[aRow + i][kv]){% else %}tileA[aRow + i][kv]{% endif %}; }
150
- {{ regIndent }} for (var j: u32 = 0u; j < TN; j = j + 1u) { bv[j] = {% if regCast %}vec4<{{ regT }}>(tileB[bCol + j][kv]){% else %}tileB[bCol + j][kv]{% endif %}; }
151
- {{ regIndent }} for (var i: u32 = 0u; i < TM; i = i + 1u) {
152
- {{ regIndent }} for (var j: u32 = 0u; j < TN; j = j + 1u) {
153
- {{ regIndent }} acc[i * TN + j] = acc[i * TN + j] + dot(av[i], bv[j]);
154
- {{ regIndent }} }
155
- {{ regIndent }} }
156
- {{ regIndent }}}
 
 
 
157
  workgroupBarrier();
158
  }
159
 
 
10
  // vector word, so the micro-tile accumulates through dot() and one step reads
11
  // TM + TN words instead of 4 * (TM + TN) scalars.
12
  {% set gemmEpi = gemmEpilogue if gemmEpilogue is defined else "none" %}
 
13
  fn tanh_safe(x: f32) -> f32 {
14
  if (x > 10.0) { return 1.0; }
15
  if (x < -10.0) { return -1.0; }
 
19
  fn gelu_tanh(v: f32) -> f32 {
20
  return 0.5 * v * (1.0 + tanh_safe(0.7978845608028654 * (v + 0.044715 * v * v * v)));
21
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  {% set epiOpen = "gelu_tanh(" if gemmEpi == "fastgelu" else ("fused_act(" if gemmEpi == "activation" else "") %}
23
  {% set epiClose = ")" if gemmEpi != "none" else "" %}
24
  const BK: u32 = 16u;
 
61
  // Cooperative load: one vector word per lane per pass. The lane grid is
62
  // laid out so the axis the operand stores contiguously stays coalesced.
63
  for (var idx: u32 = li; idx < BM * K_VECS; idx = idx + 256u) {
 
 
 
 
 
64
  // A stored [M, K]: logical A[m,k] = a[m*K + k]. Coalesce on k (contiguous).
65
  let ar = idx / K_VECS;
66
  let ac4 = idx % K_VECS;
 
67
  let am = mBase + ar;
68
  let ak = kBase + ac4 * 4u;
69
  var aWord = vec4<{{ tileT }}>({{ tileT }}(0.0));
70
  if (am < M) {
71
  {% for component in range(4) %}
72
  if (ak + {{ component }}u < K) {
 
 
 
73
  aWord[{{ component }}u] = {{ tileT }}(a[am * K + ak + {{ component }}u]);
 
74
  }
75
  {% endfor %}
76
  }
77
  tileA[ar][ac4] = aWord;
78
  }
79
  for (var idx: u32 = li; idx < BN * K_VECS; idx = idx + 256u) {
 
 
 
 
 
80
  // B stored [K, N]: logical B[k,n] = b[k*N + n]. Coalesce on n (contiguous).
81
  let bc = idx % BN;
82
  let br4 = idx / BN;
 
83
  let bn = nBase + bc;
84
  let bk = kBase + br4 * 4u;
85
  var bWord = vec4<{{ tileT }}>({{ tileT }}(0.0));
86
  if (bn < N) {
87
  {% for component in range(4) %}
88
  if (bk + {{ component }}u < K) {
 
 
 
89
  bWord[{{ component }}u] = {{ tileT }}(b[(bk + {{ component }}u) * N + bn]);
 
90
  }
91
  {% endfor %}
92
  }
93
  tileB[bc][br4] = bWord;
94
  }
95
  workgroupBarrier();
96
+ {% set regT = "f32" %}{% set regCast = 1 if tileT != "f32" else 0 %}
97
+ {% filter indent(8, true) %}
98
+ let aRow = lid.y * TM;
99
+ let bCol = lid.x * TN;
100
+ for (var kv: u32 = 0u; kv < BK / 4u; kv = kv + 1u) {
101
+ var av: array<vec4<{{ regT }}>, TM>;
102
+ var bv: array<vec4<{{ regT }}>, TN>;
103
+ for (var i: u32 = 0u; i < TM; i = i + 1u) { av[i] = {% if regCast %}vec4<{{ regT }}>(tileA[aRow + i][kv]){% else %}tileA[aRow + i][kv]{% endif %}; }
104
+ for (var j: u32 = 0u; j < TN; j = j + 1u) { bv[j] = {% if regCast %}vec4<{{ regT }}>(tileB[bCol + j][kv]){% else %}tileB[bCol + j][kv]{% endif %}; }
105
+ for (var i: u32 = 0u; i < TM; i = i + 1u) {
106
+ for (var j: u32 = 0u; j < TN; j = j + 1u) {
107
+ acc[i * TN + j] = acc[i * TN + j] + dot(av[i], bv[j]);
108
+ }
109
+ }
110
+ }
111
+ {% endfilter %}
112
  workgroupBarrier();
113
  }
114
 
build/webgpu/gemm-subgroup-matrix.wgsl.jinja CHANGED
@@ -1,6 +1,3 @@
1
- {% if usesF16 %}
2
- enable f16;
3
- {% endif %}
4
  enable subgroups;
5
  {% if pinSubgroupSize32 %}
6
  enable subgroup_size_control;
@@ -8,6 +5,7 @@ enable subgroup_size_control;
8
  enable chromium_experimental_subgroup_matrix;
9
  diagnostic(off, chromium.subgroup_matrix_uniformity);
10
 
 
11
  {{ env.wgsl.resourceDeclarations }}
12
 
13
  {% set operandScalar = mmaScalar %}
@@ -20,19 +18,52 @@ diagnostic(off, chromium.subgroup_matrix_uniformity);
20
  {% set gemmEpi = gemmEpilogue if gemmEpilogue is defined else "none" %}
21
  {% set epiOpen = "gelu_tanh(" if gemmEpi == "fastgelu" else ("fused_act(" if gemmEpi == "activation" else "") %}
22
  {% set epiClose = ")" if gemmEpi != "none" else "" %}
 
 
23
  {% set useDoubleBufferedB = doubleBufferedB is defined and doubleBufferedB %}
 
 
24
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
25
  {% set columnTileBaseValue = columnTileBase if columnTileBase is defined else 0 %}
26
  {% set rowTileBaseValue = rowTileBase if rowTileBase is defined else 0 %}
27
  // Four subgroups cover 32xN as a 2x2 grid, or taller tiles as four row bands.
 
 
 
 
28
  {% set subtileCols = (tileNValue / 2)|int %}
29
  {% set subtileRows = 16 %}
 
30
  {% set aTilesPerSubgroup = (subtileRows / 8)|int %}
31
  {% set bTilesPerSubgroup = (subtileCols / 8)|int %}
32
  {% set subgroupCount = 4 %}
33
  {% set scratchBanks = subgroupCount * aTilesPerSubgroup * bTilesPerSubgroup %}
34
 
35
- {% if gemmEpi == "fastgelu" %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  fn tanh_safe(x: f32) -> f32 {
37
  if (x > 10.0) { return 1.0; }
38
  if (x < -10.0) { return -1.0; }
@@ -42,33 +73,6 @@ fn tanh_safe(x: f32) -> f32 {
42
  fn gelu_tanh(v: f32) -> f32 {
43
  return 0.5 * v * (1.0 + tanh_safe(0.7978845608028654 * (v + 0.044715 * v * v * v)));
44
  }
45
- {% elif gemmEpi == "activation" %}
46
- {% set actMode = gemmActivation | default("") %}
47
- {% set actA = gemmActAlpha | default(0.0) %}
48
- {% set actB = gemmActBeta | default(0.0) %}
49
- fn fused_act(v: f32) -> f32 {
50
- {% if actMode == "Relu" %}
51
- return max(v, 0.0);
52
- {% endif %}
53
- {% if actMode == "LeakyRelu" %}
54
- return select(v * f32({{ actA }}), v, v >= 0.0);
55
- {% endif %}
56
- {% if actMode == "Sigmoid" %}
57
- return 1.0 / (1.0 + exp(-v));
58
- {% endif %}
59
- {% if actMode == "Tanh" %}
60
- // Clamp first: the hardware tanh evaluates (e^2v - 1)/(e^2v + 1), which is
61
- // Inf/Inf = NaN once e^2v overflows (v > ~44). A GEMM accumulator reaches that
62
- // easily because it grows with K. tanh is already +/-1 to full f32 precision by
63
- // |v| ~ 9, so the clamp changes no representable result.
64
- return tanh(clamp(v, -10.0, 10.0));
65
- {% endif %}
66
- {% if actMode == "HardSigmoid" %}
67
- return clamp(f32({{ actA }}) * v + f32({{ actB }}), 0.0, 1.0);
68
- {% endif %}
69
- }
70
-
71
- {% endif %}
72
  const IN_F: u32 = {{ inFeatures }}u;
73
  const OUT_F: u32 = {{ outFeatures }}u;
74
  {% set kLoopEndValue = kLoopEnd if kLoopEnd is defined else ("K_LOOP" if tailSafe is defined and tailSafe else "IN_F") %}
@@ -79,13 +83,16 @@ const SUB_COLS: u32 = {{ subtileCols }}u;
79
  const SUB_ROWS: u32 = {{ subtileRows }}u;
80
  const COLUMN_TILE_BASE: u32 = {{ columnTileBaseValue }}u;
81
  const ROW_TILE_BASE: u32 = {{ rowTileBaseValue }}u;
 
82
 
83
  var<workgroup> tile_A: array<{{ operandScalar }}, {{ tileMValue }} * {{ tileKValue }}>;
 
84
  var<workgroup> tile_B: array<{{ operandScalar }}, {{ (2 if useDoubleBufferedB else 1) * tileNValue }} * {{ tileKValue }}>;
85
  // Distinct readback banks for every result matrix, so one barrier can publish
86
  // the full subtile without write-after-read reuse.
87
  var<workgroup> scratch: array<array<{{ accScalar }}, 64>, {{ scratchBanks }}>;
88
 
 
89
  fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
90
  // Clamp the partial-M tail to the last valid row (M-1) instead of zero-filling
91
  // it. Some subgroup-matrix implementations let a zero-padded left tile corrupt
@@ -104,8 +111,8 @@ fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
104
  }
105
  }
106
 
 
107
  fn loadSHMB(tile_base: u32, k_idx: u32, row: u32, c_idx: u32, bank_offset: u32) {
108
- {% if bTransposed %}
109
  // Generic transB=0 stores B as [K,N]. Stage that native orientation and load
110
  // the subgroup right operand without a transpose.
111
  let local_idx = row * {{ (tileKValue / 16)|int }}u + c_idx;
@@ -121,32 +128,18 @@ fn loadSHMB(tile_base: u32, k_idx: u32, row: u32, c_idx: u32, bank_offset: u32)
121
  tile_B[bank_offset + k_local * TILE_COLS + n_local] = w[global_k * OUT_F + global_n];
122
  }
123
  }
124
- {% else %}
125
- let col: u32 = c_idx * 16u;
126
- for (var row_offset: u32 = 0u; row_offset < TILE_COLS; row_offset += {{ (2048 / tileKValue)|int }}u) {
127
- let b_row: u32 = row + row_offset;
128
- // Non-power-of-two tuning widths (48/96) use only a prefix of lanes on the
129
- // final row band. Keep those inactive lanes out of tile_B and global memory.
130
- if (b_row < TILE_COLS) {
131
- let w_global: u32 = tile_base + b_row;
132
- for (var i: u32 = 0u; i < 16u; i++) {
133
- let k: u32 = k_idx + col + i;
134
- tile_B[bank_offset + b_row * TILE_K + col + i] =
135
- w[w_global * IN_F + k];
136
- }
137
- }
138
- }
139
- {% endif %}
140
  }
141
 
142
  {% set needsColBase = hasBias or (tailSafe is defined and tailSafe) %}
143
  {% set BIAS_1 = (" + " ~ accScalar ~ "(bias[col_base + col])") if hasBias else "" %}
144
  {% set BIAS_2 = (" + " ~ accScalar ~ "(bias[col_base + col2])") if hasBias else "" %}
145
- fn storeOutput(offset: u32{% if needsColBase %}, col_base: u32{% endif %}, row: u32, col: u32, src_slot: u32, row_limit: i32) {
 
 
146
  if (row_limit > 0 && row < u32(row_limit)) {
147
  let col2: u32 = col + 1u;
148
- {{ OUT }}[offset + row * OUT_F + col] = {{ OUT_SCALAR }}({{ epiOpen }}scratch[src_slot][row * 8u + col]{{ BIAS_1 }}{{ epiClose }});
149
- {{ OUT }}[offset + row * OUT_F + col2] = {{ OUT_SCALAR }}({{ epiOpen }}scratch[src_slot][row * 8u + col2]{{ BIAS_2 }}{{ epiClose }});
150
  }
151
  }
152
 
@@ -166,35 +159,179 @@ fn main(
166
  let w_global_base: u32 = (COLUMN_TILE_BASE + workgroup_id.x) * TILE_COLS;
167
 
168
  let subtile_id: u32 = local_idx / sg_size;
 
 
 
 
169
  let subtile_idx: u32 = subtile_id / 2u;
170
  let subtile_idy: u32 = subtile_id % 2u;
 
171
  let base_A: u32 = subtile_idy * SUB_ROWS;
172
  let base_B: u32 = subtile_idx * SUB_COLS;
173
 
174
- {% for m in range(aTilesPerSubgroup) %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  {% for n in range(bTilesPerSubgroup) %}
176
- var matC{{ m }}{{ n }}: subgroup_matrix_result<{{ accScalar }}, 8, 8>;
 
 
 
 
177
  {% endfor %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  {% endfor %}
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  for (var kidx: u32 = 0u; kidx < {{ kLoopEndValue }}; kidx += TILE_K) {
 
181
  loadSHMA(a_global_base, kidx, local_idx / {{ (tileKValue / 8)|int }}u, local_idx % {{ (tileKValue / 8)|int }}u);
 
182
  loadSHMB(w_global_base, kidx, local_idx / {{ (tileKValue / 16)|int }}u, local_idx % {{ (tileKValue / 16)|int }}u, 0u);
183
  workgroupBarrier();
184
 
185
  for (var step: u32 = 0u; step < TILE_K; step += 8u) {
186
  {% set dynamicATiles = aTilesPerSubgroup if aTilesPerSubgroup is defined else 2 %}
 
 
 
 
187
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
 
188
  {% set B_BANK = "b_bank_offset + " if doubleBufferedB is defined and doubleBufferedB else "" %}
189
- let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + step;
 
 
 
 
 
 
 
 
190
  {% for m in range(dynamicATiles) %}
 
 
 
 
 
191
  var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * TILE_K{% endif %}, TILE_K);
 
192
  {% endfor %}
193
 
194
  {% if bTransposed %}
195
- let matrix_b_offset = {{ B_BANK }}step * TILE_COLS + subtile_idx * SUB_COLS;
196
  {% else %}
197
- let matrix_b_offset = {{ B_BANK }}subtile_idx * SUB_COLS * TILE_K + step;
198
  {% endif %}
199
  {% for n in range(bTilesPerSubgroup) %}
200
  {% if bTransposed %}
@@ -206,14 +343,18 @@ fn main(
206
 
207
  {% for m in range(dynamicATiles) %}
208
  {% for n in range(bTilesPerSubgroup) %}
209
- matC{{ m }}{{ n }} = subgroupMatrixMultiplyAccumulate(matA{{ m }}, matB{{ n }}, matC{{ m }}{{ n }});
210
  {% endfor %}
211
  {% endfor %}
212
 
213
  }
214
  workgroupBarrier();
215
  }
 
216
 
 
 
 
217
  let matrix_c_offset: u32 = (a_global_base + base_A) * OUT_F + w_global_base + base_B;
218
  let row: u32 = sg_id / 4u;
219
  let col: u32 = (sg_id % 4u) * 2u;
@@ -222,13 +363,28 @@ fn main(
222
  // across lanes, so its cross-lane readback must be published before partial-M
223
  // guards diverge. The epilogue also handles output conversion and bias.
224
  let bank: u32 = subtile_id * {{ aTilesPerSubgroup * bTilesPerSubgroup }}u;
 
 
 
 
 
225
  {% for m in range(aTilesPerSubgroup) %}
226
  {% for n in range(bTilesPerSubgroup) %}
227
- subgroupMatrixStore<row_major>(&scratch[bank + {{ m * bTilesPerSubgroup + n }}u], 0u, matC{{ m }}{{ n }}, 8u);
 
 
 
 
 
 
 
 
 
228
  {% endfor %}
229
  {% endfor %}
230
  workgroupBarrier();
231
 
 
232
  {% for m in range(aTilesPerSubgroup) %}
233
  let row_limit_{{ m }}: i32 = i32(params.M) - i32(a_global_base + base_A + {{ m * 8 }}u);
234
  {% for n in range(bTilesPerSubgroup) %}
@@ -240,7 +396,7 @@ fn main(
240
  row,
241
  col,
242
  bank + {{ m * bTilesPerSubgroup + n }}u,
243
- row_limit_{{ m }}
244
  );
245
  {% endfor %}
246
  {% endfor %}
 
 
 
 
1
  enable subgroups;
2
  {% if pinSubgroupSize32 %}
3
  enable subgroup_size_control;
 
5
  enable chromium_experimental_subgroup_matrix;
6
  diagnostic(off, chromium.subgroup_matrix_uniformity);
7
 
8
+
9
  {{ env.wgsl.resourceDeclarations }}
10
 
11
  {% set operandScalar = mmaScalar %}
 
18
  {% set gemmEpi = gemmEpilogue if gemmEpilogue is defined else "none" %}
19
  {% set epiOpen = "gelu_tanh(" if gemmEpi == "fastgelu" else ("fused_act(" if gemmEpi == "activation" else "") %}
20
  {% set epiClose = ")" if gemmEpi != "none" else "" %}
21
+ {% set useDirectStore = directStore is defined and directStore and gemmEpi == "none" %}
22
+ {% set useDirectAStorage = directAStorage is defined and directAStorage %}
23
  {% set useDoubleBufferedB = doubleBufferedB is defined and doubleBufferedB %}
24
+ {% set useBalancedAccumulation = balancedMatrixAccumulation is defined and balancedMatrixAccumulation and useDoubleBufferedB and not useDirectStore and operandScalar == "f32" %}
25
+ {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
26
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
27
  {% set columnTileBaseValue = columnTileBase if columnTileBase is defined else 0 %}
28
  {% set rowTileBaseValue = rowTileBase if rowTileBase is defined else 0 %}
29
  // Four subgroups cover 32xN as a 2x2 grid, or taller tiles as four row bands.
30
+ {% if tileMValue > 32 %}
31
+ {% set subtileCols = tileNValue %}
32
+ {% set subtileRows = (tileMValue / 4)|int %}
33
+ {% else %}
34
  {% set subtileCols = (tileNValue / 2)|int %}
35
  {% set subtileRows = 16 %}
36
+ {% endif %}
37
  {% set aTilesPerSubgroup = (subtileRows / 8)|int %}
38
  {% set bTilesPerSubgroup = (subtileCols / 8)|int %}
39
  {% set subgroupCount = 4 %}
40
  {% set scratchBanks = subgroupCount * aTilesPerSubgroup * bTilesPerSubgroup %}
41
 
42
+ {% macro matrixAccumulators() %}
43
+ {% for m in range(aTilesPerSubgroup) %}
44
+ {% for n in range(bTilesPerSubgroup) %}
45
+ var matC{{ m }}{{ n }}: subgroup_matrix_result<{{ accScalar }}, 8, 8>;
46
+ {% if useBalancedAccumulation %}
47
+ var matD{{ m }}{{ n }}: subgroup_matrix_result<{{ accScalar }}, 8, 8>;
48
+ var matE{{ m }}{{ n }}: subgroup_matrix_result<{{ accScalar }}, 8, 8>;
49
+ var matF{{ m }}{{ n }}: subgroup_matrix_result<{{ accScalar }}, 8, 8>;
50
+ {% endif %}
51
+ {% endfor %}
52
+ {% endfor %}
53
+
54
+ {% endmacro %}
55
+
56
+ {% macro storeMatrixAccumulators() %}
57
+ {% for m in range(aTilesPerSubgroup) %}
58
+ {% for n in range(bTilesPerSubgroup) %}
59
+ subgroupMatrixStore<row_major>(&scratch[bank + {{ m * bTilesPerSubgroup + n }}u], 0u, matC{{ m }}{{ n }}, 8u);
60
+ {% if useBalancedAccumulation %}
61
+ subgroupMatrixStore<row_major>(&tile_B, (bank + {{ m * bTilesPerSubgroup + n }}u) * 64u, matD{{ m }}{{ n }}, 8u);
62
+ {% endif %}
63
+ {% endfor %}
64
+ {% endfor %}
65
+ {% endmacro %}
66
+
67
  fn tanh_safe(x: f32) -> f32 {
68
  if (x > 10.0) { return 1.0; }
69
  if (x < -10.0) { return -1.0; }
 
73
  fn gelu_tanh(v: f32) -> f32 {
74
  return 0.5 * v * (1.0 + tanh_safe(0.7978845608028654 * (v + 0.044715 * v * v * v)));
75
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  const IN_F: u32 = {{ inFeatures }}u;
77
  const OUT_F: u32 = {{ outFeatures }}u;
78
  {% set kLoopEndValue = kLoopEnd if kLoopEnd is defined else ("K_LOOP" if tailSafe is defined and tailSafe else "IN_F") %}
 
83
  const SUB_ROWS: u32 = {{ subtileRows }}u;
84
  const COLUMN_TILE_BASE: u32 = {{ columnTileBaseValue }}u;
85
  const ROW_TILE_BASE: u32 = {{ rowTileBaseValue }}u;
86
+ {% if not useDirectAStorage %}
87
 
88
  var<workgroup> tile_A: array<{{ operandScalar }}, {{ tileMValue }} * {{ tileKValue }}>;
89
+ {% endif %}
90
  var<workgroup> tile_B: array<{{ operandScalar }}, {{ (2 if useDoubleBufferedB else 1) * tileNValue }} * {{ tileKValue }}>;
91
  // Distinct readback banks for every result matrix, so one barrier can publish
92
  // the full subtile without write-after-read reuse.
93
  var<workgroup> scratch: array<array<{{ accScalar }}, 64>, {{ scratchBanks }}>;
94
 
95
+ {% if not useDirectAStorage %}
96
  fn loadSHMA(tile_base: u32, k_idx: u32, row: u32, c_idx: u32) {
97
  // Clamp the partial-M tail to the last valid row (M-1) instead of zero-filling
98
  // it. Some subgroup-matrix implementations let a zero-padded left tile corrupt
 
111
  }
112
  }
113
 
114
+ {% endif %}
115
  fn loadSHMB(tile_base: u32, k_idx: u32, row: u32, c_idx: u32, bank_offset: u32) {
 
116
  // Generic transB=0 stores B as [K,N]. Stage that native orientation and load
117
  // the subgroup right operand without a transpose.
118
  let local_idx = row * {{ (tileKValue / 16)|int }}u + c_idx;
 
128
  tile_B[bank_offset + k_local * TILE_COLS + n_local] = w[global_k * OUT_F + global_n];
129
  }
130
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
  {% set needsColBase = hasBias or (tailSafe is defined and tailSafe) %}
134
  {% set BIAS_1 = (" + " ~ accScalar ~ "(bias[col_base + col])") if hasBias else "" %}
135
  {% set BIAS_2 = (" + " ~ accScalar ~ "(bias[col_base + col2])") if hasBias else "" %}
136
+ {% set ACC_1 = "(prior.x + (scratch[src_slot][row * 8u + col] + tile_B[src_slot * 64u + row * 8u + col]))" if useBalancedAccumulation else "scratch[src_slot][row * 8u + col]" %}
137
+ {% set ACC_2 = "(prior.y + (scratch[src_slot][row * 8u + col2] + tile_B[src_slot * 64u + row * 8u + col2]))" if useBalancedAccumulation else "scratch[src_slot][row * 8u + col2]" %}
138
+ fn storeOutput(offset: u32{% if needsColBase %}, col_base: u32{% endif %}, row: u32, col: u32, src_slot: u32, row_limit: i32{% if useBalancedAccumulation %}, prior: vec2<f32>{% endif %}) {
139
  if (row_limit > 0 && row < u32(row_limit)) {
140
  let col2: u32 = col + 1u;
141
+ {{ OUT }}[offset + row * OUT_F + col] = {{ OUT_SCALAR }}({{ epiOpen }}{{ ACC_1 }}{{ BIAS_1 }}{{ epiClose }});
142
+ {{ OUT }}[offset + row * OUT_F + col2] = {{ OUT_SCALAR }}({{ epiOpen }}{{ ACC_2 }}{{ BIAS_2 }}{{ epiClose }});
143
  }
144
  }
145
 
 
159
  let w_global_base: u32 = (COLUMN_TILE_BASE + workgroup_id.x) * TILE_COLS;
160
 
161
  let subtile_id: u32 = local_idx / sg_size;
162
+ {% if tileMValue > 32 %}
163
+ let subtile_idx: u32 = 0u;
164
+ let subtile_idy: u32 = subtile_id;
165
+ {% else %}
166
  let subtile_idx: u32 = subtile_id / 2u;
167
  let subtile_idy: u32 = subtile_id % 2u;
168
+ {% endif %}
169
  let base_A: u32 = subtile_idy * SUB_ROWS;
170
  let base_B: u32 = subtile_idx * SUB_COLS;
171
 
172
+ {{ matrixAccumulators() }}
173
+ {% if useDoubleBufferedB %}
174
+ // Large aligned f32 tiles do not benefit from staging A: each row band is
175
+ // consumed by exactly one subgroup. Load those matrices directly from x and
176
+ // ping-pong only B, which is reused by all four subgroups. The barrier that
177
+ // publishes the next B bank also proves the previous bank is no longer read,
178
+ // reducing the steady-state schedule from two barriers per K tile to one.
179
+ var b_bank: u32 = 0u;
180
+ loadSHMB(w_global_base, 0u, local_idx / {{ (tileKValue / 16)|int }}u, local_idx % {{ (tileKValue / 16)|int }}u, 0u);
181
+ workgroupBarrier();
182
+ for (var kidx: u32 = 0u; kidx < {{ kLoopEndValue }}; kidx += TILE_K) {
183
+ let b_bank_offset = b_bank * TILE_COLS * TILE_K;
184
+ for (var step: u32 = 0u; step < TILE_K; step += {{ 32 if useBalancedAccumulation else 8 }}u) {
185
+ {% if useBalancedAccumulation %}
186
+ {% for stream in range(4) %}
187
+ {
188
+ {% set matrixAccumulator = ["matC", "matD", "matE", "matF"][stream] %}
189
+ {% set matrixStepOffset = stream * 8 %}
190
+ {% set dynamicATiles = aTilesPerSubgroup if aTilesPerSubgroup is defined else 2 %}
191
+ {% set matrixAccumulator = matrixAccumulator | default("matC") %}
192
+ {% set matrixStepOffset = matrixStepOffset | default(0) %}
193
+ {% set matrixStep = "step" if matrixStepOffset == 0 else "(step + " ~ matrixStepOffset ~ "u)" %}
194
+ {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
195
+ {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
196
+ {% set directA = directAStorage is defined and directAStorage %}
197
+ {% set B_BANK = "b_bank_offset + " if doubleBufferedB is defined and doubleBufferedB else "" %}
198
+ {% if directA %}
199
+ // Direct storage guarantees a full aligned M/K tile, so subgroup-matrix
200
+ // loads need no guarded workgroup staging.
201
+ let matrix_a_offset = (a_global_base + base_A) * IN_F + kidx + {{ matrixStep }};
202
+ {% elif aTransposed %}
203
+ let matrix_a_offset = {{ matrixStep }} * TILE_ROWS + subtile_idy * SUB_ROWS;
204
+ {% else %}
205
+ let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + {{ matrixStep }};
206
+ {% endif %}
207
+ {% for m in range(dynamicATiles) %}
208
+ {% if directA %}
209
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&x, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * IN_F{% endif %}, IN_F);
210
+ {% elif aTransposed %}
211
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, col_major>(&tile_A, matrix_a_offset + {{ m * 8 }}u, TILE_ROWS);
212
+ {% else %}
213
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * TILE_K{% endif %}, TILE_K);
214
+ {% endif %}
215
+ {% endfor %}
216
+
217
+ {% if bTransposed %}
218
+ let matrix_b_offset = {{ B_BANK }}{{ matrixStep }} * TILE_COLS + subtile_idx * SUB_COLS;
219
+ {% else %}
220
+ let matrix_b_offset = {{ B_BANK }}subtile_idx * SUB_COLS * TILE_K + {{ matrixStep }};
221
+ {% endif %}
222
  {% for n in range(bTilesPerSubgroup) %}
223
+ {% if bTransposed %}
224
+ var matB{{ n }}: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, row_major>(&tile_B, matrix_b_offset + {{ n * 8 }}u, TILE_COLS);
225
+ {% else %}
226
+ var matB{{ n }}: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + {{ n * 8 }}u * TILE_K, TILE_K);
227
+ {% endif %}
228
  {% endfor %}
229
+
230
+ {% for m in range(dynamicATiles) %}
231
+ {% for n in range(bTilesPerSubgroup) %}
232
+ {{ matrixAccumulator }}{{ m }}{{ n }} = subgroupMatrixMultiplyAccumulate(matA{{ m }}, matB{{ n }}, {{ matrixAccumulator }}{{ m }}{{ n }});
233
+ {% endfor %}
234
+ {% endfor %}
235
+
236
+ }
237
+ {% endfor %}
238
+ {% else %}
239
+ {% set dynamicATiles = aTilesPerSubgroup if aTilesPerSubgroup is defined else 2 %}
240
+ {% set matrixAccumulator = matrixAccumulator | default("matC") %}
241
+ {% set matrixStepOffset = matrixStepOffset | default(0) %}
242
+ {% set matrixStep = "step" if matrixStepOffset == 0 else "(step + " ~ matrixStepOffset ~ "u)" %}
243
+ {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
244
+ {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
245
+ {% set directA = directAStorage is defined and directAStorage %}
246
+ {% set B_BANK = "b_bank_offset + " if doubleBufferedB is defined and doubleBufferedB else "" %}
247
+ {% if directA %}
248
+ // Direct storage guarantees a full aligned M/K tile, so subgroup-matrix
249
+ // loads need no guarded workgroup staging.
250
+ let matrix_a_offset = (a_global_base + base_A) * IN_F + kidx + {{ matrixStep }};
251
+ {% elif aTransposed %}
252
+ let matrix_a_offset = {{ matrixStep }} * TILE_ROWS + subtile_idy * SUB_ROWS;
253
+ {% else %}
254
+ let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + {{ matrixStep }};
255
+ {% endif %}
256
+ {% for m in range(dynamicATiles) %}
257
+ {% if directA %}
258
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&x, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * IN_F{% endif %}, IN_F);
259
+ {% elif aTransposed %}
260
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, col_major>(&tile_A, matrix_a_offset + {{ m * 8 }}u, TILE_ROWS);
261
+ {% else %}
262
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * TILE_K{% endif %}, TILE_K);
263
+ {% endif %}
264
+ {% endfor %}
265
+
266
+ {% if bTransposed %}
267
+ let matrix_b_offset = {{ B_BANK }}{{ matrixStep }} * TILE_COLS + subtile_idx * SUB_COLS;
268
+ {% else %}
269
+ let matrix_b_offset = {{ B_BANK }}subtile_idx * SUB_COLS * TILE_K + {{ matrixStep }};
270
+ {% endif %}
271
+ {% for n in range(bTilesPerSubgroup) %}
272
+ {% if bTransposed %}
273
+ var matB{{ n }}: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, row_major>(&tile_B, matrix_b_offset + {{ n * 8 }}u, TILE_COLS);
274
+ {% else %}
275
+ var matB{{ n }}: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + {{ n * 8 }}u * TILE_K, TILE_K);
276
+ {% endif %}
277
  {% endfor %}
278
 
279
+ {% for m in range(dynamicATiles) %}
280
+ {% for n in range(bTilesPerSubgroup) %}
281
+ {{ matrixAccumulator }}{{ m }}{{ n }} = subgroupMatrixMultiplyAccumulate(matA{{ m }}, matB{{ n }}, {{ matrixAccumulator }}{{ m }}{{ n }});
282
+ {% endfor %}
283
+ {% endfor %}
284
+
285
+ {% endif %}
286
+ }
287
+ let next_kidx = kidx + TILE_K;
288
+ if (next_kidx < {{ kLoopEndValue }}) {
289
+ let next_bank = 1u - b_bank;
290
+ loadSHMB(w_global_base, next_kidx, local_idx / {{ (tileKValue / 16)|int }}u, local_idx % {{ (tileKValue / 16)|int }}u, next_bank * TILE_COLS * TILE_K);
291
+ workgroupBarrier();
292
+ b_bank = next_bank;
293
+ }
294
+ }
295
+ {% else %}
296
  for (var kidx: u32 = 0u; kidx < {{ kLoopEndValue }}; kidx += TILE_K) {
297
+ {% if not useDirectAStorage %}
298
  loadSHMA(a_global_base, kidx, local_idx / {{ (tileKValue / 8)|int }}u, local_idx % {{ (tileKValue / 8)|int }}u);
299
+ {% endif %}
300
  loadSHMB(w_global_base, kidx, local_idx / {{ (tileKValue / 16)|int }}u, local_idx % {{ (tileKValue / 16)|int }}u, 0u);
301
  workgroupBarrier();
302
 
303
  for (var step: u32 = 0u; step < TILE_K; step += 8u) {
304
  {% set dynamicATiles = aTilesPerSubgroup if aTilesPerSubgroup is defined else 2 %}
305
+ {% set matrixAccumulator = matrixAccumulator | default("matC") %}
306
+ {% set matrixStepOffset = matrixStepOffset | default(0) %}
307
+ {% set matrixStep = "step" if matrixStepOffset == 0 else "(step + " ~ matrixStepOffset ~ "u)" %}
308
+ {% set aTransposed = aTransposedStorage if aTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
309
  {% set bTransposed = bTransposedStorage if bTransposedStorage is defined else (matrixInputsTransposed is defined and matrixInputsTransposed) %}
310
+ {% set directA = directAStorage is defined and directAStorage %}
311
  {% set B_BANK = "b_bank_offset + " if doubleBufferedB is defined and doubleBufferedB else "" %}
312
+ {% if directA %}
313
+ // Direct storage guarantees a full aligned M/K tile, so subgroup-matrix
314
+ // loads need no guarded workgroup staging.
315
+ let matrix_a_offset = (a_global_base + base_A) * IN_F + kidx + {{ matrixStep }};
316
+ {% elif aTransposed %}
317
+ let matrix_a_offset = {{ matrixStep }} * TILE_ROWS + subtile_idy * SUB_ROWS;
318
+ {% else %}
319
+ let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + {{ matrixStep }};
320
+ {% endif %}
321
  {% for m in range(dynamicATiles) %}
322
+ {% if directA %}
323
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&x, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * IN_F{% endif %}, IN_F);
324
+ {% elif aTransposed %}
325
+ var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, col_major>(&tile_A, matrix_a_offset + {{ m * 8 }}u, TILE_ROWS);
326
+ {% else %}
327
  var matA{{ m }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&tile_A, matrix_a_offset{% if m > 0 %} + {{ m * 8 }}u * TILE_K{% endif %}, TILE_K);
328
+ {% endif %}
329
  {% endfor %}
330
 
331
  {% if bTransposed %}
332
+ let matrix_b_offset = {{ B_BANK }}{{ matrixStep }} * TILE_COLS + subtile_idx * SUB_COLS;
333
  {% else %}
334
+ let matrix_b_offset = {{ B_BANK }}subtile_idx * SUB_COLS * TILE_K + {{ matrixStep }};
335
  {% endif %}
336
  {% for n in range(bTilesPerSubgroup) %}
337
  {% if bTransposed %}
 
343
 
344
  {% for m in range(dynamicATiles) %}
345
  {% for n in range(bTilesPerSubgroup) %}
346
+ {{ matrixAccumulator }}{{ m }}{{ n }} = subgroupMatrixMultiplyAccumulate(matA{{ m }}, matB{{ n }}, {{ matrixAccumulator }}{{ m }}{{ n }});
347
  {% endfor %}
348
  {% endfor %}
349
 
350
  }
351
  workgroupBarrier();
352
  }
353
+ {% endif %}
354
 
355
+ {% if useBalancedAccumulation %}
356
+ workgroupBarrier();
357
+ {% endif %}
358
  let matrix_c_offset: u32 = (a_global_base + base_A) * OUT_F + w_global_base + base_B;
359
  let row: u32 = sg_id / 4u;
360
  let col: u32 = (sg_id % 4u) * 2u;
 
363
  // across lanes, so its cross-lane readback must be published before partial-M
364
  // guards diverge. The epilogue also handles output conversion and bias.
365
  let bank: u32 = subtile_id * {{ aTilesPerSubgroup * bTilesPerSubgroup }}u;
366
+ {{ storeMatrixAccumulators() }}
367
+ workgroupBarrier();
368
+
369
+ {% if useBalancedAccumulation %}
370
+ var pairTotals: array<vec2<f32>, {{ aTilesPerSubgroup * bTilesPerSubgroup }}>;
371
  {% for m in range(aTilesPerSubgroup) %}
372
  {% for n in range(bTilesPerSubgroup) %}
373
+ pairTotals[{{ m * bTilesPerSubgroup + n }}] = vec2<f32>(
374
+ scratch[bank + {{ m * bTilesPerSubgroup + n }}u][row * 8u + col] + tile_B[(bank + {{ m * bTilesPerSubgroup + n }}u) * 64u + row * 8u + col],
375
+ scratch[bank + {{ m * bTilesPerSubgroup + n }}u][row * 8u + col + 1u] + tile_B[(bank + {{ m * bTilesPerSubgroup + n }}u) * 64u + row * 8u + col + 1u]);
376
+ {% endfor %}
377
+ {% endfor %}
378
+ workgroupBarrier();
379
+ {% for m in range(aTilesPerSubgroup) %}
380
+ {% for n in range(bTilesPerSubgroup) %}
381
+ subgroupMatrixStore<row_major>(&scratch[bank + {{ m * bTilesPerSubgroup + n }}u], 0u, matE{{ m }}{{ n }}, 8u);
382
+ subgroupMatrixStore<row_major>(&tile_B, (bank + {{ m * bTilesPerSubgroup + n }}u) * 64u, matF{{ m }}{{ n }}, 8u);
383
  {% endfor %}
384
  {% endfor %}
385
  workgroupBarrier();
386
 
387
+ {% endif %}
388
  {% for m in range(aTilesPerSubgroup) %}
389
  let row_limit_{{ m }}: i32 = i32(params.M) - i32(a_global_base + base_A + {{ m * 8 }}u);
390
  {% for n in range(bTilesPerSubgroup) %}
 
396
  row,
397
  col,
398
  bank + {{ m * bTilesPerSubgroup + n }}u,
399
+ row_limit_{{ m }}{{ ", pairTotals[" ~ (m * bTilesPerSubgroup + n) ~ "]" if useBalancedAccumulation else "" }}
400
  );
401
  {% endfor %}
402
  {% endfor %}
build/webgpu/manifest.json CHANGED
@@ -2,38 +2,13 @@
2
  "domain": "com.microsoft",
3
  "name": "GemmFastGelu",
4
  "sinceVersion": 1,
5
- "description": "Fuses MatMul, an optional bias, and FastGelu: `Y = FastGelu(X @ W + bias)`. `X` has rank at least 2 with shape `(..., K)`, `W` has shape `(K, N)`, and `bias` has shape `(N)`. The activation runs in the float32 accumulator before the output is narrowed, avoiding an intermediate `(..., N)` tensor. Bfloat16 is not implemented.",
6
- "inputs": [
7
- {
8
- "role": "X",
9
- "dtype": "T",
10
- "description": "Left operand of rank 2 or greater with shape `(..., K)`; every leading-axis coordinate identifies a row of the product."
11
- },
12
- { "role": "W", "dtype": "T", "rank": 2, "description": "Right operand with shape `(K, N)`." },
13
- {
14
- "role": "bias",
15
- "dtype": "T",
16
- "rank": 1,
17
- "optional": true,
18
- "description": "Optional bias with shape `(N)`, added before the activation."
19
- }
20
- ],
21
- "outputs": [
22
- {
23
- "role": "Y",
24
- "dtype": "T",
25
- "rank": "ranks.X",
26
- "shape": "matmulShape(shapes.X, shapes.W)",
27
- "description": "`FastGelu(X @ W + bias)`, with the same rank and leading dimensions as `X` and a trailing `N`."
28
- }
29
- ],
30
- "typeConstraints": { "T": ["float32", "float16"] },
31
- "args": {
32
- "X": { "kind": "tensor", "semantic": "X", "role": "input" },
33
- "W": { "kind": "tensor", "semantic": "W", "role": "weights" },
34
- "bias": { "kind": "tensor", "semantic": "bias", "role": "weights", "required": false },
35
- "Y": { "kind": "tensor", "semantic": "Y", "role": "output" }
36
  },
 
 
37
  "derive": {
38
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
39
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
@@ -52,157 +27,336 @@
52
  "sgmatOperandBytes": "2 if tensorDtypes.X == \"float16\" and tensorDtypes.W == \"float16\" else 4",
53
  "sgmatStorageBytes": "(32 * 32 + 64 * 32) * sgmatOperandBytes + 32 * 64 * 4",
54
  "sgmatResourcesFit": "128 <= deviceWorkgroupCap and sgmatStorageBytes <= device.limits.maxComputeWorkgroupStorageSize",
55
- "sgmatLayoutOk": "gfgRows > 0 and kDim % 32 == 0 and nDim % 64 == 0 and ((tensorDtypes.X == \"float16\" and device.features.has(\"shader-f16\") and gfgRows >= 2) or (tensorDtypes.X == \"float32\" and gfgRows >= 32)) and ceilDiv(gfgRows, 32) <= device.limits.maxComputeWorkgroupsPerDimension and ceilDiv(nDim, 64) <= device.limits.maxComputeWorkgroupsPerDimension",
56
- "sgmatContract": "wave32Effective and sgmatLayoutOk and sgmatResourcesFit"
57
- },
58
- "constants": {
59
  "usesF16": "tensorDtypes.X == \"float16\"",
60
  "transA": false,
61
  "transB": false,
62
- "gemmEpilogue": "\"fastgelu\""
 
 
 
 
63
  },
64
- "bindingSets": {
65
- "noBias": [
66
- { "name": "a", "arg": "X", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
67
- { "name": "b", "arg": "W", "semantic": "W", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
68
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
69
- {
70
- "name": "params",
71
- "semantic": "kernel.params",
72
- "buffer": { "type": "uniform" },
73
- "struct": {
74
- "name": "Params",
75
- "fields": [
76
- { "name": "M", "type": "u32", "value": "gfgRows" },
77
- { "name": "N", "type": "u32", "value": "nDim" },
78
- { "name": "K", "type": "u32", "value": "kDim" },
79
- { "name": "alpha", "type": "f32", "value": 1 }
80
- ]
81
- }
82
- }
83
- ],
84
- "withBias": [
85
- { "name": "a", "arg": "X", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
86
- { "name": "b", "arg": "W", "semantic": "W", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
87
- {
88
- "name": "bias",
89
- "arg": "bias",
90
- "semantic": "bias",
91
- "buffer": { "type": "read-only-storage" },
92
- "elementType": "$T"
93
- },
94
- { "name": "y", "arg": "Y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
95
- {
96
- "name": "params",
97
- "semantic": "kernel.params",
98
- "buffer": { "type": "uniform" },
99
- "struct": {
100
- "name": "Params",
101
- "fields": [
102
- { "name": "M", "type": "u32", "value": "gfgRows" },
103
- { "name": "N", "type": "u32", "value": "nDim" },
104
- { "name": "K", "type": "u32", "value": "kDim" },
105
- { "name": "alpha", "type": "f32", "value": 1 },
106
- { "name": "beta", "type": "f32", "value": 1 }
107
- ]
108
- }
109
- }
110
- ],
111
- "sgmatNoBias": [
112
- { "name": "x", "arg": "X", "semantic": "a", "buffer": { "type": "read-only-storage" }, "elementType": "$xScalar" },
113
- { "name": "w", "arg": "W", "semantic": "b", "buffer": { "type": "read-only-storage" }, "elementType": "$wScalar" },
114
- { "name": "y", "arg": "Y", "semantic": "y", "buffer": { "type": "storage" }, "elementType": "$yScalar" },
115
- {
116
- "name": "params",
117
- "semantic": "kernel.params",
118
- "buffer": { "type": "uniform" },
119
- "struct": { "name": "Params", "fields": [{ "name": "M", "type": "u32", "value": "gfgRows" }] }
120
- }
121
- ],
122
- "sgmatBias": [
123
- { "name": "x", "arg": "X", "semantic": "a", "buffer": { "type": "read-only-storage" }, "elementType": "$xScalar" },
124
- { "name": "w", "arg": "W", "semantic": "b", "buffer": { "type": "read-only-storage" }, "elementType": "$wScalar" },
125
- {
126
- "name": "bias",
127
- "arg": "bias",
128
- "semantic": "bias",
129
- "buffer": { "type": "read-only-storage" },
130
- "elementType": "$bScalar"
131
- },
132
- { "name": "y", "arg": "Y", "semantic": "y", "buffer": { "type": "storage" }, "elementType": "$yScalar" },
133
- {
134
- "name": "params",
135
- "semantic": "kernel.params",
136
- "buffer": { "type": "uniform" },
137
- "struct": { "name": "Params", "fields": [{ "name": "M", "type": "u32", "value": "gfgRows" }] }
138
- }
139
- ]
140
  },
141
  "variants": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  {
143
  "id": "sgmat_bias",
144
  "priority": 100,
 
145
  "requires": {
146
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
147
- "subgroupMatrixConfigs": [
148
- { "componentType": "f16", "M": 8, "N": 8, "K": 8 },
149
- { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
150
- ]
151
  },
152
- "when": ["biasContract", "sgmatContract"],
153
- "constants": {
154
  "hasBias": true,
155
- "gemmEpilogue": "\"fastgelu\"",
156
  "bTransposedStorage": true,
157
- "xScalar": "\"f16\" if tensorDtypes.X == \"float16\" else \"f32\"",
158
- "wScalar": "\"f16\" if tensorDtypes.W == \"float16\" else \"f32\"",
159
- "bScalar": "\"f16\" if tensorDtypes.bias == \"float16\" else \"f32\"",
160
- "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
161
- "mmaScalar": "\"f16\" if tensorDtypes.X == \"float16\" and tensorDtypes.W == \"float16\" else \"f32\"",
162
- "usesF16": "tensorDtypes.X == \"float16\" or tensorDtypes.W == \"float16\" or tensorDtypes.Y == \"float16\"",
163
  "inFeatures": "kDim",
164
- "outFeatures": "nDim"
 
 
 
 
 
 
165
  },
166
  "passes": [
167
  {
168
  "id": "main",
169
- "name": "GemmFastGelu.SubgroupMatrixBias",
170
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
171
- "bindings": "sgmatBias",
172
- "dispatch": { "workgroups": "ceilDiv(gfgRows, 32)", "x": "ceilDiv(nDim, 64)" }
 
 
 
 
173
  }
174
  ]
175
  },
176
  {
177
  "id": "sgmat",
178
  "priority": 100,
 
179
  "requires": {
180
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
181
- "subgroupMatrixConfigs": [
182
- { "componentType": "f16", "M": 8, "N": 8, "K": 8 },
183
- { "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }
184
- ]
185
  },
186
- "when": ["noBiasContract", "sgmatContract"],
187
- "constants": {
188
  "hasBias": false,
189
- "gemmEpilogue": "\"fastgelu\"",
190
  "bTransposedStorage": true,
191
- "xScalar": "\"f16\" if tensorDtypes.X == \"float16\" else \"f32\"",
192
- "wScalar": "\"f16\" if tensorDtypes.W == \"float16\" else \"f32\"",
193
- "yScalar": "\"f16\" if tensorDtypes.Y == \"float16\" else \"f32\"",
194
- "mmaScalar": "\"f16\" if tensorDtypes.X == \"float16\" and tensorDtypes.W == \"float16\" else \"f32\"",
195
- "usesF16": "tensorDtypes.X == \"float16\" or tensorDtypes.W == \"float16\" or tensorDtypes.Y == \"float16\"",
196
  "inFeatures": "kDim",
197
- "outFeatures": "nDim"
 
 
 
 
 
 
198
  },
199
  "passes": [
200
  {
201
  "id": "main",
202
- "name": "GemmFastGelu.SubgroupMatrix",
203
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
204
- "bindings": "sgmatNoBias",
205
- "dispatch": { "workgroups": "ceilDiv(gfgRows, 32)", "x": "ceilDiv(nDim, 64)" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  }
207
  ]
208
  },
@@ -210,16 +364,31 @@
210
  "id": "tiled_bias",
211
  "priority": 0,
212
  "when": ["biasContract"],
213
- "constants": { "hasBias": true, "rowBias": true, "scalarBias": false, "columnBias": false },
214
  "passes": [
215
  {
216
  "id": "main",
217
  "name": "GemmFastGelu.Bias",
218
  "shader": "gemm-fast-gelu.wgsl.jinja",
219
- "bindings": "withBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  "dispatch": {
221
- "x": "min(ceilDiv(nDim, 64), device.limits.maxComputeWorkgroupsPerDimension)",
222
- "y": "min(ceilDiv(gfgRows, 64), device.limits.maxComputeWorkgroupsPerDimension)"
223
  }
224
  }
225
  ]
@@ -228,16 +397,29 @@
228
  "id": "tiled",
229
  "priority": 0,
230
  "when": ["noBiasContract"],
231
- "constants": { "hasBias": false },
232
  "passes": [
233
  {
234
  "id": "main",
235
  "name": "GemmFastGelu",
236
  "shader": "gemm-fast-gelu.wgsl.jinja",
237
- "bindings": "noBias",
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  "dispatch": {
239
- "x": "min(ceilDiv(nDim, 64), device.limits.maxComputeWorkgroupsPerDimension)",
240
- "y": "min(ceilDiv(gfgRows, 64), device.limits.maxComputeWorkgroupsPerDimension)"
241
  }
242
  }
243
  ]
 
2
  "domain": "com.microsoft",
3
  "name": "GemmFastGelu",
4
  "sinceVersion": 1,
5
+ "inputs": {
6
+ "X": { "dtype": "T" },
7
+ "W": { "dtype": "T", "rank": 2 },
8
+ "bias": { "dtype": "T", "rank": 1, "optional": true }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  },
10
+ "outputs": { "Y": { "dtype": "T", "rank": "ranks.X", "shape": "matmulShape(shapes.X, shapes.W)" } },
11
+ "typeConstraints": { "T": ["float32", "float16"] },
12
  "derive": {
13
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
14
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
 
27
  "sgmatOperandBytes": "2 if tensorDtypes.X == \"float16\" and tensorDtypes.W == \"float16\" else 4",
28
  "sgmatStorageBytes": "(32 * 32 + 64 * 32) * sgmatOperandBytes + 32 * 64 * 4",
29
  "sgmatResourcesFit": "128 <= deviceWorkgroupCap and sgmatStorageBytes <= device.limits.maxComputeWorkgroupStorageSize",
30
+ "sgmatLayoutOk": "gfgRows > 0 and kDim % 32 == 0 and nDim % 64 == 0 and ((tensorDtypes.X == \"float16\" and device.features.has(\"shader-f16\") and gfgRows >= 2) or (tensorDtypes.X == \"float32\" and gfgRows >= 32)) and ceilDiv(gfgRows, 32) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceilDiv(nDim, 64) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535)",
31
+ "sgmatContract": "wave32Effective and sgmatLayoutOk and sgmatResourcesFit",
 
 
32
  "usesF16": "tensorDtypes.X == \"float16\"",
33
  "transA": false,
34
  "transB": false,
35
+ "gemmEpilogue": "\"fastgelu\"",
36
+ "sgmatDirectRows": 64,
37
+ "sgmatDirectCols": 32,
38
+ "sgmatDirectStorageBytes": "2 * sgmatDirectCols * 32 * sgmatOperandBytes + sgmatDirectRows * sgmatDirectCols * 4",
39
+ "sgmatDirectContract": "wave32Effective and gfgRows > 0 and gfgRows % sgmatDirectRows == 0 and kDim % 32 == 0 and nDim % sgmatDirectCols == 0 and deviceWorkgroupCap >= 128 and sgmatDirectStorageBytes <= device.limits.maxComputeWorkgroupStorageSize and ceilDiv(gfgRows, sgmatDirectRows) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceilDiv(nDim, sgmatDirectCols) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535)"
40
  },
41
+ "bindings": {
42
+ "x": { "arg": "X", "buffer": "read-only-storage", "elementType": "$xScalar" },
43
+ "w": { "arg": "W", "buffer": "read-only-storage", "elementType": "$wScalar" },
44
+ "y": { "arg": "Y", "buffer": "storage", "elementType": "$yScalar" },
45
+ "params": { "buffer": "uniform", "struct": [{ "name": "M", "type": "u32", "value": "gfgRows" }] },
46
+ "a": { "arg": "X", "buffer": "read-only-storage", "elementType": "$T" },
47
+ "b": { "arg": "W", "buffer": "read-only-storage", "elementType": "$T" },
48
+ "y_2": { "arg": "Y", "name": "y", "buffer": "storage", "elementType": "$T" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  },
50
  "variants": [
51
+ {
52
+ "id": "sgmat_direct_bias",
53
+ "priority": 110,
54
+ "when": ["biasContract", "sgmatDirectContract", "tensorDtypes.X == \"float32\""],
55
+ "requires": {
56
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
57
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "M": 8, "N": 8, "K": 8, "resultComponentType": "f32" }]
58
+ },
59
+ "derive": {
60
+ "hasBias": true,
61
+ "bTransposedStorage": true,
62
+ "xScalar": "\"f32\"",
63
+ "wScalar": "\"f32\"",
64
+ "bScalar": "\"f32\"",
65
+ "yScalar": "\"f32\"",
66
+ "mmaScalar": "\"f32\"",
67
+ "inFeatures": "kDim",
68
+ "outFeatures": "nDim",
69
+ "tileM": "sgmatDirectRows",
70
+ "tileN": "sgmatDirectCols",
71
+ "tileK": 32,
72
+ "directAStorage": true,
73
+ "doubleBufferedB": true,
74
+ "balancedMatrixAccumulation": true
75
+ },
76
+ "passes": [
77
+ {
78
+ "id": "main",
79
+ "name": "GemmFastGelu.sgmat_direct_bias",
80
+ "shader": "gemm-subgroup-matrix.wgsl.jinja",
81
+ "bindings": ["x", "w", { "arg": "bias", "elementType": "$bScalar" }, "y", "params"],
82
+ "dispatch": {
83
+ "x": "ceilDiv(nDim, tileN)",
84
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
85
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
86
+ }
87
+ }
88
+ ]
89
+ },
90
+ {
91
+ "id": "sgmat_direct",
92
+ "priority": 110,
93
+ "when": ["noBiasContract", "sgmatDirectContract", "tensorDtypes.X == \"float32\""],
94
+ "requires": {
95
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
96
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "M": 8, "N": 8, "K": 8, "resultComponentType": "f32" }]
97
+ },
98
+ "derive": {
99
+ "hasBias": false,
100
+ "bTransposedStorage": true,
101
+ "xScalar": "\"f32\"",
102
+ "wScalar": "\"f32\"",
103
+ "bScalar": "\"f32\"",
104
+ "yScalar": "\"f32\"",
105
+ "mmaScalar": "\"f32\"",
106
+ "inFeatures": "kDim",
107
+ "outFeatures": "nDim",
108
+ "tileM": "sgmatDirectRows",
109
+ "tileN": "sgmatDirectCols",
110
+ "tileK": 32,
111
+ "directAStorage": true,
112
+ "doubleBufferedB": true,
113
+ "balancedMatrixAccumulation": true
114
+ },
115
+ "passes": [
116
+ {
117
+ "id": "main",
118
+ "name": "GemmFastGelu.sgmat_direct",
119
+ "shader": "gemm-subgroup-matrix.wgsl.jinja",
120
+ "bindings": ["x", "w", "y", "params"],
121
+ "dispatch": {
122
+ "x": "ceilDiv(nDim, tileN)",
123
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
124
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
125
+ }
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ "id": "sgmat_direct_bias_f16",
131
+ "priority": 110,
132
+ "when": ["biasContract", "sgmatDirectContract", "tensorDtypes.X == \"float16\""],
133
+ "requires": {
134
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
135
+ "subgroupMatrixConfigs": [{ "componentType": "f16", "M": 8, "N": 8, "K": 8 }]
136
+ },
137
+ "derive": {
138
+ "hasBias": true,
139
+ "bTransposedStorage": true,
140
+ "xScalar": "\"f16\"",
141
+ "wScalar": "\"f16\"",
142
+ "bScalar": "\"f16\"",
143
+ "yScalar": "\"f16\"",
144
+ "mmaScalar": "\"f16\"",
145
+ "inFeatures": "kDim",
146
+ "outFeatures": "nDim",
147
+ "tileM": "sgmatDirectRows",
148
+ "tileN": "sgmatDirectCols",
149
+ "tileK": 32,
150
+ "directAStorage": true,
151
+ "doubleBufferedB": true,
152
+ "balancedMatrixAccumulation": false
153
+ },
154
+ "passes": [
155
+ {
156
+ "id": "main",
157
+ "name": "GemmFastGelu.sgmat_direct_bias_f16",
158
+ "shader": "gemm-subgroup-matrix.wgsl.jinja",
159
+ "bindings": ["x", "w", { "arg": "bias", "elementType": "$bScalar" }, "y", "params"],
160
+ "dispatch": {
161
+ "x": "ceilDiv(nDim, tileN)",
162
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
163
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
164
+ }
165
+ }
166
+ ]
167
+ },
168
+ {
169
+ "id": "sgmat_direct_f16",
170
+ "priority": 110,
171
+ "when": ["noBiasContract", "sgmatDirectContract", "tensorDtypes.X == \"float16\""],
172
+ "requires": {
173
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
174
+ "subgroupMatrixConfigs": [{ "componentType": "f16", "M": 8, "N": 8, "K": 8 }]
175
+ },
176
+ "derive": {
177
+ "hasBias": false,
178
+ "bTransposedStorage": true,
179
+ "xScalar": "\"f16\"",
180
+ "wScalar": "\"f16\"",
181
+ "bScalar": "\"f16\"",
182
+ "yScalar": "\"f16\"",
183
+ "mmaScalar": "\"f16\"",
184
+ "inFeatures": "kDim",
185
+ "outFeatures": "nDim",
186
+ "tileM": "sgmatDirectRows",
187
+ "tileN": "sgmatDirectCols",
188
+ "tileK": 32,
189
+ "directAStorage": true,
190
+ "doubleBufferedB": true,
191
+ "balancedMatrixAccumulation": false
192
+ },
193
+ "passes": [
194
+ {
195
+ "id": "main",
196
+ "name": "GemmFastGelu.sgmat_direct_f16",
197
+ "shader": "gemm-subgroup-matrix.wgsl.jinja",
198
+ "bindings": ["x", "w", "y", "params"],
199
+ "dispatch": {
200
+ "x": "ceilDiv(nDim, tileN)",
201
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
202
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
203
+ }
204
+ }
205
+ ]
206
+ },
207
  {
208
  "id": "sgmat_bias",
209
  "priority": 100,
210
+ "when": ["biasContract", "sgmatContract", "tensorDtypes.X == \"float32\""],
211
  "requires": {
212
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
213
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "M": 8, "N": 8, "K": 8, "resultComponentType": "f32" }]
 
 
 
214
  },
215
+ "derive": {
 
216
  "hasBias": true,
 
217
  "bTransposedStorage": true,
218
+ "xScalar": "\"f32\"",
219
+ "wScalar": "\"f32\"",
220
+ "bScalar": "\"f32\"",
221
+ "yScalar": "\"f32\"",
222
+ "mmaScalar": "\"f32\"",
 
223
  "inFeatures": "kDim",
224
+ "outFeatures": "nDim",
225
+ "tileM": 32,
226
+ "tileN": 64,
227
+ "tileK": 32,
228
+ "directAStorage": false,
229
+ "doubleBufferedB": false,
230
+ "balancedMatrixAccumulation": false
231
  },
232
  "passes": [
233
  {
234
  "id": "main",
235
+ "name": "GemmFastGelu.sgmat_bias",
236
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
237
+ "bindings": ["x", "w", { "arg": "bias", "elementType": "$bScalar" }, "y", "params"],
238
+ "dispatch": {
239
+ "x": "ceilDiv(nDim, tileN)",
240
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
241
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
242
+ }
243
  }
244
  ]
245
  },
246
  {
247
  "id": "sgmat",
248
  "priority": 100,
249
+ "when": ["noBiasContract", "sgmatContract", "tensorDtypes.X == \"float32\""],
250
  "requires": {
251
  "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
252
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "M": 8, "N": 8, "K": 8, "resultComponentType": "f32" }]
 
 
 
253
  },
254
+ "derive": {
 
255
  "hasBias": false,
 
256
  "bTransposedStorage": true,
257
+ "xScalar": "\"f32\"",
258
+ "wScalar": "\"f32\"",
259
+ "bScalar": "\"f32\"",
260
+ "yScalar": "\"f32\"",
261
+ "mmaScalar": "\"f32\"",
262
  "inFeatures": "kDim",
263
+ "outFeatures": "nDim",
264
+ "tileM": 32,
265
+ "tileN": 64,
266
+ "tileK": 32,
267
+ "directAStorage": false,
268
+ "doubleBufferedB": false,
269
+ "balancedMatrixAccumulation": false
270
  },
271
  "passes": [
272
  {
273
  "id": "main",
274
+ "name": "GemmFastGelu.sgmat",
275
  "shader": "gemm-subgroup-matrix.wgsl.jinja",
276
+ "bindings": ["x", "w", "y", "params"],
277
+ "dispatch": {
278
+ "x": "ceilDiv(nDim, tileN)",
279
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
280
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
281
+ }
282
+ }
283
+ ]
284
+ },
285
+ {
286
+ "id": "sgmat_bias_f16",
287
+ "priority": 100,
288
+ "when": ["biasContract", "sgmatContract", "tensorDtypes.X == \"float16\""],
289
+ "requires": {
290
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
291
+ "subgroupMatrixConfigs": [{ "componentType": "f16", "M": 8, "N": 8, "K": 8 }]
292
+ },
293
+ "derive": {
294
+ "hasBias": true,
295
+ "bTransposedStorage": true,
296
+ "xScalar": "\"f16\"",
297
+ "wScalar": "\"f16\"",
298
+ "bScalar": "\"f16\"",
299
+ "yScalar": "\"f16\"",
300
+ "mmaScalar": "\"f16\"",
301
+ "inFeatures": "kDim",
302
+ "outFeatures": "nDim",
303
+ "tileM": 32,
304
+ "tileN": 64,
305
+ "tileK": 32,
306
+ "directAStorage": false,
307
+ "doubleBufferedB": false,
308
+ "balancedMatrixAccumulation": false
309
+ },
310
+ "passes": [
311
+ {
312
+ "id": "main",
313
+ "name": "GemmFastGelu.sgmat_bias_f16",
314
+ "shader": "gemm-subgroup-matrix.wgsl.jinja",
315
+ "bindings": ["x", "w", { "arg": "bias", "elementType": "$bScalar" }, "y", "params"],
316
+ "dispatch": {
317
+ "x": "ceilDiv(nDim, tileN)",
318
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
319
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
320
+ }
321
+ }
322
+ ]
323
+ },
324
+ {
325
+ "id": "sgmat_f16",
326
+ "priority": 100,
327
+ "when": ["noBiasContract", "sgmatContract", "tensorDtypes.X == \"float16\""],
328
+ "requires": {
329
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
330
+ "subgroupMatrixConfigs": [{ "componentType": "f16", "M": 8, "N": 8, "K": 8 }]
331
+ },
332
+ "derive": {
333
+ "hasBias": false,
334
+ "bTransposedStorage": true,
335
+ "xScalar": "\"f16\"",
336
+ "wScalar": "\"f16\"",
337
+ "bScalar": "\"f16\"",
338
+ "yScalar": "\"f16\"",
339
+ "mmaScalar": "\"f16\"",
340
+ "inFeatures": "kDim",
341
+ "outFeatures": "nDim",
342
+ "tileM": 32,
343
+ "tileN": 64,
344
+ "tileK": 32,
345
+ "directAStorage": false,
346
+ "doubleBufferedB": false,
347
+ "balancedMatrixAccumulation": false
348
+ },
349
+ "passes": [
350
+ {
351
+ "id": "main",
352
+ "name": "GemmFastGelu.sgmat_f16",
353
+ "shader": "gemm-subgroup-matrix.wgsl.jinja",
354
+ "bindings": ["x", "w", "y", "params"],
355
+ "dispatch": {
356
+ "x": "ceilDiv(nDim, tileN)",
357
+ "y": "min(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)",
358
+ "z": "ceilDiv(ceilDiv(gfgRows, tileM), DISPATCH_FOLD_WIDTH)"
359
+ }
360
  }
361
  ]
362
  },
 
364
  "id": "tiled_bias",
365
  "priority": 0,
366
  "when": ["biasContract"],
367
+ "derive": { "hasBias": true, "rowBias": true, "scalarBias": false, "columnBias": false },
368
  "passes": [
369
  {
370
  "id": "main",
371
  "name": "GemmFastGelu.Bias",
372
  "shader": "gemm-fast-gelu.wgsl.jinja",
373
+ "bindings": [
374
+ "a",
375
+ "b",
376
+ "bias",
377
+ "y_2",
378
+ {
379
+ "name": "params",
380
+ "struct": [
381
+ { "name": "M", "type": "u32", "value": "gfgRows" },
382
+ { "name": "N", "type": "u32", "value": "nDim" },
383
+ { "name": "K", "type": "u32", "value": "kDim" },
384
+ { "name": "alpha", "type": "f32", "value": 1 },
385
+ { "name": "beta", "type": "f32", "value": 1 }
386
+ ]
387
+ }
388
+ ],
389
  "dispatch": {
390
+ "x": "min(ceilDiv(nDim, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
391
+ "y": "min(ceilDiv(gfgRows, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
392
  }
393
  }
394
  ]
 
397
  "id": "tiled",
398
  "priority": 0,
399
  "when": ["noBiasContract"],
400
+ "derive": { "hasBias": false },
401
  "passes": [
402
  {
403
  "id": "main",
404
  "name": "GemmFastGelu",
405
  "shader": "gemm-fast-gelu.wgsl.jinja",
406
+ "bindings": [
407
+ "a",
408
+ "b",
409
+ "y_2",
410
+ {
411
+ "name": "params",
412
+ "struct": [
413
+ { "name": "M", "type": "u32", "value": "gfgRows" },
414
+ { "name": "N", "type": "u32", "value": "nDim" },
415
+ { "name": "K", "type": "u32", "value": "kDim" },
416
+ { "name": "alpha", "type": "f32", "value": 1 }
417
+ ]
418
+ }
419
+ ],
420
  "dispatch": {
421
+ "x": "min(ceilDiv(nDim, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))",
422
+ "y": "min(ceilDiv(gfgRows, 64), min(device.limits.maxComputeWorkgroupsPerDimension, 65535))"
423
  }
424
  }
425
  ]
build/webgpu/metadata.json CHANGED
@@ -1,19 +1,33 @@
1
  {
2
  "name": "com.microsoft.GemmFastGelu",
3
- "id": "_com_microsoft_gemmfastgelu_webgpu_720afad",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "CPFUVoaXFHE1nKK5oZMKmB0LohpzyltnfuRq/70nJAk=",
11
- "gemm-fast-gelu.wgsl.jinja": "XL5rMA9oUlUJZCMHq2PZAzmORxFZoz/xa5Mo05/aK3k=",
12
- "gemm-subgroup-matrix.wgsl.jinja": "JJZezmRCzycCZ5Y6RTmbisasCFLbVAgwkSkV4Sct7eY=",
13
- "manifest.json": "bcpv0SoGGVwU23+3nF7YJ2BSX80r64AInCEZgAuCRoI=",
14
- "test.json": "WNyAFlYnnkTg3N4r9ja8Kh2HwlO8jr7EncOBBa9oI48="
15
  }
16
  },
17
- "provenance": { "kernel": { "sha": "c928d21e6cc1310861cba3bafb75f5f679ecf5f3", "dirty": false } },
18
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.GemmFastGelu" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
 
1
  {
2
  "name": "com.microsoft.GemmFastGelu",
3
+ "id": "_com_microsoft_gemmfastgelu_webgpu_a713abb",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "oqlnhXJEZ8273ZAZC7ET+BMVMM8J84BrfZAVpaboErU=",
11
+ "gemm-fast-gelu.wgsl.jinja": "qh+CurhpiRX3buxeBnrOX2J2mzPrlqMP+m++faB5SfI=",
12
+ "gemm-subgroup-matrix.wgsl.jinja": "aaVCZdPC5akSTqDdYvM/MP8NnncAcK9kwSbvrd4Bvj8=",
13
+ "manifest.json": "EespK6n0UbB6+9TwNvJ40Pp/lYYHsHCO0BCKXxLRZhM=",
14
+ "test.json": "LFwbPPwea77gPJaoCjDJtA/uWfmqDYMUtIoDxd8iphY="
15
  }
16
  },
17
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
18
+ "webgpu": {
19
+ "manifestSpec": "2.0",
20
+ "variants": {
21
+ "sgmat_direct_bias": ["gemm-subgroup-matrix.wgsl.jinja"],
22
+ "sgmat_direct": ["gemm-subgroup-matrix.wgsl.jinja"],
23
+ "sgmat_direct_bias_f16": ["gemm-subgroup-matrix.wgsl.jinja"],
24
+ "sgmat_direct_f16": ["gemm-subgroup-matrix.wgsl.jinja"],
25
+ "sgmat_bias": ["gemm-subgroup-matrix.wgsl.jinja"],
26
+ "sgmat": ["gemm-subgroup-matrix.wgsl.jinja"],
27
+ "sgmat_bias_f16": ["gemm-subgroup-matrix.wgsl.jinja"],
28
+ "sgmat_f16": ["gemm-subgroup-matrix.wgsl.jinja"],
29
+ "tiled_bias": ["gemm-fast-gelu.wgsl.jinja"],
30
+ "tiled": ["gemm-fast-gelu.wgsl.jinja"]
31
+ }
32
+ }
33
  }
build/webgpu/test.json CHANGED
The diff for this file is too large to render. See raw diff