sync 91d990483a17
Browse files- README.md +28 -11
- build/webgpu/bench.json +68 -1
- build/webgpu/gemm-fast-gelu.wgsl.jinja +16 -61
- build/webgpu/gemm-subgroup-matrix.wgsl.jinja +215 -59
- build/webgpu/manifest.json +335 -153
- build/webgpu/metadata.json +22 -8
- build/webgpu/test.json +0 -0
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 |
|
| 22 |
-
| --- | --- | --- | --- | --- | --- |
|
| 23 |
-
| `X` | `
|
| 24 |
-
| `W` | `
|
| 25 |
-
| `bias` | `
|
| 26 |
|
| 27 |
## Outputs
|
| 28 |
|
| 29 |
-
| Name |
|
| 30 |
-
| --- | --- | --- | --- | --- | --- |
|
| 31 |
-
| `Y` | `
|
| 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 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
| 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
|
| 145 |
-
{
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
{%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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 }}
|
| 149 |
-
{{ OUT }}[offset + row * OUT_F + col2] = {{ OUT_SCALAR }}({{ epiOpen }}
|
| 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 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
{% for n in range(bTilesPerSubgroup) %}
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 }}
|
| 196 |
{% else %}
|
| 197 |
-
let matrix_b_offset = {{ B_BANK }}subtile_idx * SUB_COLS * TILE_K +
|
| 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 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
"
|
| 6 |
-
|
| 7 |
-
{
|
| 8 |
-
|
| 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 |
-
"
|
| 65 |
-
"
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 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 |
-
"
|
| 153 |
-
"constants": {
|
| 154 |
"hasBias": true,
|
| 155 |
-
"gemmEpilogue": "\"fastgelu\"",
|
| 156 |
"bTransposedStorage": true,
|
| 157 |
-
"xScalar": "\"
|
| 158 |
-
"wScalar": "\"
|
| 159 |
-
"bScalar": "\"
|
| 160 |
-
"yScalar": "\"
|
| 161 |
-
"mmaScalar": "\"
|
| 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.
|
| 170 |
"shader": "gemm-subgroup-matrix.wgsl.jinja",
|
| 171 |
-
"bindings": "
|
| 172 |
-
"dispatch": {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
"
|
| 187 |
-
"constants": {
|
| 188 |
"hasBias": false,
|
| 189 |
-
"gemmEpilogue": "\"fastgelu\"",
|
| 190 |
"bTransposedStorage": true,
|
| 191 |
-
"xScalar": "\"
|
| 192 |
-
"wScalar": "\"
|
| 193 |
-
"
|
| 194 |
-
"
|
| 195 |
-
"
|
| 196 |
"inFeatures": "kDim",
|
| 197 |
-
"outFeatures": "nDim"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
},
|
| 199 |
"passes": [
|
| 200 |
{
|
| 201 |
"id": "main",
|
| 202 |
-
"name": "GemmFastGelu.
|
| 203 |
"shader": "gemm-subgroup-matrix.wgsl.jinja",
|
| 204 |
-
"bindings": "
|
| 205 |
-
"dispatch": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
}
|
| 207 |
]
|
| 208 |
},
|
|
@@ -210,16 +364,31 @@
|
|
| 210 |
"id": "tiled_bias",
|
| 211 |
"priority": 0,
|
| 212 |
"when": ["biasContract"],
|
| 213 |
-
"
|
| 214 |
"passes": [
|
| 215 |
{
|
| 216 |
"id": "main",
|
| 217 |
"name": "GemmFastGelu.Bias",
|
| 218 |
"shader": "gemm-fast-gelu.wgsl.jinja",
|
| 219 |
-
"bindings":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
"
|
| 232 |
"passes": [
|
| 233 |
{
|
| 234 |
"id": "main",
|
| 235 |
"name": "GemmFastGelu",
|
| 236 |
"shader": "gemm-fast-gelu.wgsl.jinja",
|
| 237 |
-
"bindings":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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": "
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
-
"bench.json": "
|
| 11 |
-
"gemm-fast-gelu.wgsl.jinja": "
|
| 12 |
-
"gemm-subgroup-matrix.wgsl.jinja": "
|
| 13 |
-
"manifest.json": "
|
| 14 |
-
"test.json": "
|
| 15 |
}
|
| 16 |
},
|
| 17 |
-
"provenance": { "kernel": { "sha": "
|
| 18 |
-
"webgpu": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|