sync 91d990483a17
Browse files- README.md +26 -16
- build/webgpu/bench.json +9 -10
- build/webgpu/manifest.json +199 -543
- build/webgpu/metadata.json +24 -15
- build/webgpu/qmoe-fc1-activation-grouped-sgmat.wgsl.jinja +26 -30
- build/webgpu/qmoe-fc1-activation-grouped.wgsl.jinja +1 -1
- build/webgpu/qmoe-fc1-activation-stage.wgsl.jinja +4 -5
- build/webgpu/qmoe-mix-stage.wgsl.jinja +4 -5
- build/webgpu/qmoe-output-grouped-sgmat.wgsl.jinja +28 -34
- build/webgpu/qmoe-output-grouped.wgsl.jinja +1 -1
- build/webgpu/qmoe-output-stage.wgsl.jinja +4 -5
- build/webgpu/qmoe-output-zero-inter.wgsl.jinja +3 -4
- build/webgpu/qmoe-route-stage.wgsl.jinja +4 -5
- build/webgpu/test.json +199 -22
README.md
CHANGED
|
@@ -12,26 +12,26 @@ tags:
|
|
| 12 |
|
| 13 |
## Description
|
| 14 |
|
| 15 |
-
Quantized mixture of experts over float32 activations and raw uint8-packed integer expert weights. The package implements 4- and 8-bit symmetric dequantization, ReLU, and interleaved SwiGLU for rank-2 or rank-3 input. Routing uses the softmax over the selected top-k logits
|
| 16 |
|
| 17 |
See the [ONNX Runtime `QMoE` contrib-operator spec](https://github.com/microsoft/onnxruntime/blob/main/docs/ContribOperators.md#com.microsoft.QMoE) for the reference semantics.
|
| 18 |
|
| 19 |
## Inputs
|
| 20 |
|
| 21 |
-
| Name |
|
| 22 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
-
| `
|
| 24 |
-
| `
|
| 25 |
-
| `
|
| 26 |
-
| `
|
| 27 |
-
| `
|
| 28 |
-
| `
|
| 29 |
|
| 30 |
## Outputs
|
| 31 |
|
| 32 |
-
| Name |
|
| 33 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 34 |
-
| `
|
| 35 |
|
| 36 |
## Attributes
|
| 37 |
|
|
@@ -42,15 +42,15 @@ Attributes and default values (overridable per request):
|
|
| 42 |
| `activation_alpha` | `1` | Alpha used by SwiGLU; the exact standard default is 1. |
|
| 43 |
| `activation_beta` | `0` | Beta added to the SwiGLU linear branch; the exact standard default is 0. |
|
| 44 |
| `activation_type` | `"relu"` | Activation applied after FC1. This package supports `relu` and `swiglu`; the exact standard default is `relu`. |
|
|
|
|
| 45 |
| `expert_weight_bits` | `4` | Integer expert-weight bit width. This package supports 4 and 8; the exact standard default is 4. |
|
| 46 |
| `k` | `1` | Number of experts selected per token; the exact standard default is 1. |
|
| 47 |
-
| `normalize_routing_weights` | `0` | Accepted values are 0 and 1. With the separate `router_weights` input omitted,
|
| 48 |
| `quant_type` | `"int"` | Quantization family. This package supports only the exact standard default `int`. |
|
| 49 |
| `swiglu_fusion` | `0` | SwiGLU packing mode. ReLU uses the exact standard default 0; supported SwiGLU interleaves gate/up FC1 rows with value 1. |
|
|
|
|
| 50 |
| `use_sparse_mixer` | `0` | Whether to use sparse-mixer routing. The exact standard default and only supported value is 0. |
|
| 51 |
| `weights_prepacked` | `-1` | Provider weight-layout selector. Values -1 and 0 both consume the public raw packed tensor layout; provider-specific prepacked value 1 is not portable and is rejected. |
|
| 52 |
-
| `block_size` | — | Optional quantization block size along the reduction dimension. Omission selects column-wise scaling; a supplied value must be a power of two of at least 16 and divide both `hidden_size` and `inter_size`. |
|
| 53 |
-
| `swiglu_limit` | — | Optional SwiGLU clamp limit. Omission means no finite clamp. |
|
| 54 |
|
| 55 |
## Type constraints
|
| 56 |
|
|
@@ -60,13 +60,19 @@ Attributes and default values (overridable per request):
|
|
| 60 |
| `T1` | `uint8` |
|
| 61 |
| `T2` | `float32` |
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
## Device requirements
|
| 64 |
|
| 65 |
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.
|
| 66 |
|
| 67 |
## Files
|
| 68 |
|
| 69 |
-
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
|
| 70 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 71 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 72 |
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
|
@@ -85,10 +91,14 @@ Some implementation variants require `subgroup-matrix` and `subgroups`. These ar
|
|
| 85 |
|
| 86 |
## Use with `@huggingface/kernels`
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
| 90 |
|
| 91 |
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
|
|
|
| 92 |
|
| 93 |
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 94 |
|
|
|
|
| 12 |
|
| 13 |
## Description
|
| 14 |
|
| 15 |
+
Quantized mixture of experts over float32 activations and raw uint8-packed integer expert weights. The package implements 4- and 8-bit symmetric dequantization, ReLU, and interleaved SwiGLU for rank-2 or rank-3 input. Routing uses the softmax over the selected top-k logits with `router_weights` absent. Biases, explicit zero points, FC3, sparse mixing, provider-prepacked layouts, and FP4/FP8 modes are not implemented.
|
| 16 |
|
| 17 |
See the [ONNX Runtime `QMoE` contrib-operator spec](https://github.com/microsoft/onnxruntime/blob/main/docs/ContribOperators.md#com.microsoft.QMoE) for the reference semantics.
|
| 18 |
|
| 19 |
## Inputs
|
| 20 |
|
| 21 |
+
| Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
|
| 22 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
+
| `inputT` | `input` | `T` | — | — | Token activations of shape `(num_tokens, hidden_size)` or `(batch_size, sequence_length, hidden_size)`. | required |
|
| 24 |
+
| `routerT` | `router_probs` | `T` | `2` | — | Routing logits of shape `(num_tokens, num_experts)`, where `num_tokens` is the product of the leading input dimensions. | required |
|
| 25 |
+
| `fc1T` | `fc1_experts_weights` | `T1` | `3` | — | Raw packed FC1 weights of shape `(num_experts, fusion_size * inter_size, hidden_size / pack_size)`, where `fusion_size` is 2 only for interleaved SwiGLU and `pack_size` is 2 for 4-bit or 1 for 8-bit weights. | required |
|
| 26 |
+
| `fc1ScalesT` | `fc1_scales` | `T2` | — | — | Required FC1 dequantization scales: rank 2 `(num_experts, fusion_size * inter_size)` for column-wise quantization, or rank 3 with a trailing `hidden_size / block_size` axis. | required |
|
| 27 |
+
| `fc2T` | `fc2_experts_weights` | `T1` | `3` | — | Raw packed FC2 weights of shape `(num_experts, hidden_size, inter_size / pack_size)`. | required |
|
| 28 |
+
| `fc2ScalesT` | `fc2_scales` | `T2` | — | — | Required FC2 dequantization scales: rank 2 `(num_experts, hidden_size)` for column-wise quantization, or rank 3 with a trailing `inter_size / block_size` axis. | required |
|
| 29 |
|
| 30 |
## Outputs
|
| 31 |
|
| 32 |
+
| Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
|
| 33 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 34 |
+
| `outputT` | `output` | `T` | same as `inputT` | same as `inputT` | Routed expert output with the same shape as `input`. | required |
|
| 35 |
|
| 36 |
## Attributes
|
| 37 |
|
|
|
|
| 42 |
| `activation_alpha` | `1` | Alpha used by SwiGLU; the exact standard default is 1. |
|
| 43 |
| `activation_beta` | `0` | Beta added to the SwiGLU linear branch; the exact standard default is 0. |
|
| 44 |
| `activation_type` | `"relu"` | Activation applied after FC1. This package supports `relu` and `swiglu`; the exact standard default is `relu`. |
|
| 45 |
+
| `block_size` | — | Optional quantization block size along the reduction dimension. Omission selects column-wise scaling; a supplied value must be a power of two of at least 16 and divide both `hidden_size` and `inter_size`. |
|
| 46 |
| `expert_weight_bits` | `4` | Integer expert-weight bit width. This package supports 4 and 8; the exact standard default is 4. |
|
| 47 |
| `k` | `1` | Number of experts selected per token; the exact standard default is 1. |
|
| 48 |
+
| `normalize_routing_weights` | `0` | Accepted values are 0 and 1. With the separate `router_weights` input omitted, routing applies a softmax over the selected top-k logits for either value; the attribute only distinguishes the unsupported separate-weight path. |
|
| 49 |
| `quant_type` | `"int"` | Quantization family. This package supports only the exact standard default `int`. |
|
| 50 |
| `swiglu_fusion` | `0` | SwiGLU packing mode. ReLU uses the exact standard default 0; supported SwiGLU interleaves gate/up FC1 rows with value 1. |
|
| 51 |
+
| `swiglu_limit` | — | Optional SwiGLU clamp limit. Omission means no finite clamp. |
|
| 52 |
| `use_sparse_mixer` | `0` | Whether to use sparse-mixer routing. The exact standard default and only supported value is 0. |
|
| 53 |
| `weights_prepacked` | `-1` | Provider weight-layout selector. Values -1 and 0 both consume the public raw packed tensor layout; provider-specific prepacked value 1 is not portable and is rejected. |
|
|
|
|
|
|
|
| 54 |
|
| 55 |
## Type constraints
|
| 56 |
|
|
|
|
| 60 |
| `T1` | `uint8` |
|
| 61 |
| `T2` | `float32` |
|
| 62 |
|
| 63 |
+
## Implementation variants
|
| 64 |
+
|
| 65 |
+
One implementation is selected per call from the device capabilities, the request shapes and the dtypes; these notes say what each one covers.
|
| 66 |
+
|
| 67 |
+
- `quant_grouped_sgmat_routed` — Expert-grouped f32 matrix multiplication with per-invocation routing slots and reuse of the input tile for output publication. The larger operand/output footprint determines workgroup storage admission from device limits.
|
| 68 |
+
|
| 69 |
## Device requirements
|
| 70 |
|
| 71 |
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.
|
| 72 |
|
| 73 |
## Files
|
| 74 |
|
| 75 |
+
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
|
| 76 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 77 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 78 |
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
|
|
|
| 91 |
|
| 92 |
## Use with `@huggingface/kernels`
|
| 93 |
|
| 94 |
+
```sh
|
| 95 |
+
npm install --save-exact @huggingface/kernels@0.0.1-preview.2
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
|
| 99 |
|
| 100 |
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
| 101 |
+
It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
|
| 102 |
|
| 103 |
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 104 |
|
build/webgpu/bench.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
{
|
| 2 |
-
"op": "com.microsoft.QMoE",
|
| 3 |
"tunableSpace": {
|
| 4 |
"workgroupSize": [64, 128, 256],
|
| 5 |
"groupThreads": [8, 16],
|
|
@@ -109,7 +108,7 @@
|
|
| 109 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 110 |
},
|
| 111 |
"provenance": {
|
| 112 |
-
"notes": "
|
| 113 |
}
|
| 114 |
},
|
| 115 |
{
|
|
@@ -144,7 +143,7 @@
|
|
| 144 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 145 |
},
|
| 146 |
"provenance": {
|
| 147 |
-
"notes": "
|
| 148 |
}
|
| 149 |
},
|
| 150 |
{
|
|
@@ -180,7 +179,7 @@
|
|
| 180 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 181 |
},
|
| 182 |
"provenance": {
|
| 183 |
-
"notes": "
|
| 184 |
}
|
| 185 |
},
|
| 186 |
{
|
|
@@ -215,7 +214,7 @@
|
|
| 215 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 216 |
},
|
| 217 |
"provenance": {
|
| 218 |
-
"notes": "
|
| 219 |
}
|
| 220 |
},
|
| 221 |
{
|
|
@@ -249,7 +248,7 @@
|
|
| 249 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 2 * args.hidden * args.inter * args.bits / 2" }]
|
| 250 |
},
|
| 251 |
"provenance": {
|
| 252 |
-
"notes": "
|
| 253 |
}
|
| 254 |
},
|
| 255 |
{
|
|
@@ -419,7 +418,7 @@
|
|
| 419 |
"name": "qmoe-q4-qwen3-moe-decode-t1-h2048-e64-k8-i768",
|
| 420 |
"preset": "model",
|
| 421 |
"provenance": {
|
| 422 |
-
"notes": "Qwen3-MoE
|
| 423 |
},
|
| 424 |
"vars": { "tokens": 1, "hidden": 2048, "experts": 64, "inter": 768, "topK": 8, "bits": 4 },
|
| 425 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 8 },
|
|
@@ -489,7 +488,7 @@
|
|
| 489 |
"name": "qmoe-q4-olmoe-decode-t1-h2048-e32-k8-i2048",
|
| 490 |
"preset": "model",
|
| 491 |
"provenance": {
|
| 492 |
-
"notes": "OLMoE
|
| 493 |
},
|
| 494 |
"vars": { "tokens": 1, "hidden": 2048, "experts": 32, "inter": 2048, "topK": 8, "bits": 4 },
|
| 495 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 8 },
|
|
@@ -557,7 +556,7 @@
|
|
| 557 |
"name": "qmoe-q4-gpt-oss-decode-t1-h2880-e16-k4-i2880",
|
| 558 |
"preset": "model",
|
| 559 |
"provenance": {
|
| 560 |
-
"notes": "GPT-OSS
|
| 561 |
},
|
| 562 |
"vars": { "tokens": 1, "hidden": 2880, "experts": 16, "inter": 2880, "topK": 4, "bits": 4 },
|
| 563 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 4 },
|
|
@@ -625,7 +624,7 @@
|
|
| 625 |
"name": "qmoe-q4-phimoe-decode-t1-h4096-e4-k2-i6400",
|
| 626 |
"preset": "model",
|
| 627 |
"provenance": {
|
| 628 |
-
"notes": "Phi-MoE
|
| 629 |
},
|
| 630 |
"vars": { "tokens": 1, "hidden": 4096, "experts": 4, "inter": 6400, "topK": 2, "bits": 4 },
|
| 631 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 2 },
|
|
|
|
| 1 |
{
|
|
|
|
| 2 |
"tunableSpace": {
|
| 3 |
"workgroupSize": [64, 128, 256],
|
| 4 |
"groupThreads": [8, 16],
|
|
|
|
| 108 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 109 |
},
|
| 110 |
"provenance": {
|
| 111 |
+
"notes": "The bandwidth metric counts the selected experts' three quantized weight matrices at `bits/2` bytes per logical weight in the bound storage layout."
|
| 112 |
}
|
| 113 |
},
|
| 114 |
{
|
|
|
|
| 143 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 144 |
},
|
| 145 |
"provenance": {
|
| 146 |
+
"notes": "The bandwidth metric counts the selected experts' three quantized weight matrices at `bits/2` bytes per logical weight in the bound storage layout."
|
| 147 |
}
|
| 148 |
},
|
| 149 |
{
|
|
|
|
| 179 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 180 |
},
|
| 181 |
"provenance": {
|
| 182 |
+
"notes": "The bandwidth metric counts the selected experts' three quantized weight matrices at `bits/2` bytes per logical weight in the bound storage layout."
|
| 183 |
}
|
| 184 |
},
|
| 185 |
{
|
|
|
|
| 214 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 3 * args.hidden * args.inter * args.bits / 2" }]
|
| 215 |
},
|
| 216 |
"provenance": {
|
| 217 |
+
"notes": "The bandwidth metric counts the selected experts' three quantized weight matrices at `bits/2` bytes per logical weight in the bound storage layout."
|
| 218 |
}
|
| 219 |
},
|
| 220 |
{
|
|
|
|
| 248 |
"metrics": [{ "type": "bandwidth", "value": "args.topK * 2 * args.hidden * args.inter * args.bits / 2" }]
|
| 249 |
},
|
| 250 |
"provenance": {
|
| 251 |
+
"notes": "The bandwidth metric counts the selected expert's two quantized weight matrices at `bits/2` bytes per logical weight in the bound storage layout."
|
| 252 |
}
|
| 253 |
},
|
| 254 |
{
|
|
|
|
| 418 |
"name": "qmoe-q4-qwen3-moe-decode-t1-h2048-e64-k8-i768",
|
| 419 |
"preset": "model",
|
| 420 |
"provenance": {
|
| 421 |
+
"notes": "Qwen3-MoE geometry uses hidden size 2,048, intermediate size 768, top-k 8, and 64 instantiated experts to keep each weight tensor below 512 MB. The bandwidth metric counts three selected quantized matrices at `bits/2` bytes per logical weight."
|
| 422 |
},
|
| 423 |
"vars": { "tokens": 1, "hidden": 2048, "experts": 64, "inter": 768, "topK": 8, "bits": 4 },
|
| 424 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 8 },
|
|
|
|
| 488 |
"name": "qmoe-q4-olmoe-decode-t1-h2048-e32-k8-i2048",
|
| 489 |
"preset": "model",
|
| 490 |
"provenance": {
|
| 491 |
+
"notes": "OLMoE geometry uses hidden and intermediate sizes of 2,048, top-k 8, and 32 instantiated experts. The bandwidth metric counts three selected quantized matrices at `bits/2` bytes per logical weight."
|
| 492 |
},
|
| 493 |
"vars": { "tokens": 1, "hidden": 2048, "experts": 32, "inter": 2048, "topK": 8, "bits": 4 },
|
| 494 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 8 },
|
|
|
|
| 556 |
"name": "qmoe-q4-gpt-oss-decode-t1-h2880-e16-k4-i2880",
|
| 557 |
"preset": "model",
|
| 558 |
"provenance": {
|
| 559 |
+
"notes": "GPT-OSS geometry uses hidden and intermediate sizes of 2,880, top-k 4, and 16 instantiated experts. The bandwidth metric counts three selected quantized matrices at `bits/2` bytes per logical weight."
|
| 560 |
},
|
| 561 |
"vars": { "tokens": 1, "hidden": 2880, "experts": 16, "inter": 2880, "topK": 4, "bits": 4 },
|
| 562 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 4 },
|
|
|
|
| 624 |
"name": "qmoe-q4-phimoe-decode-t1-h4096-e4-k2-i6400",
|
| 625 |
"preset": "model",
|
| 626 |
"provenance": {
|
| 627 |
+
"notes": "Phi-MoE geometry uses hidden size 4,096, intermediate size 6,400, top-k 2, and four instantiated experts. The bandwidth metric counts three selected quantized matrices at `bits/2` bytes per logical weight."
|
| 628 |
},
|
| 629 |
"vars": { "tokens": 1, "hidden": 4096, "experts": 4, "inter": 6400, "topK": 2, "bits": 4 },
|
| 630 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1, "k": 2 },
|
build/webgpu/manifest.json
CHANGED
|
@@ -2,75 +2,28 @@
|
|
| 2 |
"domain": "com.microsoft",
|
| 3 |
"name": "QMoE",
|
| 4 |
"sinceVersion": 1,
|
| 5 |
-
"
|
| 6 |
-
|
| 7 |
-
{
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
{
|
| 13 |
-
"role": "router_probs",
|
| 14 |
-
"dtype": "T",
|
| 15 |
-
"rank": 2,
|
| 16 |
-
"description": "Routing logits of shape `(num_tokens, num_experts)`, where `num_tokens` is the product of the leading input dimensions."
|
| 17 |
-
},
|
| 18 |
-
{
|
| 19 |
-
"role": "fc1_experts_weights",
|
| 20 |
-
"dtype": "T1",
|
| 21 |
-
"rank": 3,
|
| 22 |
-
"description": "Raw packed FC1 weights of shape `(num_experts, fusion_size * inter_size, hidden_size / pack_size)`, where `fusion_size` is 2 only for interleaved SwiGLU and `pack_size` is 2 for 4-bit or 1 for 8-bit weights."
|
| 23 |
-
},
|
| 24 |
-
{
|
| 25 |
-
"role": "fc1_scales",
|
| 26 |
-
"dtype": "T2",
|
| 27 |
-
"description": "Required FC1 dequantization scales: rank 2 `(num_experts, fusion_size * inter_size)` for column-wise quantization, or rank 3 with a trailing `hidden_size / block_size` axis."
|
| 28 |
-
},
|
| 29 |
-
{
|
| 30 |
-
"role": "fc2_experts_weights",
|
| 31 |
-
"dtype": "T1",
|
| 32 |
-
"rank": 3,
|
| 33 |
-
"description": "Raw packed FC2 weights of shape `(num_experts, hidden_size, inter_size / pack_size)`."
|
| 34 |
-
},
|
| 35 |
-
{
|
| 36 |
-
"role": "fc2_scales",
|
| 37 |
-
"dtype": "T2",
|
| 38 |
-
"description": "Required FC2 dequantization scales: rank 2 `(num_experts, hidden_size)` for column-wise quantization, or rank 3 with a trailing `inter_size / block_size` axis."
|
| 39 |
-
}
|
| 40 |
-
],
|
| 41 |
-
"outputs": [
|
| 42 |
-
{
|
| 43 |
-
"role": "output",
|
| 44 |
-
"dtype": "T",
|
| 45 |
-
"shape": "shapes.input",
|
| 46 |
-
"description": "Routed expert output with the same shape as `input`."
|
| 47 |
-
}
|
| 48 |
-
],
|
| 49 |
-
"attributes": {
|
| 50 |
-
"activation_alpha": 1,
|
| 51 |
-
"activation_beta": 0,
|
| 52 |
-
"activation_type": "relu",
|
| 53 |
-
"expert_weight_bits": 4,
|
| 54 |
-
"k": 1,
|
| 55 |
-
"normalize_routing_weights": 0,
|
| 56 |
-
"quant_type": "int",
|
| 57 |
-
"swiglu_fusion": 0,
|
| 58 |
-
"use_sparse_mixer": 0,
|
| 59 |
-
"weights_prepacked": -1
|
| 60 |
},
|
| 61 |
-
"
|
| 62 |
-
|
| 63 |
-
"
|
| 64 |
-
"
|
| 65 |
-
"
|
| 66 |
-
"expert_weight_bits":
|
| 67 |
-
"k":
|
| 68 |
-
"normalize_routing_weights": "
|
| 69 |
-
"quant_type":
|
| 70 |
-
"swiglu_fusion":
|
| 71 |
-
"
|
| 72 |
-
"
|
| 73 |
-
"
|
|
|
|
| 74 |
},
|
| 75 |
"attributeConstraints": {
|
| 76 |
"activation_type": { "values": ["relu", "swiglu"] },
|
|
@@ -82,25 +35,16 @@
|
|
| 82 |
"weights_prepacked": { "values": [-1, 0] }
|
| 83 |
},
|
| 84 |
"typeConstraints": { "T": ["float32"], "T1": ["uint8"], "T2": ["float32"] },
|
| 85 |
-
"args": {
|
| 86 |
-
"inputT": { "kind": "tensor", "semantic": "input", "role": "input" },
|
| 87 |
-
"routerT": { "kind": "tensor", "semantic": "router_probs", "role": "input" },
|
| 88 |
-
"fc1T": { "kind": "tensor", "semantic": "fc1_experts_weights", "role": "input" },
|
| 89 |
-
"fc1ScalesT": { "kind": "tensor", "semantic": "fc1_scales", "role": "input" },
|
| 90 |
-
"fc2T": { "kind": "tensor", "semantic": "fc2_experts_weights", "role": "input" },
|
| 91 |
-
"fc2ScalesT": { "kind": "tensor", "semantic": "fc2_scales", "role": "input" },
|
| 92 |
-
"outputT": { "kind": "tensor", "semantic": "output", "role": "output" }
|
| 93 |
-
},
|
| 94 |
"tunables": {
|
| 95 |
-
"workgroupSize": 64,
|
| 96 |
-
"decodeLanes": 32,
|
| 97 |
-
"decodeBlockTarget": 1024,
|
| 98 |
-
"decodeMinLaneTrips": 4,
|
| 99 |
-
"groupThreads": 8,
|
| 100 |
-
"groupRegM": 4,
|
| 101 |
-
"groupRegN": 4,
|
| 102 |
-
"groupTileK": 16,
|
| 103 |
-
"groupRouteWorkgroup": 256
|
| 104 |
},
|
| 105 |
"derive": {
|
| 106 |
"deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
|
|
@@ -108,28 +52,28 @@
|
|
| 108 |
"canPinSubgroupSize32": "device.features.has(\"subgroups\") and device.features.has(\"subgroup-size-control\") and has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize <= 32 and device.adapterInfo.subgroupMaxSize >= 32",
|
| 109 |
"pinSubgroupSize32": "canPinSubgroupSize32 and not wave32Adapter",
|
| 110 |
"wave32Effective": "wave32Adapter or pinSubgroupSize32",
|
| 111 |
-
"hiddenSize": "dim(shapes.
|
| 112 |
-
"numTokens": "numel(shapes.
|
| 113 |
"topK": "attrs.k",
|
| 114 |
"weightBits": "attrs.expert_weight_bits",
|
| 115 |
"quantBlockSize": "attrs.block_size if has(attrs, \"block_size\") else 0",
|
| 116 |
"packSize": "2 if weightBits == 4 else 1",
|
| 117 |
"quantMidpoint": "8 if weightBits == 4 else 128",
|
| 118 |
"fusionSize": "2 if attrs.activation_type == \"swiglu\" and attrs.swiglu_fusion == 1 else 1",
|
| 119 |
-
"interSize": "dim(shapes.
|
| 120 |
-
"fc1PackedCols": "dim(shapes.
|
| 121 |
-
"fc2PackedCols": "dim(shapes.
|
| 122 |
"colWiseScales": "quantBlockSize == 0",
|
| 123 |
"fc1ScaleBlocks": "1 if colWiseScales else hiddenSize / max(1, quantBlockSize)",
|
| 124 |
"fc2ScaleBlocks": "1 if colWiseScales else interSize / max(1, quantBlockSize)",
|
| 125 |
"activationSupported": "(attrs.activation_type == \"relu\" and attrs.swiglu_fusion == 0) or (attrs.activation_type == \"swiglu\" and attrs.swiglu_fusion == 1)",
|
| 126 |
"routingModeSupported": "attrs.normalize_routing_weights == 0 or attrs.normalize_routing_weights == 1",
|
| 127 |
"rawWeightLayout": "attrs.weights_prepacked == -1 or attrs.weights_prepacked == 0",
|
| 128 |
-
"inputOutputShapeOk": "((ranks.
|
| 129 |
"quantBlockSizeOk": "colWiseScales or (quantBlockSize >= 16 and pow2ceil(quantBlockSize) == quantBlockSize and hiddenSize % quantBlockSize == 0 and interSize % quantBlockSize == 0)",
|
| 130 |
-
"quantScalesOk": "tensorDtypes.
|
| 131 |
-
"quantShapeOk": "inputOutputShapeOk and ranks.
|
| 132 |
-
"quantContract": "activationSupported and routingModeSupported and rawWeightLayout and quantShapeOk and topK >= 1 and topK <= dim(shapes.
|
| 133 |
"workgroupSizeOk": "tunables.workgroupSize >= 1 and tunables.workgroupSize <= deviceWorkgroupCap",
|
| 134 |
"hiddenChunkFits": "topK * interSize * 4 <= device.limits.maxStorageBufferBindingSize and topK * interSize * 4 <= device.limits.maxBufferSize",
|
| 135 |
"hiddenChunkTokens": "numTokens if interSize == 0 else min(numTokens, max(1, floor(min(device.limits.maxStorageBufferBindingSize, device.limits.maxBufferSize) / (topK * interSize * 4))))",
|
|
@@ -141,421 +85,89 @@
|
|
| 141 |
"groupTileKVec": "ceilDiv(tunables.groupTileK, 4)",
|
| 142 |
"groupThreadCount": "tunables.groupThreads * tunables.groupThreads",
|
| 143 |
"groupSlots": "hiddenChunkTokens * topK",
|
| 144 |
-
"groupMaxTiles": "ceilDiv(groupSlots, max(1, groupTileM)) + dim(shapes.
|
| 145 |
"groupSharedBytes": "(groupTileM * tunables.groupTileK + 2 * groupTileN * tunables.groupTileK + groupTileM) * 4",
|
| 146 |
"groupSlotOutBytes": "hiddenChunkTokens * topK * hiddenSize * 4",
|
| 147 |
-
"groupedDeviceOk": "groupThreadCount <= device.limits.maxComputeInvocationsPerWorkgroup and tunables.groupThreads <= device.limits.maxComputeWorkgroupSizeX and tunables.groupThreads <= device.limits.maxComputeWorkgroupSizeY and tunables.groupRouteWorkgroup <= deviceWorkgroupCap and groupSharedBytes <= device.limits.maxComputeWorkgroupStorageSize and dim(shapes.
|
| 148 |
-
"groupedDispatchOk": "groupMaxTiles <= device.limits.maxComputeWorkgroupsPerDimension and ceilDiv(interSize, max(1, groupTileN)) <= device.limits.maxComputeWorkgroupsPerDimension and ceilDiv(hiddenSize, max(1, groupTileN)) <= device.limits.maxComputeWorkgroupsPerDimension",
|
| 149 |
"groupSlotOutFits": "groupSlotOutBytes <= device.limits.maxStorageBufferBindingSize and groupSlotOutBytes <= device.limits.maxBufferSize",
|
| 150 |
-
"groupedShapeOk": "numTokens * topK * 4 >= groupTileM * dim(shapes.
|
| 151 |
"groupedContract": "quantContract and workgroupSizeOk and hiddenChunkFits and routedScratchFits and interSize > 0 and groupedDeviceOk and groupedDispatchOk and groupSlotOutFits and groupedShapeOk",
|
| 152 |
"sgmatWorkgroup": "128",
|
| 153 |
"sgmatSubgroups": "4",
|
| 154 |
"sgmatRowSubtiles": "2",
|
| 155 |
-
"
|
| 156 |
-
"
|
|
|
|
| 157 |
"decodeLanesOk": "tunables.decodeLanes >= 1 and tunables.decodeLanes <= tunables.workgroupSize and tunables.workgroupSize % tunables.decodeLanes == 0",
|
| 158 |
"decodeRows": "max(1, tunables.workgroupSize / max(1, tunables.decodeLanes))",
|
| 159 |
"decodeDeviceOk": "workgroupSizeOk and decodeLanesOk and tunables.decodeLanes <= device.limits.maxComputeWorkgroupSizeX and decodeRows <= device.limits.maxComputeWorkgroupSizeY and tunables.workgroupSize * 8 <= device.limits.maxComputeWorkgroupStorageSize",
|
| 160 |
-
"decodeDispatchOk": "ceilDiv(interSize, decodeRows) <= device.limits.maxComputeWorkgroupsPerDimension and ceilDiv(hiddenSize, decodeRows) <= device.limits.maxComputeWorkgroupsPerDimension and hiddenChunkTokens * topK <= device.limits.maxComputeWorkgroupsPerDimension",
|
| 161 |
"decodeLaneDepth": "min(hiddenSize, interSize) / max(1, tunables.decodeLanes)",
|
| 162 |
"decodeDepthOk": "decodeLaneDepth >= tunables.decodeMinLaneTrips",
|
| 163 |
"splitFc1Blocks": "ceilDiv(numTokens * topK * interSize, max(1, tunables.workgroupSize))",
|
| 164 |
"decodeOccupancyOk": "splitFc1Blocks <= tunables.decodeBlockTarget",
|
| 165 |
-
"decodeContract": "quantContract and decodeDeviceOk and decodeDispatchOk and hiddenChunkFits and routedScratchFits and interSize > 0 and decodeDepthOk and (decodeOccupancyOk or not groupedContract)"
|
| 166 |
-
},
|
| 167 |
-
"constants": {
|
| 168 |
"tokens": "numTokens",
|
| 169 |
"hidden": "hiddenSize",
|
| 170 |
-
"experts": "dim(shapes.
|
| 171 |
-
"fc1Rows": "dim(shapes.
|
| 172 |
"inter": "interSize",
|
| 173 |
-
"topK": "topK",
|
| 174 |
"workgroupSize": "tunables.workgroupSize",
|
| 175 |
"activationType": "attrs.activation_type",
|
| 176 |
"hasSwigluLimit": "has(attrs, \"swiglu_limit\")",
|
| 177 |
-
"swigluLimit": "attrs.swiglu_limit if has(attrs, \"swiglu_limit\") else 0"
|
| 178 |
-
"quantMidpoint": "quantMidpoint",
|
| 179 |
-
"quantBlockSize": "quantBlockSize",
|
| 180 |
-
"packSize": "packSize",
|
| 181 |
-
"fc1PackedCols": "fc1PackedCols",
|
| 182 |
-
"fc2PackedCols": "fc2PackedCols",
|
| 183 |
-
"colWiseScales": "colWiseScales",
|
| 184 |
-
"fc1ScaleBlocks": "fc1ScaleBlocks",
|
| 185 |
-
"fc2ScaleBlocks": "fc2ScaleBlocks"
|
| 186 |
},
|
| 187 |
-
"
|
| 188 |
-
"
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
"
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
"
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
"name": "route_expert",
|
| 209 |
-
"semantic": "routeExpert",
|
| 210 |
-
"buffer": { "type": "read-only-storage" },
|
| 211 |
-
"elementType": "u32"
|
| 212 |
-
},
|
| 213 |
-
{
|
| 214 |
-
"name": "fc1_experts_weights",
|
| 215 |
-
"arg": "fc1T",
|
| 216 |
-
"semantic": "fc1_experts_weights",
|
| 217 |
-
"buffer": { "type": "read-only-storage" },
|
| 218 |
-
"elementType": "u32"
|
| 219 |
-
},
|
| 220 |
-
{
|
| 221 |
-
"name": "fc1_scales",
|
| 222 |
-
"arg": "fc1ScalesT",
|
| 223 |
-
"semantic": "fc1_scales",
|
| 224 |
-
"buffer": { "type": "read-only-storage" },
|
| 225 |
-
"elementType": "f32"
|
| 226 |
-
},
|
| 227 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 228 |
-
{
|
| 229 |
-
"name": "params",
|
| 230 |
-
"semantic": "kernel.params",
|
| 231 |
-
"buffer": { "type": "uniform" },
|
| 232 |
-
"struct": {
|
| 233 |
-
"name": "Params",
|
| 234 |
-
"fields": [
|
| 235 |
-
{ "name": "activationAlpha", "type": "f32", "value": "attrs.activation_alpha" },
|
| 236 |
-
{ "name": "activationBeta", "type": "f32", "value": "attrs.activation_beta" },
|
| 237 |
-
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" },
|
| 238 |
-
{
|
| 239 |
-
"name": "tokenCount",
|
| 240 |
-
"type": "u32",
|
| 241 |
-
"value": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
| 242 |
-
}
|
| 243 |
-
]
|
| 244 |
-
}
|
| 245 |
-
}
|
| 246 |
-
],
|
| 247 |
-
"output": [
|
| 248 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 249 |
-
{
|
| 250 |
-
"name": "route_expert",
|
| 251 |
-
"semantic": "routeExpert",
|
| 252 |
-
"buffer": { "type": "read-only-storage" },
|
| 253 |
-
"elementType": "u32"
|
| 254 |
-
},
|
| 255 |
-
{ "name": "route_mix", "semantic": "routeMix", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 256 |
-
{
|
| 257 |
-
"name": "fc2_experts_weights",
|
| 258 |
-
"arg": "fc2T",
|
| 259 |
-
"semantic": "fc2_experts_weights",
|
| 260 |
-
"buffer": { "type": "read-only-storage" },
|
| 261 |
-
"elementType": "u32"
|
| 262 |
-
},
|
| 263 |
-
{
|
| 264 |
-
"name": "fc2_scales",
|
| 265 |
-
"arg": "fc2ScalesT",
|
| 266 |
-
"semantic": "fc2_scales",
|
| 267 |
-
"buffer": { "type": "read-only-storage" },
|
| 268 |
-
"elementType": "f32"
|
| 269 |
-
},
|
| 270 |
-
{
|
| 271 |
-
"name": "output",
|
| 272 |
-
"arg": "outputT",
|
| 273 |
-
"semantic": "output",
|
| 274 |
-
"buffer": { "type": "storage" },
|
| 275 |
-
"elementType": "f32"
|
| 276 |
-
},
|
| 277 |
-
{
|
| 278 |
-
"name": "params",
|
| 279 |
-
"semantic": "kernel.params",
|
| 280 |
-
"buffer": { "type": "uniform" },
|
| 281 |
-
"struct": {
|
| 282 |
-
"name": "Params",
|
| 283 |
-
"fields": [
|
| 284 |
-
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" },
|
| 285 |
-
{
|
| 286 |
-
"name": "tokenCount",
|
| 287 |
-
"type": "u32",
|
| 288 |
-
"value": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
| 289 |
-
}
|
| 290 |
-
]
|
| 291 |
-
}
|
| 292 |
-
}
|
| 293 |
-
],
|
| 294 |
-
"zeroInterOutput": [
|
| 295 |
-
{
|
| 296 |
-
"name": "output",
|
| 297 |
-
"arg": "outputT",
|
| 298 |
-
"semantic": "output",
|
| 299 |
-
"buffer": { "type": "storage" },
|
| 300 |
-
"elementType": "f32"
|
| 301 |
-
}
|
| 302 |
-
],
|
| 303 |
-
"group": [
|
| 304 |
-
{
|
| 305 |
-
"name": "route_expert",
|
| 306 |
-
"semantic": "routeExpert",
|
| 307 |
-
"buffer": { "type": "read-only-storage" },
|
| 308 |
-
"elementType": "u32"
|
| 309 |
-
},
|
| 310 |
-
{ "name": "slot_list", "semantic": "slotList", "buffer": { "type": "storage" }, "elementType": "u32" },
|
| 311 |
-
{ "name": "tile_meta", "semantic": "tileMeta", "buffer": { "type": "storage" }, "elementType": "u32" },
|
| 312 |
-
{
|
| 313 |
-
"name": "params",
|
| 314 |
-
"semantic": "kernel.params",
|
| 315 |
-
"buffer": { "type": "uniform" },
|
| 316 |
-
"struct": {
|
| 317 |
-
"name": "Params",
|
| 318 |
-
"fields": [
|
| 319 |
-
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" },
|
| 320 |
-
{
|
| 321 |
-
"name": "tokenCount",
|
| 322 |
-
"type": "u32",
|
| 323 |
-
"value": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
| 324 |
-
}
|
| 325 |
-
]
|
| 326 |
-
}
|
| 327 |
-
}
|
| 328 |
-
],
|
| 329 |
-
"fc1Grouped": [
|
| 330 |
-
{
|
| 331 |
-
"name": "input",
|
| 332 |
-
"arg": "inputT",
|
| 333 |
-
"semantic": "input",
|
| 334 |
-
"buffer": { "type": "read-only-storage" },
|
| 335 |
-
"elementType": "f32"
|
| 336 |
-
},
|
| 337 |
-
{ "name": "slot_list", "semantic": "slotList", "buffer": { "type": "read-only-storage" }, "elementType": "u32" },
|
| 338 |
-
{ "name": "tile_meta", "semantic": "tileMeta", "buffer": { "type": "read-only-storage" }, "elementType": "u32" },
|
| 339 |
-
{
|
| 340 |
-
"name": "fc1_experts_weights",
|
| 341 |
-
"arg": "fc1T",
|
| 342 |
-
"semantic": "fc1_experts_weights",
|
| 343 |
-
"buffer": { "type": "read-only-storage" },
|
| 344 |
-
"elementType": "u32"
|
| 345 |
-
},
|
| 346 |
-
{
|
| 347 |
-
"name": "fc1_scales",
|
| 348 |
-
"arg": "fc1ScalesT",
|
| 349 |
-
"semantic": "fc1_scales",
|
| 350 |
-
"buffer": { "type": "read-only-storage" },
|
| 351 |
-
"elementType": "f32"
|
| 352 |
-
},
|
| 353 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 354 |
-
{
|
| 355 |
-
"name": "params",
|
| 356 |
-
"semantic": "kernel.params",
|
| 357 |
-
"buffer": { "type": "uniform" },
|
| 358 |
-
"struct": {
|
| 359 |
-
"name": "Params",
|
| 360 |
-
"fields": [
|
| 361 |
-
{ "name": "activationAlpha", "type": "f32", "value": "attrs.activation_alpha" },
|
| 362 |
-
{ "name": "activationBeta", "type": "f32", "value": "attrs.activation_beta" },
|
| 363 |
-
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" }
|
| 364 |
-
]
|
| 365 |
-
}
|
| 366 |
-
}
|
| 367 |
-
],
|
| 368 |
-
"outputGrouped": [
|
| 369 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 370 |
-
{ "name": "slot_list", "semantic": "slotList", "buffer": { "type": "read-only-storage" }, "elementType": "u32" },
|
| 371 |
-
{ "name": "tile_meta", "semantic": "tileMeta", "buffer": { "type": "read-only-storage" }, "elementType": "u32" },
|
| 372 |
-
{
|
| 373 |
-
"name": "fc2_experts_weights",
|
| 374 |
-
"arg": "fc2T",
|
| 375 |
-
"semantic": "fc2_experts_weights",
|
| 376 |
-
"buffer": { "type": "read-only-storage" },
|
| 377 |
-
"elementType": "u32"
|
| 378 |
-
},
|
| 379 |
-
{
|
| 380 |
-
"name": "fc2_scales",
|
| 381 |
-
"arg": "fc2ScalesT",
|
| 382 |
-
"semantic": "fc2_scales",
|
| 383 |
-
"buffer": { "type": "read-only-storage" },
|
| 384 |
-
"elementType": "f32"
|
| 385 |
-
},
|
| 386 |
-
{ "name": "slot_out", "semantic": "slotOut", "buffer": { "type": "storage" }, "elementType": "f32" }
|
| 387 |
-
],
|
| 388 |
-
"mix": [
|
| 389 |
-
{ "name": "slot_out", "semantic": "slotOut", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 390 |
-
{ "name": "route_mix", "semantic": "routeMix", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 391 |
-
{
|
| 392 |
-
"name": "output",
|
| 393 |
-
"arg": "outputT",
|
| 394 |
-
"semantic": "output",
|
| 395 |
-
"buffer": { "type": "storage" },
|
| 396 |
-
"elementType": "f32"
|
| 397 |
-
},
|
| 398 |
-
{
|
| 399 |
-
"name": "params",
|
| 400 |
-
"semantic": "kernel.params",
|
| 401 |
-
"buffer": { "type": "uniform" },
|
| 402 |
-
"struct": {
|
| 403 |
-
"name": "Params",
|
| 404 |
-
"fields": [
|
| 405 |
-
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" },
|
| 406 |
-
{
|
| 407 |
-
"name": "tokenCount",
|
| 408 |
-
"type": "u32",
|
| 409 |
-
"value": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
| 410 |
-
}
|
| 411 |
-
]
|
| 412 |
-
}
|
| 413 |
-
}
|
| 414 |
-
],
|
| 415 |
-
"fc1ActivationIo": [
|
| 416 |
-
{
|
| 417 |
-
"name": "input",
|
| 418 |
-
"arg": "inputT",
|
| 419 |
-
"semantic": "input",
|
| 420 |
-
"buffer": { "type": "read-only-storage" },
|
| 421 |
-
"elementType": "f32"
|
| 422 |
-
},
|
| 423 |
-
{
|
| 424 |
-
"name": "route_expert",
|
| 425 |
-
"semantic": "routeExpert",
|
| 426 |
-
"buffer": { "type": "read-only-storage" },
|
| 427 |
-
"elementType": "u32"
|
| 428 |
-
},
|
| 429 |
-
{
|
| 430 |
-
"name": "fc1_experts_weights",
|
| 431 |
-
"arg": "fc1T",
|
| 432 |
-
"semantic": "fc1_experts_weights",
|
| 433 |
-
"buffer": { "type": "read-only-storage" },
|
| 434 |
-
"elementType": "u32"
|
| 435 |
-
},
|
| 436 |
-
{
|
| 437 |
-
"name": "fc1_scales",
|
| 438 |
-
"arg": "fc1ScalesT",
|
| 439 |
-
"semantic": "fc1_scales",
|
| 440 |
-
"buffer": { "type": "read-only-storage" },
|
| 441 |
-
"elementType": "f32"
|
| 442 |
-
},
|
| 443 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "storage" }, "elementType": "f32" }
|
| 444 |
-
],
|
| 445 |
-
"fc1ActivationScheduled": [
|
| 446 |
-
{
|
| 447 |
-
"name": "input",
|
| 448 |
-
"arg": "inputT",
|
| 449 |
-
"semantic": "input",
|
| 450 |
-
"buffer": { "type": "read-only-storage" },
|
| 451 |
-
"elementType": "f32"
|
| 452 |
-
},
|
| 453 |
-
{
|
| 454 |
-
"name": "route_expert",
|
| 455 |
-
"semantic": "routeExpert",
|
| 456 |
-
"buffer": { "type": "read-only-storage" },
|
| 457 |
-
"elementType": "u32"
|
| 458 |
-
},
|
| 459 |
-
{
|
| 460 |
-
"name": "fc1_experts_weights",
|
| 461 |
-
"arg": "fc1T",
|
| 462 |
-
"semantic": "fc1_experts_weights",
|
| 463 |
-
"buffer": { "type": "read-only-storage" },
|
| 464 |
-
"elementType": "u32"
|
| 465 |
-
},
|
| 466 |
-
{
|
| 467 |
-
"name": "fc1_scales",
|
| 468 |
-
"arg": "fc1ScalesT",
|
| 469 |
-
"semantic": "fc1_scales",
|
| 470 |
-
"buffer": { "type": "read-only-storage" },
|
| 471 |
-
"elementType": "f32"
|
| 472 |
-
},
|
| 473 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "storage" }, "elementType": "f32" },
|
| 474 |
-
{
|
| 475 |
-
"name": "params",
|
| 476 |
-
"semantic": "kernel.params",
|
| 477 |
-
"buffer": { "type": "uniform" },
|
| 478 |
-
"struct": {
|
| 479 |
-
"name": "Params",
|
| 480 |
-
"fields": [
|
| 481 |
-
{ "name": "activationAlpha", "type": "f32", "value": "attrs.activation_alpha" },
|
| 482 |
-
{ "name": "activationBeta", "type": "f32", "value": "attrs.activation_beta" },
|
| 483 |
-
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" }
|
| 484 |
-
]
|
| 485 |
-
}
|
| 486 |
-
}
|
| 487 |
-
],
|
| 488 |
-
"outputIo": [
|
| 489 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 490 |
-
{
|
| 491 |
-
"name": "route_expert",
|
| 492 |
-
"semantic": "routeExpert",
|
| 493 |
-
"buffer": { "type": "read-only-storage" },
|
| 494 |
-
"elementType": "u32"
|
| 495 |
-
},
|
| 496 |
-
{ "name": "route_mix", "semantic": "routeMix", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 497 |
-
{
|
| 498 |
-
"name": "fc2_experts_weights",
|
| 499 |
-
"arg": "fc2T",
|
| 500 |
-
"semantic": "fc2_experts_weights",
|
| 501 |
-
"buffer": { "type": "read-only-storage" },
|
| 502 |
-
"elementType": "u32"
|
| 503 |
-
},
|
| 504 |
-
{
|
| 505 |
-
"name": "fc2_scales",
|
| 506 |
-
"arg": "fc2ScalesT",
|
| 507 |
-
"semantic": "fc2_scales",
|
| 508 |
-
"buffer": { "type": "read-only-storage" },
|
| 509 |
-
"elementType": "f32"
|
| 510 |
-
},
|
| 511 |
-
{
|
| 512 |
-
"name": "output",
|
| 513 |
-
"arg": "outputT",
|
| 514 |
-
"semantic": "output",
|
| 515 |
-
"buffer": { "type": "storage" },
|
| 516 |
-
"elementType": "f32"
|
| 517 |
-
}
|
| 518 |
-
],
|
| 519 |
-
"outputScheduled": [
|
| 520 |
-
{ "name": "hidden_act", "semantic": "hiddenAct", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 521 |
-
{
|
| 522 |
-
"name": "route_expert",
|
| 523 |
-
"semantic": "routeExpert",
|
| 524 |
-
"buffer": { "type": "read-only-storage" },
|
| 525 |
-
"elementType": "u32"
|
| 526 |
-
},
|
| 527 |
-
{ "name": "route_mix", "semantic": "routeMix", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
|
| 528 |
-
{
|
| 529 |
-
"name": "fc2_experts_weights",
|
| 530 |
-
"arg": "fc2T",
|
| 531 |
-
"semantic": "fc2_experts_weights",
|
| 532 |
-
"buffer": { "type": "read-only-storage" },
|
| 533 |
-
"elementType": "u32"
|
| 534 |
-
},
|
| 535 |
-
{
|
| 536 |
-
"name": "fc2_scales",
|
| 537 |
-
"arg": "fc2ScalesT",
|
| 538 |
-
"semantic": "fc2_scales",
|
| 539 |
-
"buffer": { "type": "read-only-storage" },
|
| 540 |
-
"elementType": "f32"
|
| 541 |
-
},
|
| 542 |
-
{
|
| 543 |
-
"name": "output",
|
| 544 |
-
"arg": "outputT",
|
| 545 |
-
"semantic": "output",
|
| 546 |
-
"buffer": { "type": "storage" },
|
| 547 |
-
"elementType": "f32"
|
| 548 |
-
},
|
| 549 |
-
{
|
| 550 |
-
"name": "params",
|
| 551 |
-
"semantic": "kernel.params",
|
| 552 |
-
"buffer": { "type": "uniform" },
|
| 553 |
-
"struct": {
|
| 554 |
-
"name": "Params",
|
| 555 |
-
"fields": [{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" }]
|
| 556 |
}
|
| 557 |
-
|
| 558 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 559 |
},
|
| 560 |
"variants": [
|
| 561 |
{
|
|
@@ -566,12 +178,14 @@
|
|
| 566 |
{
|
| 567 |
"id": "output_stage",
|
| 568 |
"name": "QMoE.OutputStageZeroInter",
|
| 569 |
-
"
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
|
|
|
|
|
|
| 575 |
}
|
| 576 |
]
|
| 577 |
},
|
|
@@ -579,13 +193,11 @@
|
|
| 579 |
"id": "quant_grouped_sgmat_routed",
|
| 580 |
"priority": 32,
|
| 581 |
"when": ["groupedSgmatOk", "wave32Effective"],
|
| 582 |
-
"
|
| 583 |
-
"
|
| 584 |
-
"
|
| 585 |
-
"sgmatWorkgroup": "sgmatWorkgroup",
|
| 586 |
-
"sgmatSubgroups": "sgmatSubgroups",
|
| 587 |
-
"sgmatRowSubtiles": "sgmatRowSubtiles"
|
| 588 |
},
|
|
|
|
| 589 |
"intermediates": [
|
| 590 |
{ "id": "routeExpert", "dtype": "uint32", "shape": "[numTokens * topK]" },
|
| 591 |
{ "id": "routeMix", "dtype": "float32", "shape": "[numTokens * topK]" },
|
|
@@ -599,8 +211,12 @@
|
|
| 599 |
"id": "route_stage",
|
| 600 |
"name": "QMoE.RouteStage",
|
| 601 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 602 |
-
"bindings": "
|
| 603 |
-
"dispatch": {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
},
|
| 605 |
{
|
| 606 |
"id": "token_chunks",
|
|
@@ -610,54 +226,48 @@
|
|
| 610 |
"id": "group_stage",
|
| 611 |
"name": "QMoE.GroupStage",
|
| 612 |
"shader": "expert-group-slots.wgsl.jinja",
|
| 613 |
-
"bindings": "
|
| 614 |
"dispatch": {
|
| 615 |
-
"
|
| 616 |
-
"
|
|
|
|
| 617 |
}
|
| 618 |
},
|
| 619 |
{
|
| 620 |
"id": "fc1_activation_stage",
|
| 621 |
"name": "QMoE.FC1ActivationStageGroupedSgmat",
|
| 622 |
"shader": "qmoe-fc1-activation-grouped-sgmat.wgsl.jinja",
|
| 623 |
-
"bindings": "
|
| 624 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(interSize * fusionSize, 64)" }
|
| 625 |
},
|
| 626 |
{
|
| 627 |
"id": "output_stage",
|
| 628 |
"name": "QMoE.OutputStageGroupedSgmat",
|
| 629 |
"shader": "qmoe-output-grouped-sgmat.wgsl.jinja",
|
| 630 |
-
"bindings": "
|
| 631 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(hiddenSize, 64)" }
|
| 632 |
},
|
| 633 |
{
|
| 634 |
"id": "mix_stage",
|
| 635 |
"name": "QMoE.MixStage",
|
| 636 |
"shader": "qmoe-mix-stage.wgsl.jinja",
|
| 637 |
-
"bindings": "
|
| 638 |
"dispatch": {
|
| 639 |
-
"
|
| 640 |
-
"
|
|
|
|
| 641 |
}
|
| 642 |
}
|
| 643 |
]
|
| 644 |
}
|
| 645 |
-
]
|
| 646 |
-
"requires": {
|
| 647 |
-
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 648 |
-
"limits": { "maxComputeWorkgroupStorageSize": 16512 },
|
| 649 |
-
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
| 650 |
-
}
|
| 651 |
},
|
| 652 |
{
|
| 653 |
"id": "quant_grouped_routed",
|
| 654 |
"priority": 30,
|
| 655 |
"when": ["groupedContract"],
|
| 656 |
-
"
|
| 657 |
-
"groupTileM": "groupTileM",
|
| 658 |
-
"groupTileN": "groupTileN",
|
| 659 |
"groupTileK": "tunables.groupTileK",
|
| 660 |
-
"groupTileKVec": "groupTileKVec",
|
| 661 |
"groupThreads": "tunables.groupThreads",
|
| 662 |
"regM": "tunables.groupRegM",
|
| 663 |
"regN": "tunables.groupRegN",
|
|
@@ -676,8 +286,12 @@
|
|
| 676 |
"id": "route_stage",
|
| 677 |
"name": "QMoE.RouteStage",
|
| 678 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 679 |
-
"bindings": "
|
| 680 |
-
"dispatch": {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 681 |
},
|
| 682 |
{
|
| 683 |
"id": "token_chunks",
|
|
@@ -687,34 +301,36 @@
|
|
| 687 |
"id": "group_stage",
|
| 688 |
"name": "QMoE.GroupStage",
|
| 689 |
"shader": "expert-group-slots.wgsl.jinja",
|
| 690 |
-
"bindings": "
|
| 691 |
"dispatch": {
|
| 692 |
-
"
|
| 693 |
-
"
|
|
|
|
| 694 |
}
|
| 695 |
},
|
| 696 |
{
|
| 697 |
"id": "fc1_activation_stage",
|
| 698 |
"name": "QMoE.FC1ActivationStageGrouped",
|
| 699 |
"shader": "qmoe-fc1-activation-grouped.wgsl.jinja",
|
| 700 |
-
"bindings": "
|
| 701 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(interSize, groupTileN)" }
|
| 702 |
},
|
| 703 |
{
|
| 704 |
"id": "output_stage",
|
| 705 |
"name": "QMoE.OutputStageGrouped",
|
| 706 |
"shader": "qmoe-output-grouped.wgsl.jinja",
|
| 707 |
-
"bindings": "
|
| 708 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(hiddenSize, groupTileN)" }
|
| 709 |
},
|
| 710 |
{
|
| 711 |
"id": "mix_stage",
|
| 712 |
"name": "QMoE.MixStage",
|
| 713 |
"shader": "qmoe-mix-stage.wgsl.jinja",
|
| 714 |
-
"bindings": "
|
| 715 |
"dispatch": {
|
| 716 |
-
"
|
| 717 |
-
"
|
|
|
|
| 718 |
}
|
| 719 |
}
|
| 720 |
]
|
|
@@ -725,7 +341,7 @@
|
|
| 725 |
"id": "quant_gemv_routed",
|
| 726 |
"priority": 20,
|
| 727 |
"when": ["decodeContract"],
|
| 728 |
-
"
|
| 729 |
"intermediates": [
|
| 730 |
{ "id": "routeExpert", "dtype": "uint32", "shape": "[numTokens * topK]" },
|
| 731 |
{ "id": "routeMix", "dtype": "float32", "shape": "[numTokens * topK]" },
|
|
@@ -736,8 +352,12 @@
|
|
| 736 |
"id": "route_stage",
|
| 737 |
"name": "QMoE.RouteStage",
|
| 738 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 739 |
-
"bindings": "
|
| 740 |
-
"dispatch": {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 741 |
},
|
| 742 |
{
|
| 743 |
"id": "token_chunks",
|
|
@@ -747,7 +367,7 @@
|
|
| 747 |
"id": "fc1_activation_stage",
|
| 748 |
"name": "QMoE.FC1ActivationStageGemv",
|
| 749 |
"shader": "qmoe-fc1-activation-gemv.wgsl.jinja",
|
| 750 |
-
"bindings": "
|
| 751 |
"dispatch": {
|
| 752 |
"x": "ceilDiv(interSize, decodeRows)",
|
| 753 |
"y": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * topK"
|
|
@@ -757,7 +377,18 @@
|
|
| 757 |
"id": "output_stage",
|
| 758 |
"name": "QMoE.OutputStageGemv",
|
| 759 |
"shader": "qmoe-output-gemv.wgsl.jinja",
|
| 760 |
-
"bindings":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
"dispatch": {
|
| 762 |
"x": "ceilDiv(hiddenSize, decodeRows)",
|
| 763 |
"y": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
|
@@ -781,8 +412,12 @@
|
|
| 781 |
"id": "route_stage",
|
| 782 |
"name": "QMoE.RouteStage",
|
| 783 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 784 |
-
"bindings": "
|
| 785 |
-
"dispatch": {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 786 |
},
|
| 787 |
{
|
| 788 |
"id": "token_chunks",
|
|
@@ -792,20 +427,41 @@
|
|
| 792 |
"id": "fc1_activation_stage",
|
| 793 |
"name": "QMoE.FC1ActivationStage",
|
| 794 |
"shader": "qmoe-fc1-activation-stage.wgsl.jinja",
|
| 795 |
-
"bindings":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
"dispatch": {
|
| 797 |
-
"
|
| 798 |
-
"
|
|
|
|
| 799 |
}
|
| 800 |
},
|
| 801 |
{
|
| 802 |
"id": "output_stage",
|
| 803 |
"name": "QMoE.OutputStage",
|
| 804 |
"shader": "qmoe-output-stage.wgsl.jinja",
|
| 805 |
-
"bindings": "output",
|
| 806 |
"dispatch": {
|
| 807 |
-
"
|
| 808 |
-
"
|
|
|
|
| 809 |
}
|
| 810 |
}
|
| 811 |
]
|
|
|
|
| 2 |
"domain": "com.microsoft",
|
| 3 |
"name": "QMoE",
|
| 4 |
"sinceVersion": 1,
|
| 5 |
+
"inputs": {
|
| 6 |
+
"inputT": { "onnx": "input", "dtype": "T" },
|
| 7 |
+
"routerT": { "onnx": "router_probs", "dtype": "T", "rank": 2 },
|
| 8 |
+
"fc1T": { "onnx": "fc1_experts_weights", "dtype": "T1", "rank": 3 },
|
| 9 |
+
"fc1ScalesT": { "onnx": "fc1_scales", "dtype": "T2" },
|
| 10 |
+
"fc2T": { "onnx": "fc2_experts_weights", "dtype": "T1", "rank": 3 },
|
| 11 |
+
"fc2ScalesT": { "onnx": "fc2_scales", "dtype": "T2" }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
},
|
| 13 |
+
"outputs": { "outputT": { "onnx": "output", "dtype": "T", "shape": "shapes.inputT" } },
|
| 14 |
+
"attributes": {
|
| 15 |
+
"activation_alpha": { "default": 1 },
|
| 16 |
+
"activation_beta": { "default": 0 },
|
| 17 |
+
"activation_type": { "default": "relu" },
|
| 18 |
+
"expert_weight_bits": { "default": 4 },
|
| 19 |
+
"k": { "default": 1 },
|
| 20 |
+
"normalize_routing_weights": { "default": 0 },
|
| 21 |
+
"quant_type": { "default": "int" },
|
| 22 |
+
"swiglu_fusion": { "default": 0 },
|
| 23 |
+
"use_sparse_mixer": { "default": 0 },
|
| 24 |
+
"weights_prepacked": { "default": -1 },
|
| 25 |
+
"block_size": {},
|
| 26 |
+
"swiglu_limit": {}
|
| 27 |
},
|
| 28 |
"attributeConstraints": {
|
| 29 |
"activation_type": { "values": ["relu", "swiglu"] },
|
|
|
|
| 35 |
"weights_prepacked": { "values": [-1, 0] }
|
| 36 |
},
|
| 37 |
"typeConstraints": { "T": ["float32"], "T1": ["uint8"], "T2": ["float32"] },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
"tunables": {
|
| 39 |
+
"workgroupSize": { "default": 64 },
|
| 40 |
+
"decodeLanes": { "default": 32 },
|
| 41 |
+
"decodeBlockTarget": { "default": 1024 },
|
| 42 |
+
"decodeMinLaneTrips": { "default": 4 },
|
| 43 |
+
"groupThreads": { "default": 8 },
|
| 44 |
+
"groupRegM": { "default": 4 },
|
| 45 |
+
"groupRegN": { "default": 4 },
|
| 46 |
+
"groupTileK": { "default": 16 },
|
| 47 |
+
"groupRouteWorkgroup": { "default": 256 }
|
| 48 |
},
|
| 49 |
"derive": {
|
| 50 |
"deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
|
|
|
|
| 52 |
"canPinSubgroupSize32": "device.features.has(\"subgroups\") and device.features.has(\"subgroup-size-control\") and has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize <= 32 and device.adapterInfo.subgroupMaxSize >= 32",
|
| 53 |
"pinSubgroupSize32": "canPinSubgroupSize32 and not wave32Adapter",
|
| 54 |
"wave32Effective": "wave32Adapter or pinSubgroupSize32",
|
| 55 |
+
"hiddenSize": "dim(shapes.inputT, ranks.inputT - 1)",
|
| 56 |
+
"numTokens": "numel(shapes.inputT) / max(1, hiddenSize)",
|
| 57 |
"topK": "attrs.k",
|
| 58 |
"weightBits": "attrs.expert_weight_bits",
|
| 59 |
"quantBlockSize": "attrs.block_size if has(attrs, \"block_size\") else 0",
|
| 60 |
"packSize": "2 if weightBits == 4 else 1",
|
| 61 |
"quantMidpoint": "8 if weightBits == 4 else 128",
|
| 62 |
"fusionSize": "2 if attrs.activation_type == \"swiglu\" and attrs.swiglu_fusion == 1 else 1",
|
| 63 |
+
"interSize": "dim(shapes.fc1T, 1) / fusionSize",
|
| 64 |
+
"fc1PackedCols": "dim(shapes.fc1T, 2)",
|
| 65 |
+
"fc2PackedCols": "dim(shapes.fc2T, 2)",
|
| 66 |
"colWiseScales": "quantBlockSize == 0",
|
| 67 |
"fc1ScaleBlocks": "1 if colWiseScales else hiddenSize / max(1, quantBlockSize)",
|
| 68 |
"fc2ScaleBlocks": "1 if colWiseScales else interSize / max(1, quantBlockSize)",
|
| 69 |
"activationSupported": "(attrs.activation_type == \"relu\" and attrs.swiglu_fusion == 0) or (attrs.activation_type == \"swiglu\" and attrs.swiglu_fusion == 1)",
|
| 70 |
"routingModeSupported": "attrs.normalize_routing_weights == 0 or attrs.normalize_routing_weights == 1",
|
| 71 |
"rawWeightLayout": "attrs.weights_prepacked == -1 or attrs.weights_prepacked == 0",
|
| 72 |
+
"inputOutputShapeOk": "((ranks.inputT == 2 and ranks.outputT == 2 and dim(shapes.outputT, 0) == dim(shapes.inputT, 0) and dim(shapes.outputT, 1) == dim(shapes.inputT, 1)) or (ranks.inputT == 3 and ranks.outputT == 3 and dim(shapes.outputT, 0) == dim(shapes.inputT, 0) and dim(shapes.outputT, 1) == dim(shapes.inputT, 1) and dim(shapes.outputT, 2) == dim(shapes.inputT, 2))) and hiddenSize > 0",
|
| 73 |
"quantBlockSizeOk": "colWiseScales or (quantBlockSize >= 16 and pow2ceil(quantBlockSize) == quantBlockSize and hiddenSize % quantBlockSize == 0 and interSize % quantBlockSize == 0)",
|
| 74 |
+
"quantScalesOk": "tensorDtypes.fc1ScalesT == \"float32\" and tensorDtypes.fc2ScalesT == \"float32\" and dim(shapes.fc1ScalesT, 0) == dim(shapes.routerT, 1) and dim(shapes.fc2ScalesT, 0) == dim(shapes.routerT, 1) and dim(shapes.fc1ScalesT, 1) == dim(shapes.fc1T, 1) and dim(shapes.fc2ScalesT, 1) == hiddenSize and ((ranks.fc1ScalesT == 2 and ranks.fc2ScalesT == 2) if colWiseScales else (ranks.fc1ScalesT == 3 and ranks.fc2ScalesT == 3 and dim(shapes.fc1ScalesT, 2) == fc1ScaleBlocks and dim(shapes.fc2ScalesT, 2) == fc2ScaleBlocks))",
|
| 75 |
+
"quantShapeOk": "inputOutputShapeOk and ranks.routerT == 2 and ranks.fc1T == 3 and ranks.fc2T == 3 and dim(shapes.routerT, 0) == numTokens and dim(shapes.fc1T, 0) == dim(shapes.routerT, 1) and dim(shapes.fc2T, 0) == dim(shapes.routerT, 1) and dim(shapes.fc1T, 1) % fusionSize == 0 and dim(shapes.fc2T, 1) == hiddenSize and dim(shapes.fc1T, 2) * packSize == hiddenSize and dim(shapes.fc2T, 2) * packSize == interSize and quantBlockSizeOk and quantScalesOk",
|
| 76 |
+
"quantContract": "activationSupported and routingModeSupported and rawWeightLayout and quantShapeOk and topK >= 1 and topK <= dim(shapes.routerT, 1)",
|
| 77 |
"workgroupSizeOk": "tunables.workgroupSize >= 1 and tunables.workgroupSize <= deviceWorkgroupCap",
|
| 78 |
"hiddenChunkFits": "topK * interSize * 4 <= device.limits.maxStorageBufferBindingSize and topK * interSize * 4 <= device.limits.maxBufferSize",
|
| 79 |
"hiddenChunkTokens": "numTokens if interSize == 0 else min(numTokens, max(1, floor(min(device.limits.maxStorageBufferBindingSize, device.limits.maxBufferSize) / (topK * interSize * 4))))",
|
|
|
|
| 85 |
"groupTileKVec": "ceilDiv(tunables.groupTileK, 4)",
|
| 86 |
"groupThreadCount": "tunables.groupThreads * tunables.groupThreads",
|
| 87 |
"groupSlots": "hiddenChunkTokens * topK",
|
| 88 |
+
"groupMaxTiles": "ceilDiv(groupSlots, max(1, groupTileM)) + dim(shapes.routerT, 1)",
|
| 89 |
"groupSharedBytes": "(groupTileM * tunables.groupTileK + 2 * groupTileN * tunables.groupTileK + groupTileM) * 4",
|
| 90 |
"groupSlotOutBytes": "hiddenChunkTokens * topK * hiddenSize * 4",
|
| 91 |
+
"groupedDeviceOk": "groupThreadCount <= device.limits.maxComputeInvocationsPerWorkgroup and tunables.groupThreads <= device.limits.maxComputeWorkgroupSizeX and tunables.groupThreads <= device.limits.maxComputeWorkgroupSizeY and tunables.groupRouteWorkgroup <= deviceWorkgroupCap and groupSharedBytes <= device.limits.maxComputeWorkgroupStorageSize and dim(shapes.routerT, 1) * 8 <= device.limits.maxComputeWorkgroupStorageSize and tunables.groupTileK % 4 == 0",
|
| 92 |
+
"groupedDispatchOk": "groupMaxTiles <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceilDiv(interSize, max(1, groupTileN)) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceilDiv(hiddenSize, max(1, groupTileN)) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535)",
|
| 93 |
"groupSlotOutFits": "groupSlotOutBytes <= device.limits.maxStorageBufferBindingSize and groupSlotOutBytes <= device.limits.maxBufferSize",
|
| 94 |
+
"groupedShapeOk": "numTokens * topK * 4 >= groupTileM * dim(shapes.routerT, 1)",
|
| 95 |
"groupedContract": "quantContract and workgroupSizeOk and hiddenChunkFits and routedScratchFits and interSize > 0 and groupedDeviceOk and groupedDispatchOk and groupSlotOutFits and groupedShapeOk",
|
| 96 |
"sgmatWorkgroup": "128",
|
| 97 |
"sgmatSubgroups": "4",
|
| 98 |
"sgmatRowSubtiles": "2",
|
| 99 |
+
"groupedSgmatStageElements": "max(groupTileM * 32, sgmatSubgroups * 4 * 64)",
|
| 100 |
+
"groupedSgmatSharedBytes": "(groupedSgmatStageElements + 64 * 32) * 4",
|
| 101 |
+
"groupedSgmatOk": "groupedContract and groupTileM == 32 and interSize % 32 == 0 and groupedSgmatSharedBytes <= device.limits.maxComputeWorkgroupStorageSize and ceilDiv(hiddenSize, 64) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and hiddenSize % 32 == 0",
|
| 102 |
"decodeLanesOk": "tunables.decodeLanes >= 1 and tunables.decodeLanes <= tunables.workgroupSize and tunables.workgroupSize % tunables.decodeLanes == 0",
|
| 103 |
"decodeRows": "max(1, tunables.workgroupSize / max(1, tunables.decodeLanes))",
|
| 104 |
"decodeDeviceOk": "workgroupSizeOk and decodeLanesOk and tunables.decodeLanes <= device.limits.maxComputeWorkgroupSizeX and decodeRows <= device.limits.maxComputeWorkgroupSizeY and tunables.workgroupSize * 8 <= device.limits.maxComputeWorkgroupStorageSize",
|
| 105 |
+
"decodeDispatchOk": "ceilDiv(interSize, decodeRows) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and ceilDiv(hiddenSize, decodeRows) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) and hiddenChunkTokens * topK <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535)",
|
| 106 |
"decodeLaneDepth": "min(hiddenSize, interSize) / max(1, tunables.decodeLanes)",
|
| 107 |
"decodeDepthOk": "decodeLaneDepth >= tunables.decodeMinLaneTrips",
|
| 108 |
"splitFc1Blocks": "ceilDiv(numTokens * topK * interSize, max(1, tunables.workgroupSize))",
|
| 109 |
"decodeOccupancyOk": "splitFc1Blocks <= tunables.decodeBlockTarget",
|
| 110 |
+
"decodeContract": "quantContract and decodeDeviceOk and decodeDispatchOk and hiddenChunkFits and routedScratchFits and interSize > 0 and decodeDepthOk and (decodeOccupancyOk or not groupedContract)",
|
|
|
|
|
|
|
| 111 |
"tokens": "numTokens",
|
| 112 |
"hidden": "hiddenSize",
|
| 113 |
+
"experts": "dim(shapes.routerT, 1)",
|
| 114 |
+
"fc1Rows": "dim(shapes.fc1T, 1)",
|
| 115 |
"inter": "interSize",
|
|
|
|
| 116 |
"workgroupSize": "tunables.workgroupSize",
|
| 117 |
"activationType": "attrs.activation_type",
|
| 118 |
"hasSwigluLimit": "has(attrs, \"swiglu_limit\")",
|
| 119 |
+
"swigluLimit": "attrs.swiglu_limit if has(attrs, \"swiglu_limit\") else 0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
},
|
| 121 |
+
"bindings": {
|
| 122 |
+
"output": { "arg": "outputT", "buffer": "storage", "elementType": "f32" },
|
| 123 |
+
"router_probs": { "arg": "routerT", "buffer": "read-only-storage", "elementType": "f32" },
|
| 124 |
+
"route_expert": { "scratch": "routeExpert", "buffer": "storage", "elementType": "u32" },
|
| 125 |
+
"route_mix": { "scratch": "routeMix", "buffer": "storage", "elementType": "f32" },
|
| 126 |
+
"route_expert_2": {
|
| 127 |
+
"scratch": "routeExpert",
|
| 128 |
+
"name": "route_expert",
|
| 129 |
+
"buffer": "read-only-storage",
|
| 130 |
+
"elementType": "u32"
|
| 131 |
+
},
|
| 132 |
+
"slot_list": { "scratch": "slotList", "buffer": "storage", "elementType": "u32" },
|
| 133 |
+
"tile_meta": { "scratch": "tileMeta", "buffer": "storage", "elementType": "u32" },
|
| 134 |
+
"params": {
|
| 135 |
+
"buffer": "uniform",
|
| 136 |
+
"struct": [
|
| 137 |
+
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" },
|
| 138 |
+
{
|
| 139 |
+
"name": "tokenCount",
|
| 140 |
+
"type": "u32",
|
| 141 |
+
"value": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
}
|
| 143 |
+
]
|
| 144 |
+
},
|
| 145 |
+
"input": { "arg": "inputT", "buffer": "read-only-storage", "elementType": "f32" },
|
| 146 |
+
"slot_list_2": { "scratch": "slotList", "name": "slot_list", "buffer": "read-only-storage", "elementType": "u32" },
|
| 147 |
+
"tile_meta_2": { "scratch": "tileMeta", "name": "tile_meta", "buffer": "read-only-storage", "elementType": "u32" },
|
| 148 |
+
"fc1_experts_weights": { "arg": "fc1T", "buffer": "read-only-storage", "elementType": "u32" },
|
| 149 |
+
"fc1_scales": { "arg": "fc1ScalesT", "buffer": "read-only-storage", "elementType": "f32" },
|
| 150 |
+
"hidden_act": { "scratch": "hiddenAct", "buffer": "storage", "elementType": "f32" },
|
| 151 |
+
"params_2": {
|
| 152 |
+
"name": "params",
|
| 153 |
+
"buffer": "uniform",
|
| 154 |
+
"struct": [
|
| 155 |
+
{ "name": "activationAlpha", "type": "f32", "value": "attrs.activation_alpha" },
|
| 156 |
+
{ "name": "activationBeta", "type": "f32", "value": "attrs.activation_beta" },
|
| 157 |
+
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" }
|
| 158 |
+
]
|
| 159 |
+
},
|
| 160 |
+
"hidden_act_2": {
|
| 161 |
+
"scratch": "hiddenAct",
|
| 162 |
+
"name": "hidden_act",
|
| 163 |
+
"buffer": "read-only-storage",
|
| 164 |
+
"elementType": "f32"
|
| 165 |
+
},
|
| 166 |
+
"fc2_experts_weights": { "arg": "fc2T", "buffer": "read-only-storage", "elementType": "u32" },
|
| 167 |
+
"fc2_scales": { "arg": "fc2ScalesT", "buffer": "read-only-storage", "elementType": "f32" },
|
| 168 |
+
"slot_out": { "scratch": "slotOut", "buffer": "storage", "elementType": "f32" },
|
| 169 |
+
"slot_out_2": { "scratch": "slotOut", "name": "slot_out", "buffer": "read-only-storage", "elementType": "f32" },
|
| 170 |
+
"route_mix_2": { "scratch": "routeMix", "name": "route_mix", "buffer": "read-only-storage", "elementType": "f32" }
|
| 171 |
},
|
| 172 |
"variants": [
|
| 173 |
{
|
|
|
|
| 178 |
{
|
| 179 |
"id": "output_stage",
|
| 180 |
"name": "QMoE.OutputStageZeroInter",
|
| 181 |
+
"shader": "qmoe-output-zero-inter.wgsl.jinja",
|
| 182 |
+
"derive": { "outputElementCount": "numel(shapes.outputT)" },
|
| 183 |
+
"bindings": ["output"],
|
| 184 |
+
"dispatch": {
|
| 185 |
+
"x": "min(ceilDiv((numel(shapes.outputT)), (workgroupSize)), 65535)",
|
| 186 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.outputT)), (workgroupSize)), 65535)",
|
| 187 |
+
"z": 1
|
| 188 |
+
}
|
| 189 |
}
|
| 190 |
]
|
| 191 |
},
|
|
|
|
| 193 |
"id": "quant_grouped_sgmat_routed",
|
| 194 |
"priority": 32,
|
| 195 |
"when": ["groupedSgmatOk", "wave32Effective"],
|
| 196 |
+
"requires": {
|
| 197 |
+
"features": ["subgroups", "chromium-experimental-subgroup-matrix"],
|
| 198 |
+
"subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
|
|
|
|
|
|
|
|
|
|
| 199 |
},
|
| 200 |
+
"derive": { "groupRouteWorkgroup": "tunables.groupRouteWorkgroup" },
|
| 201 |
"intermediates": [
|
| 202 |
{ "id": "routeExpert", "dtype": "uint32", "shape": "[numTokens * topK]" },
|
| 203 |
{ "id": "routeMix", "dtype": "float32", "shape": "[numTokens * topK]" },
|
|
|
|
| 211 |
"id": "route_stage",
|
| 212 |
"name": "QMoE.RouteStage",
|
| 213 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 214 |
+
"bindings": ["router_probs", "route_expert", "route_mix"],
|
| 215 |
+
"dispatch": {
|
| 216 |
+
"x": "min(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 217 |
+
"y": "ceilDiv(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 218 |
+
"z": 1
|
| 219 |
+
}
|
| 220 |
},
|
| 221 |
{
|
| 222 |
"id": "token_chunks",
|
|
|
|
| 226 |
"id": "group_stage",
|
| 227 |
"name": "QMoE.GroupStage",
|
| 228 |
"shader": "expert-group-slots.wgsl.jinja",
|
| 229 |
+
"bindings": ["route_expert_2", "slot_list", "tile_meta", "params"],
|
| 230 |
"dispatch": {
|
| 231 |
+
"x": "min(ceilDiv((groupRouteWorkgroup), (groupRouteWorkgroup)), 65535)",
|
| 232 |
+
"y": "ceilDiv(ceilDiv((groupRouteWorkgroup), (groupRouteWorkgroup)), 65535)",
|
| 233 |
+
"z": 1
|
| 234 |
}
|
| 235 |
},
|
| 236 |
{
|
| 237 |
"id": "fc1_activation_stage",
|
| 238 |
"name": "QMoE.FC1ActivationStageGroupedSgmat",
|
| 239 |
"shader": "qmoe-fc1-activation-grouped-sgmat.wgsl.jinja",
|
| 240 |
+
"bindings": ["input", "slot_list_2", "tile_meta_2", "fc1_experts_weights", "fc1_scales", "hidden_act", "params_2"],
|
| 241 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(interSize * fusionSize, 64)" }
|
| 242 |
},
|
| 243 |
{
|
| 244 |
"id": "output_stage",
|
| 245 |
"name": "QMoE.OutputStageGroupedSgmat",
|
| 246 |
"shader": "qmoe-output-grouped-sgmat.wgsl.jinja",
|
| 247 |
+
"bindings": ["hidden_act_2", "slot_list_2", "tile_meta_2", "fc2_experts_weights", "fc2_scales", "slot_out"],
|
| 248 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(hiddenSize, 64)" }
|
| 249 |
},
|
| 250 |
{
|
| 251 |
"id": "mix_stage",
|
| 252 |
"name": "QMoE.MixStage",
|
| 253 |
"shader": "qmoe-mix-stage.wgsl.jinja",
|
| 254 |
+
"bindings": ["slot_out_2", "route_mix_2", "output", "params"],
|
| 255 |
"dispatch": {
|
| 256 |
+
"x": "min(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * hiddenSize), (workgroupSize)), 65535)",
|
| 257 |
+
"y": "ceilDiv(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * hiddenSize), (workgroupSize)), 65535)",
|
| 258 |
+
"z": 1
|
| 259 |
}
|
| 260 |
}
|
| 261 |
]
|
| 262 |
}
|
| 263 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
},
|
| 265 |
{
|
| 266 |
"id": "quant_grouped_routed",
|
| 267 |
"priority": 30,
|
| 268 |
"when": ["groupedContract"],
|
| 269 |
+
"derive": {
|
|
|
|
|
|
|
| 270 |
"groupTileK": "tunables.groupTileK",
|
|
|
|
| 271 |
"groupThreads": "tunables.groupThreads",
|
| 272 |
"regM": "tunables.groupRegM",
|
| 273 |
"regN": "tunables.groupRegN",
|
|
|
|
| 286 |
"id": "route_stage",
|
| 287 |
"name": "QMoE.RouteStage",
|
| 288 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 289 |
+
"bindings": ["router_probs", "route_expert", "route_mix"],
|
| 290 |
+
"dispatch": {
|
| 291 |
+
"x": "min(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 292 |
+
"y": "ceilDiv(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 293 |
+
"z": 1
|
| 294 |
+
}
|
| 295 |
},
|
| 296 |
{
|
| 297 |
"id": "token_chunks",
|
|
|
|
| 301 |
"id": "group_stage",
|
| 302 |
"name": "QMoE.GroupStage",
|
| 303 |
"shader": "expert-group-slots.wgsl.jinja",
|
| 304 |
+
"bindings": ["route_expert_2", "slot_list", "tile_meta", "params"],
|
| 305 |
"dispatch": {
|
| 306 |
+
"x": "min(ceilDiv((groupRouteWorkgroup), (groupRouteWorkgroup)), 65535)",
|
| 307 |
+
"y": "ceilDiv(ceilDiv((groupRouteWorkgroup), (groupRouteWorkgroup)), 65535)",
|
| 308 |
+
"z": 1
|
| 309 |
}
|
| 310 |
},
|
| 311 |
{
|
| 312 |
"id": "fc1_activation_stage",
|
| 313 |
"name": "QMoE.FC1ActivationStageGrouped",
|
| 314 |
"shader": "qmoe-fc1-activation-grouped.wgsl.jinja",
|
| 315 |
+
"bindings": ["input", "slot_list_2", "tile_meta_2", "fc1_experts_weights", "fc1_scales", "hidden_act", "params_2"],
|
| 316 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(interSize, groupTileN)" }
|
| 317 |
},
|
| 318 |
{
|
| 319 |
"id": "output_stage",
|
| 320 |
"name": "QMoE.OutputStageGrouped",
|
| 321 |
"shader": "qmoe-output-grouped.wgsl.jinja",
|
| 322 |
+
"bindings": ["hidden_act_2", "slot_list_2", "tile_meta_2", "fc2_experts_weights", "fc2_scales", "slot_out"],
|
| 323 |
"dispatch": { "x": "groupMaxTiles", "y": "ceilDiv(hiddenSize, groupTileN)" }
|
| 324 |
},
|
| 325 |
{
|
| 326 |
"id": "mix_stage",
|
| 327 |
"name": "QMoE.MixStage",
|
| 328 |
"shader": "qmoe-mix-stage.wgsl.jinja",
|
| 329 |
+
"bindings": ["slot_out_2", "route_mix_2", "output", "params"],
|
| 330 |
"dispatch": {
|
| 331 |
+
"x": "min(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * hiddenSize), (workgroupSize)), 65535)",
|
| 332 |
+
"y": "ceilDiv(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * hiddenSize), (workgroupSize)), 65535)",
|
| 333 |
+
"z": 1
|
| 334 |
}
|
| 335 |
}
|
| 336 |
]
|
|
|
|
| 341 |
"id": "quant_gemv_routed",
|
| 342 |
"priority": 20,
|
| 343 |
"when": ["decodeContract"],
|
| 344 |
+
"derive": { "decodeLanes": "tunables.decodeLanes" },
|
| 345 |
"intermediates": [
|
| 346 |
{ "id": "routeExpert", "dtype": "uint32", "shape": "[numTokens * topK]" },
|
| 347 |
{ "id": "routeMix", "dtype": "float32", "shape": "[numTokens * topK]" },
|
|
|
|
| 352 |
"id": "route_stage",
|
| 353 |
"name": "QMoE.RouteStage",
|
| 354 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 355 |
+
"bindings": ["router_probs", "route_expert", "route_mix"],
|
| 356 |
+
"dispatch": {
|
| 357 |
+
"x": "min(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 358 |
+
"y": "ceilDiv(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 359 |
+
"z": 1
|
| 360 |
+
}
|
| 361 |
},
|
| 362 |
{
|
| 363 |
"id": "token_chunks",
|
|
|
|
| 367 |
"id": "fc1_activation_stage",
|
| 368 |
"name": "QMoE.FC1ActivationStageGemv",
|
| 369 |
"shader": "qmoe-fc1-activation-gemv.wgsl.jinja",
|
| 370 |
+
"bindings": ["input", "route_expert_2", "fc1_experts_weights", "fc1_scales", "hidden_act", "params_2"],
|
| 371 |
"dispatch": {
|
| 372 |
"x": "ceilDiv(interSize, decodeRows)",
|
| 373 |
"y": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * topK"
|
|
|
|
| 377 |
"id": "output_stage",
|
| 378 |
"name": "QMoE.OutputStageGemv",
|
| 379 |
"shader": "qmoe-output-gemv.wgsl.jinja",
|
| 380 |
+
"bindings": [
|
| 381 |
+
"hidden_act_2",
|
| 382 |
+
"route_expert_2",
|
| 383 |
+
"route_mix_2",
|
| 384 |
+
"fc2_experts_weights",
|
| 385 |
+
"fc2_scales",
|
| 386 |
+
"output",
|
| 387 |
+
{
|
| 388 |
+
"name": "params",
|
| 389 |
+
"struct": [{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" }]
|
| 390 |
+
}
|
| 391 |
+
],
|
| 392 |
"dispatch": {
|
| 393 |
"x": "ceilDiv(hiddenSize, decodeRows)",
|
| 394 |
"y": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
|
|
|
| 412 |
"id": "route_stage",
|
| 413 |
"name": "QMoE.RouteStage",
|
| 414 |
"shader": "qmoe-route-stage.wgsl.jinja",
|
| 415 |
+
"bindings": ["router_probs", "route_expert", "route_mix"],
|
| 416 |
+
"dispatch": {
|
| 417 |
+
"x": "min(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 418 |
+
"y": "ceilDiv(ceilDiv((numTokens), (workgroupSize)), 65535)",
|
| 419 |
+
"z": 1
|
| 420 |
+
}
|
| 421 |
},
|
| 422 |
{
|
| 423 |
"id": "token_chunks",
|
|
|
|
| 427 |
"id": "fc1_activation_stage",
|
| 428 |
"name": "QMoE.FC1ActivationStage",
|
| 429 |
"shader": "qmoe-fc1-activation-stage.wgsl.jinja",
|
| 430 |
+
"bindings": [
|
| 431 |
+
"input",
|
| 432 |
+
"route_expert_2",
|
| 433 |
+
"fc1_experts_weights",
|
| 434 |
+
"fc1_scales",
|
| 435 |
+
"hidden_act",
|
| 436 |
+
{
|
| 437 |
+
"name": "params",
|
| 438 |
+
"struct": [
|
| 439 |
+
{ "name": "activationAlpha", "type": "f32", "value": "attrs.activation_alpha" },
|
| 440 |
+
{ "name": "activationBeta", "type": "f32", "value": "attrs.activation_beta" },
|
| 441 |
+
{ "name": "tokenOffset", "type": "u32", "value": "repeat.chunk * hiddenChunkTokens" },
|
| 442 |
+
{
|
| 443 |
+
"name": "tokenCount",
|
| 444 |
+
"type": "u32",
|
| 445 |
+
"value": "min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens)"
|
| 446 |
+
}
|
| 447 |
+
]
|
| 448 |
+
}
|
| 449 |
+
],
|
| 450 |
"dispatch": {
|
| 451 |
+
"x": "min(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * topK * interSize), (workgroupSize)), 65535)",
|
| 452 |
+
"y": "ceilDiv(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * topK * interSize), (workgroupSize)), 65535)",
|
| 453 |
+
"z": 1
|
| 454 |
}
|
| 455 |
},
|
| 456 |
{
|
| 457 |
"id": "output_stage",
|
| 458 |
"name": "QMoE.OutputStage",
|
| 459 |
"shader": "qmoe-output-stage.wgsl.jinja",
|
| 460 |
+
"bindings": ["hidden_act_2", "route_expert_2", "route_mix_2", "fc2_experts_weights", "fc2_scales", "output", "params"],
|
| 461 |
"dispatch": {
|
| 462 |
+
"x": "min(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * hiddenSize), (workgroupSize)), 65535)",
|
| 463 |
+
"y": "ceilDiv(ceilDiv((min(hiddenChunkTokens, numTokens - repeat.chunk * hiddenChunkTokens) * hiddenSize), (workgroupSize)), 65535)",
|
| 464 |
+
"z": 1
|
| 465 |
}
|
| 466 |
}
|
| 467 |
]
|
build/webgpu/metadata.json
CHANGED
|
@@ -1,29 +1,38 @@
|
|
| 1 |
{
|
| 2 |
"name": "com.microsoft.QMoE",
|
| 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 |
"expert-group-slots.wgsl.jinja": "Ta+3H2FA1qRRzksdkwgKLZiO+AokmuM+B9JVN8cv1EQ=",
|
| 12 |
-
"manifest.json": "
|
| 13 |
"qmoe-fc1-activation-gemv.wgsl.jinja": "UvP8Vyx1E9+/+I09w/z9iB6h6MHrSrOmjKfIelyIS0M=",
|
| 14 |
-
"qmoe-fc1-activation-grouped-sgmat.wgsl.jinja": "
|
| 15 |
-
"qmoe-fc1-activation-grouped.wgsl.jinja": "
|
| 16 |
-
"qmoe-fc1-activation-stage.wgsl.jinja": "
|
| 17 |
-
"qmoe-mix-stage.wgsl.jinja": "+
|
| 18 |
"qmoe-output-gemv.wgsl.jinja": "OJLoDDWMYE5yaTeDGY7qUeuf6AepJL7ZZLx8ko6OTEQ=",
|
| 19 |
-
"qmoe-output-grouped-sgmat.wgsl.jinja": "
|
| 20 |
-
"qmoe-output-grouped.wgsl.jinja": "
|
| 21 |
-
"qmoe-output-stage.wgsl.jinja": "
|
| 22 |
-
"qmoe-output-zero-inter.wgsl.jinja": "
|
| 23 |
-
"qmoe-route-stage.wgsl.jinja": "
|
| 24 |
-
"test.json": "
|
| 25 |
}
|
| 26 |
},
|
| 27 |
-
"provenance": { "kernel": { "sha": "
|
| 28 |
-
"webgpu": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"name": "com.microsoft.QMoE",
|
| 3 |
+
"id": "_com_microsoft_qmoe_webgpu_b3768d9",
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
+
"bench.json": "YuXSEWa4U+6Ek0E0ZkIeDrzqQ672IelokTilKJuKHf0=",
|
| 11 |
"expert-group-slots.wgsl.jinja": "Ta+3H2FA1qRRzksdkwgKLZiO+AokmuM+B9JVN8cv1EQ=",
|
| 12 |
+
"manifest.json": "r3eaOyzncaumYYrgh5XHHfvd9mHdvVPR3cl5xbiwDDA=",
|
| 13 |
"qmoe-fc1-activation-gemv.wgsl.jinja": "UvP8Vyx1E9+/+I09w/z9iB6h6MHrSrOmjKfIelyIS0M=",
|
| 14 |
+
"qmoe-fc1-activation-grouped-sgmat.wgsl.jinja": "uWNSXPm0htntxHKCNBzDIo5qFmROP9wDF3mjBGQo+Wo=",
|
| 15 |
+
"qmoe-fc1-activation-grouped.wgsl.jinja": "6NEYMHvSNgCp6KHJveI4LELrTqr8u/lxIJjSkRX68V4=",
|
| 16 |
+
"qmoe-fc1-activation-stage.wgsl.jinja": "C9DvNGQ2u4eBAAceyEdbGX13goQFJbxSunkXXhYN604=",
|
| 17 |
+
"qmoe-mix-stage.wgsl.jinja": "+xEc6boXboFbx+4AJ2/KWElM04LRuufmiIKfEK51/oo=",
|
| 18 |
"qmoe-output-gemv.wgsl.jinja": "OJLoDDWMYE5yaTeDGY7qUeuf6AepJL7ZZLx8ko6OTEQ=",
|
| 19 |
+
"qmoe-output-grouped-sgmat.wgsl.jinja": "jopTCpUPoIw9MBRhrmjQuZioTbJJIygwBvbwRyhHvUY=",
|
| 20 |
+
"qmoe-output-grouped.wgsl.jinja": "dOoFq8c0+yBCwOae+QVKBQyodJUK4vwam1RQb46oBNU=",
|
| 21 |
+
"qmoe-output-stage.wgsl.jinja": "yuSg3G05eKQOunyR5lcCEaJ5y5lXoT73RzT9rq6ahJQ=",
|
| 22 |
+
"qmoe-output-zero-inter.wgsl.jinja": "6jHYpVxU5+9WMflhwMste4v17hq7WWA1BDDS4q4DLS4=",
|
| 23 |
+
"qmoe-route-stage.wgsl.jinja": "SF5VN6d0zp/ssCGnTwCWccgbnOkZ0mptZiwIbMX3gXA=",
|
| 24 |
+
"test.json": "/ffqKBtjk2jseUZPswe8gOyYZfhNJu9aOZg8GNlB+9A="
|
| 25 |
}
|
| 26 |
},
|
| 27 |
+
"provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
|
| 28 |
+
"webgpu": {
|
| 29 |
+
"manifestSpec": "2.0",
|
| 30 |
+
"variants": {
|
| 31 |
+
"quant_zero_inter": ["qmoe-output-zero-inter.wgsl.jinja"],
|
| 32 |
+
"quant_grouped_sgmat_routed": ["expert-group-slots.wgsl.jinja", "qmoe-fc1-activation-grouped-sgmat.wgsl.jinja", "qmoe-mix-stage.wgsl.jinja", "qmoe-output-grouped-sgmat.wgsl.jinja", "qmoe-route-stage.wgsl.jinja"],
|
| 33 |
+
"quant_grouped_routed": ["expert-group-slots.wgsl.jinja", "qmoe-fc1-activation-grouped.wgsl.jinja", "qmoe-mix-stage.wgsl.jinja", "qmoe-output-grouped.wgsl.jinja", "qmoe-route-stage.wgsl.jinja"],
|
| 34 |
+
"quant_gemv_routed": ["qmoe-fc1-activation-gemv.wgsl.jinja", "qmoe-output-gemv.wgsl.jinja", "qmoe-route-stage.wgsl.jinja"],
|
| 35 |
+
"quant_split_routed": ["qmoe-fc1-activation-stage.wgsl.jinja", "qmoe-output-stage.wgsl.jinja", "qmoe-route-stage.wgsl.jinja"]
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
}
|
build/webgpu/qmoe-fc1-activation-grouped-sgmat.wgsl.jinja
CHANGED
|
@@ -5,14 +5,13 @@ enable subgroup_size_control;
|
|
| 5 |
enable chromium_experimental_subgroup_matrix;
|
| 6 |
diagnostic(off, chromium.subgroup_matrix_uniformity);
|
| 7 |
|
|
|
|
| 8 |
{{ env.wgsl.resourceDeclarations }}
|
| 9 |
|
| 10 |
-
//
|
| 11 |
-
//
|
| 12 |
-
//
|
| 13 |
-
//
|
| 14 |
-
// tile -- with the inner product on the matrix units and the packed weights dequantized
|
| 15 |
-
// straight into the shared B operand.
|
| 16 |
//
|
| 17 |
// A fused SwiGLU takes its gate from weight row 2c and its up from row 2c+1. Those are
|
| 18 |
// staged as adjacent columns of one B tile rather than as two tiles. The result columns a
|
|
@@ -21,7 +20,6 @@ const HIDDEN: u32 = {{ hidden }}u;
|
|
| 21 |
const FC1_ROWS: u32 = {{ fc1Rows }}u;
|
| 22 |
const INTER: u32 = {{ inter }}u;
|
| 23 |
const TOP_K: u32 = {{ topK }}u;
|
| 24 |
-
const TILE_ROWS: u32 = {{ groupTileM }}u;
|
| 25 |
const TILE_COLS: u32 = 64u;
|
| 26 |
const TILE_K: u32 = 32u;
|
| 27 |
const SUB_COLS: u32 = 32u;
|
|
@@ -43,7 +41,7 @@ const FC1_BLOCKS: u32 = {{ fc1ScaleBlocks }}u;
|
|
| 43 |
|
| 44 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 45 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 46 |
-
//
|
| 47 |
fn fc1_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 48 |
let base = expert * FC1_ROWS + row;
|
| 49 |
{% if packSize == 2 %}
|
|
@@ -86,16 +84,16 @@ fn activate(value: f32) -> f32 {
|
|
| 86 |
{% endif %}
|
| 87 |
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
|
|
|
| 91 |
var<workgroup> tile_B: array<f32, 64 * 32>;
|
| 92 |
-
var<workgroup> scratch: array<array<array<f32, 64>, 4>, {{ sgmatSubgroups }}>;
|
| 93 |
|
| 94 |
// Rows past the tile's end stage zero and are dropped at the store, so a short final
|
| 95 |
// tile needs no separate arm.
|
| 96 |
-
fn loadSHMA(k_idx: u32, rows: u32, row: u32, c_idx: u32) {
|
| 97 |
let col = c_idx * 8u;
|
| 98 |
-
let base = (params.tokenOffset +
|
| 99 |
if (row < rows) {
|
| 100 |
for (var i = 0u; i < 8u; i = i + 1u) {
|
| 101 |
tile_A[row * TILE_K + col + i] = f32(input[base + i]);
|
|
@@ -143,10 +141,7 @@ fn main(
|
|
| 143 |
let rows = tile_meta[1u + tile * 3u + 2u];
|
| 144 |
let weight_base = wid.y * TILE_COLS;
|
| 145 |
|
| 146 |
-
|
| 147 |
-
row_slot[m] = select(0u, slot_list[slice_base + m], m < rows);
|
| 148 |
-
}
|
| 149 |
-
workgroupBarrier();
|
| 150 |
|
| 151 |
let subtile_id = local_idx / sg_size;
|
| 152 |
let subtile_idx = subtile_id / {{ sgmatRowSubtiles }}u;
|
|
@@ -159,20 +154,21 @@ fn main(
|
|
| 159 |
{% endfor %}{% endfor %}
|
| 160 |
|
| 161 |
for (var kidx = 0u; kidx < HIDDEN; kidx = kidx + TILE_K) {
|
| 162 |
-
loadSHMA(kidx, rows, local_idx / 4u, local_idx % 4u);
|
| 163 |
loadSHMB(expert, weight_base, kidx, local_idx / 2u, local_idx % 2u);
|
| 164 |
workgroupBarrier();
|
| 165 |
|
| 166 |
for (var step = 0u; step < TILE_K; step = step + 8u) {
|
| 167 |
-
{% set operandScalar = "f32" %}
|
| 168 |
-
|
| 169 |
-
|
|
|
|
|
|
|
| 170 |
|
| 171 |
let matrix_b_offset = subtile_idx * SUB_COLS * TILE_K + step;
|
| 172 |
-
|
| 173 |
-
var
|
| 174 |
-
|
| 175 |
-
var matB3: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 24u * TILE_K, TILE_K);
|
| 176 |
|
| 177 |
matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
|
| 178 |
matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
|
|
@@ -197,20 +193,20 @@ fn main(
|
|
| 197 |
// The scratch banks are shared by both row groups, so the readback is fenced on both
|
| 198 |
// sides of each collective store.
|
| 199 |
{% for c in [0, 1, 2, 3] %}
|
| 200 |
-
subgroupMatrixStore<row_major>(&
|
| 201 |
{% endfor %}
|
| 202 |
workgroupBarrier();
|
| 203 |
{
|
| 204 |
let m = base_A + {{ 8 * group }}u + row;
|
| 205 |
if (m < rows) {
|
| 206 |
-
let out_base =
|
| 207 |
{% for c in [0, 1, 2, 3] %}
|
| 208 |
{% if isSwiglu %}
|
| 209 |
{
|
| 210 |
let out_col = (out_weight_base + col + {{ 8 * c }}u) / 2u;
|
| 211 |
if (out_col < INTER) {
|
| 212 |
-
hidden_act[out_base + out_col] = activate(
|
| 213 |
-
|
| 214 |
}
|
| 215 |
}
|
| 216 |
{% else %}
|
|
@@ -218,7 +214,7 @@ fn main(
|
|
| 218 |
{
|
| 219 |
let out_col = out_weight_base + {{ half }} + {{ 8 * c }}u;
|
| 220 |
if (out_col < INTER) {
|
| 221 |
-
hidden_act[out_base + out_col] = activate(
|
| 222 |
}
|
| 223 |
}
|
| 224 |
{% endfor %}
|
|
|
|
| 5 |
enable chromium_experimental_subgroup_matrix;
|
| 6 |
diagnostic(off, chromium.subgroup_matrix_uniformity);
|
| 7 |
|
| 8 |
+
|
| 9 |
{{ env.wgsl.resourceDeclarations }}
|
| 10 |
|
| 11 |
+
// Expert-grouped FC1 and activation stage using subgroup-matrix operations.
|
| 12 |
+
// Routed slots arrive sorted by expert, so each tile shares one expert and
|
| 13 |
+
// reuses its dequantized weight tile across rows. Packed weights are
|
| 14 |
+
// dequantized directly into the shared B operand.
|
|
|
|
|
|
|
| 15 |
//
|
| 16 |
// A fused SwiGLU takes its gate from weight row 2c and its up from row 2c+1. Those are
|
| 17 |
// staged as adjacent columns of one B tile rather than as two tiles. The result columns a
|
|
|
|
| 20 |
const FC1_ROWS: u32 = {{ fc1Rows }}u;
|
| 21 |
const INTER: u32 = {{ inter }}u;
|
| 22 |
const TOP_K: u32 = {{ topK }}u;
|
|
|
|
| 23 |
const TILE_COLS: u32 = 64u;
|
| 24 |
const TILE_K: u32 = 32u;
|
| 25 |
const SUB_COLS: u32 = 32u;
|
|
|
|
| 41 |
|
| 42 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 43 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 44 |
+
// this helper requires `col` to be a multiple of four.
|
| 45 |
fn fc1_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 46 |
let base = expert * FC1_ROWS + row;
|
| 47 |
{% if packSize == 2 %}
|
|
|
|
| 84 |
{% endif %}
|
| 85 |
|
| 86 |
|
| 87 |
+
// The final K-loop barrier ends the A operand's lifetime. Its storage then
|
| 88 |
+
// holds four 8x8 output banks per subgroup, fenced by the epilogue barriers.
|
| 89 |
+
var<workgroup> tile_A: array<f32, {{ groupedSgmatStageElements }}>;
|
| 90 |
var<workgroup> tile_B: array<f32, 64 * 32>;
|
|
|
|
| 91 |
|
| 92 |
// Rows past the tile's end stage zero and are dropped at the store, so a short final
|
| 93 |
// tile needs no separate arm.
|
| 94 |
+
fn loadSHMA(k_idx: u32, rows: u32, row: u32, c_idx: u32, a_slot: u32) {
|
| 95 |
let col = c_idx * 8u;
|
| 96 |
+
let base = (params.tokenOffset + a_slot / TOP_K) * HIDDEN + k_idx + col;
|
| 97 |
if (row < rows) {
|
| 98 |
for (var i = 0u; i < 8u; i = i + 1u) {
|
| 99 |
tile_A[row * TILE_K + col + i] = f32(input[base + i]);
|
|
|
|
| 141 |
let rows = tile_meta[1u + tile * 3u + 2u];
|
| 142 |
let weight_base = wid.y * TILE_COLS;
|
| 143 |
|
| 144 |
+
let a_slot = select(0u, slot_list[slice_base + local_idx / 4u], local_idx / 4u < rows);
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
let subtile_id = local_idx / sg_size;
|
| 147 |
let subtile_idx = subtile_id / {{ sgmatRowSubtiles }}u;
|
|
|
|
| 154 |
{% endfor %}{% endfor %}
|
| 155 |
|
| 156 |
for (var kidx = 0u; kidx < HIDDEN; kidx = kidx + TILE_K) {
|
| 157 |
+
loadSHMA(kidx, rows, local_idx / 4u, local_idx % 4u, a_slot);
|
| 158 |
loadSHMB(expert, weight_base, kidx, local_idx / 2u, local_idx % 2u);
|
| 159 |
workgroupBarrier();
|
| 160 |
|
| 161 |
for (var step = 0u; step < TILE_K; step = step + 8u) {
|
| 162 |
+
{% set operandScalar = "f32" %}{% set directInputs = directMatrixInputs is defined and directMatrixInputs %}
|
| 163 |
+
let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + step;
|
| 164 |
+
{% for r in range(2) %}
|
| 165 |
+
var matA{{ r }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&{{ "w" if directInputs else "tile_A" }}, matrix_a_offset{% if r > 0 %} + 8u * {{ "K" if directInputs else "TILE_K" }}{% endif %}, {{ "K" if directInputs else "TILE_K" }});
|
| 166 |
+
{% endfor %}
|
| 167 |
|
| 168 |
let matrix_b_offset = subtile_idx * SUB_COLS * TILE_K + step;
|
| 169 |
+
{% for c in range(4) %}
|
| 170 |
+
var matB{{ c }}: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&{{ "xm" if directInputs else "tile_B" }}, matrix_b_offset{% if c > 0 %} + {{ c * 8 }}u * TILE_K{% endif %}, {{ "N" if directInputs else "TILE_K" }});
|
| 171 |
+
{% endfor %}
|
|
|
|
| 172 |
|
| 173 |
matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
|
| 174 |
matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
|
|
|
|
| 193 |
// The scratch banks are shared by both row groups, so the readback is fenced on both
|
| 194 |
// sides of each collective store.
|
| 195 |
{% for c in [0, 1, 2, 3] %}
|
| 196 |
+
subgroupMatrixStore<row_major>(&tile_A, (subtile_id * 4u + {{ c }}u) * 64u, matC{{ group }}{{ c }}, 8u);
|
| 197 |
{% endfor %}
|
| 198 |
workgroupBarrier();
|
| 199 |
{
|
| 200 |
let m = base_A + {{ 8 * group }}u + row;
|
| 201 |
if (m < rows) {
|
| 202 |
+
let out_base = slot_list[slice_base + m] * INTER;
|
| 203 |
{% for c in [0, 1, 2, 3] %}
|
| 204 |
{% if isSwiglu %}
|
| 205 |
{
|
| 206 |
let out_col = (out_weight_base + col + {{ 8 * c }}u) / 2u;
|
| 207 |
if (out_col < INTER) {
|
| 208 |
+
hidden_act[out_base + out_col] = activate(tile_A[(subtile_id * 4u + {{ c }}u) * 64u + row * 8u + col],
|
| 209 |
+
tile_A[(subtile_id * 4u + {{ c }}u) * 64u + row * 8u + col2]);
|
| 210 |
}
|
| 211 |
}
|
| 212 |
{% else %}
|
|
|
|
| 214 |
{
|
| 215 |
let out_col = out_weight_base + {{ half }} + {{ 8 * c }}u;
|
| 216 |
if (out_col < INTER) {
|
| 217 |
+
hidden_act[out_base + out_col] = activate(tile_A[(subtile_id * 4u + {{ c }}u) * 64u + row * 8u + {{ half }}]);
|
| 218 |
}
|
| 219 |
}
|
| 220 |
{% endfor %}
|
build/webgpu/qmoe-fc1-activation-grouped.wgsl.jinja
CHANGED
|
@@ -43,7 +43,7 @@ fn fc1_weight(expert: u32, row: u32, col: u32) -> f32 {
|
|
| 43 |
}
|
| 44 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 45 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 46 |
-
//
|
| 47 |
fn fc1_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 48 |
let base = expert * FC1_ROWS + row;
|
| 49 |
{% if packSize == 2 %}
|
|
|
|
| 43 |
}
|
| 44 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 45 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 46 |
+
// this helper requires `col` to be a multiple of four.
|
| 47 |
fn fc1_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 48 |
let base = expert * FC1_ROWS + row;
|
| 49 |
{% if packSize == 2 %}
|
build/webgpu/qmoe-fc1-activation-stage.wgsl.jinja
CHANGED
|
@@ -60,11 +60,10 @@ fn activate(value: f32) -> f32 {
|
|
| 60 |
|
| 61 |
|
| 62 |
@compute @workgroup_size(WG, 1, 1)
|
| 63 |
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>
|
| 64 |
-
|
| 65 |
-
//
|
| 66 |
-
|
| 67 |
-
let index = gid.x + gid.y * nwg.x * WG;
|
| 68 |
{% if topK > 1 %}
|
| 69 |
// One activation row per routed slot rather than per token: the grid is
|
| 70 |
// (token, slot, inter) and hidden_act is addressed by the same flat index.
|
|
|
|
| 60 |
|
| 61 |
|
| 62 |
@compute @workgroup_size(WG, 1, 1)
|
| 63 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 64 |
+
// 2D-folded flat index: gid.y carries the high bits past the per-axis dispatch fold width.
|
| 65 |
+
// Reduces to gid.x when the dispatch does not fold.
|
| 66 |
+
let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
|
|
|
|
| 67 |
{% if topK > 1 %}
|
| 68 |
// One activation row per routed slot rather than per token: the grid is
|
| 69 |
// (token, slot, inter) and hidden_act is addressed by the same flat index.
|
build/webgpu/qmoe-mix-stage.wgsl.jinja
CHANGED
|
@@ -10,11 +10,10 @@ const TOP_K: u32 = {{ topK }}u;
|
|
| 10 |
const WG: u32 = {{ workgroupSize }}u;
|
| 11 |
|
| 12 |
@compute @workgroup_size(WG, 1, 1)
|
| 13 |
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>
|
| 14 |
-
|
| 15 |
-
//
|
| 16 |
-
|
| 17 |
-
let index = gid.x + gid.y * nwg.x * WG;
|
| 18 |
let total = params.tokenCount * HIDDEN;
|
| 19 |
if (index >= total) {
|
| 20 |
return;
|
|
|
|
| 10 |
const WG: u32 = {{ workgroupSize }}u;
|
| 11 |
|
| 12 |
@compute @workgroup_size(WG, 1, 1)
|
| 13 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 14 |
+
// 2D-folded flat index: gid.y carries the high bits past the per-axis dispatch fold width.
|
| 15 |
+
// Reduces to gid.x when the dispatch does not fold.
|
| 16 |
+
let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
|
|
|
|
| 17 |
let total = params.tokenCount * HIDDEN;
|
| 18 |
if (index >= total) {
|
| 19 |
return;
|
build/webgpu/qmoe-output-grouped-sgmat.wgsl.jinja
CHANGED
|
@@ -5,22 +5,18 @@ enable subgroup_size_control;
|
|
| 5 |
enable chromium_experimental_subgroup_matrix;
|
| 6 |
diagnostic(off, chromium.subgroup_matrix_uniformity);
|
| 7 |
|
|
|
|
| 8 |
{{ env.wgsl.resourceDeclarations }}
|
| 9 |
|
| 10 |
-
//
|
| 11 |
-
//
|
| 12 |
-
//
|
| 13 |
-
//
|
| 14 |
-
// and reused across the tile's rows -- but the inner product runs on the matrix units.
|
| 15 |
-
// The packed weights are dequantized straight into the shared B tile, so no float weight
|
| 16 |
-
// matrix is ever materialized, and the accumulate stays f32, which is exact for a 4-bit
|
| 17 |
-
// code times its scale.
|
| 18 |
//
|
| 19 |
// The M axis is the tile's routed-slot slice rather than a contiguous row range, so the
|
| 20 |
// A loader gathers through `slot_list` and the store scatters back through it.
|
| 21 |
const HIDDEN: u32 = {{ hidden }}u;
|
| 22 |
const INTER: u32 = {{ inter }}u;
|
| 23 |
-
const TILE_ROWS: u32 = {{ groupTileM }}u;
|
| 24 |
const TILE_COLS: u32 = 64u;
|
| 25 |
const TILE_K: u32 = 32u;
|
| 26 |
const SUB_COLS: u32 = 32u;
|
|
@@ -39,7 +35,7 @@ const FC2_BLOCKS: u32 = {{ fc2ScaleBlocks }}u;
|
|
| 39 |
|
| 40 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 41 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 42 |
-
//
|
| 43 |
fn fc2_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 44 |
let base = expert * HIDDEN + row;
|
| 45 |
{% if packSize == 2 %}
|
|
@@ -62,16 +58,16 @@ fn fc2_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
|
| 62 |
}
|
| 63 |
|
| 64 |
|
| 65 |
-
|
| 66 |
-
|
|
|
|
| 67 |
var<workgroup> tile_B: array<f32, 64 * 32>;
|
| 68 |
-
var<workgroup> scratch: array<array<array<f32, 64>, 4>, {{ sgmatSubgroups }}>;
|
| 69 |
|
| 70 |
// Rows past the tile's end stage zero and are dropped at the store, so a short final
|
| 71 |
// tile needs no separate arm.
|
| 72 |
-
fn loadSHMA(k_idx: u32, rows: u32, row: u32, c_idx: u32) {
|
| 73 |
let col = c_idx * 8u;
|
| 74 |
-
let base =
|
| 75 |
if (row < rows) {
|
| 76 |
for (var i = 0u; i < 8u; i = i + 1u) {
|
| 77 |
tile_A[row * TILE_K + col + i] = hidden_act[base + i];
|
|
@@ -105,15 +101,15 @@ fn loadSHMB(expert: u32, col_base: u32, k_idx: u32, row: u32, c_idx: u32) {
|
|
| 105 |
fn storeCol(col_base: u32, row: u32, cc: u32, slot: u32, src_slot: u32, out_slot: u32) {
|
| 106 |
let c = cc + slot * 8u;
|
| 107 |
if (col_base + c < HIDDEN) {
|
| 108 |
-
slot_out[out_slot * HIDDEN + col_base + c] =
|
| 109 |
}
|
| 110 |
}
|
| 111 |
|
| 112 |
-
fn storeOutput(col_base: u32, row: u32, col: u32, src_slot: u32, row_base: u32, rows: u32) {
|
| 113 |
let m = row_base + row;
|
| 114 |
if (m < rows) {
|
| 115 |
let col2 = col + 1u;
|
| 116 |
-
let out_slot =
|
| 117 |
{% for blockCol in [0, 1, 2, 3] %}
|
| 118 |
storeCol(col_base, row, col, {{ blockCol }}u, src_slot, out_slot);
|
| 119 |
storeCol(col_base, row, col2, {{ blockCol }}u, src_slot, out_slot);
|
|
@@ -137,10 +133,7 @@ fn main(
|
|
| 137 |
let rows = tile_meta[1u + tile * 3u + 2u];
|
| 138 |
let col_base = wid.y * TILE_COLS;
|
| 139 |
|
| 140 |
-
|
| 141 |
-
row_slot[m] = select(0u, slot_list[slice_base + m], m < rows);
|
| 142 |
-
}
|
| 143 |
-
workgroupBarrier();
|
| 144 |
|
| 145 |
let subtile_id = local_idx / sg_size;
|
| 146 |
let subtile_idx = subtile_id / {{ sgmatRowSubtiles }}u;
|
|
@@ -153,20 +146,21 @@ fn main(
|
|
| 153 |
{% endfor %}{% endfor %}
|
| 154 |
|
| 155 |
for (var kidx = 0u; kidx < INTER; kidx = kidx + TILE_K) {
|
| 156 |
-
loadSHMA(kidx, rows, local_idx / 4u, local_idx % 4u);
|
| 157 |
loadSHMB(expert, col_base, kidx, local_idx / 2u, local_idx % 2u);
|
| 158 |
workgroupBarrier();
|
| 159 |
|
| 160 |
for (var step = 0u; step < TILE_K; step = step + 8u) {
|
| 161 |
-
{% set operandScalar = "f32" %}
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
| 164 |
|
| 165 |
let matrix_b_offset = subtile_idx * SUB_COLS * TILE_K + step;
|
| 166 |
-
|
| 167 |
-
var
|
| 168 |
-
|
| 169 |
-
var matB3: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&tile_B, matrix_b_offset + 24u * TILE_K, TILE_K);
|
| 170 |
|
| 171 |
matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
|
| 172 |
matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
|
|
@@ -183,17 +177,17 @@ fn main(
|
|
| 183 |
// Each scratch bank is written by a collective store and then read across lanes, and
|
| 184 |
// the two row groups reuse the same banks, so both barriers are load-bearing.
|
| 185 |
{% for col in [0, 1, 2, 3] %}
|
| 186 |
-
subgroupMatrixStore<row_major>(&
|
| 187 |
{% endfor %}
|
| 188 |
workgroupBarrier();
|
| 189 |
let row = sg_id / 4u;
|
| 190 |
let col = (sg_id % 4u) * 2u;
|
| 191 |
-
storeOutput(col_base + base_B, row, col, subtile_id, base_A, rows);
|
| 192 |
workgroupBarrier();
|
| 193 |
|
| 194 |
{% for col in [0, 1, 2, 3] %}
|
| 195 |
-
subgroupMatrixStore<row_major>(&
|
| 196 |
{% endfor %}
|
| 197 |
workgroupBarrier();
|
| 198 |
-
storeOutput(col_base + base_B, row, col, subtile_id, base_A + 8u, rows);
|
| 199 |
}
|
|
|
|
| 5 |
enable chromium_experimental_subgroup_matrix;
|
| 6 |
diagnostic(off, chromium.subgroup_matrix_uniformity);
|
| 7 |
|
| 8 |
+
|
| 9 |
{{ env.wgsl.resourceDeclarations }}
|
| 10 |
|
| 11 |
+
// Expert-grouped FC2 stage using subgroup-matrix operations. Routed slots are
|
| 12 |
+
// sorted by expert, so each tile shares one expert and reuses its dequantized
|
| 13 |
+
// weight tile across rows. Packed weights are dequantized directly into the
|
| 14 |
+
// shared B tile, and accumulation remains in f32.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
//
|
| 16 |
// The M axis is the tile's routed-slot slice rather than a contiguous row range, so the
|
| 17 |
// A loader gathers through `slot_list` and the store scatters back through it.
|
| 18 |
const HIDDEN: u32 = {{ hidden }}u;
|
| 19 |
const INTER: u32 = {{ inter }}u;
|
|
|
|
| 20 |
const TILE_COLS: u32 = 64u;
|
| 21 |
const TILE_K: u32 = 32u;
|
| 22 |
const SUB_COLS: u32 = 32u;
|
|
|
|
| 35 |
|
| 36 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 37 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 38 |
+
// this helper requires `col` to be a multiple of four.
|
| 39 |
fn fc2_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 40 |
let base = expert * HIDDEN + row;
|
| 41 |
{% if packSize == 2 %}
|
|
|
|
| 58 |
}
|
| 59 |
|
| 60 |
|
| 61 |
+
// The final K-loop barrier ends the A operand's lifetime. Its storage then
|
| 62 |
+
// holds four 8x8 output banks per subgroup, fenced by the epilogue barriers.
|
| 63 |
+
var<workgroup> tile_A: array<f32, {{ groupedSgmatStageElements }}>;
|
| 64 |
var<workgroup> tile_B: array<f32, 64 * 32>;
|
|
|
|
| 65 |
|
| 66 |
// Rows past the tile's end stage zero and are dropped at the store, so a short final
|
| 67 |
// tile needs no separate arm.
|
| 68 |
+
fn loadSHMA(k_idx: u32, rows: u32, row: u32, c_idx: u32, a_slot: u32) {
|
| 69 |
let col = c_idx * 8u;
|
| 70 |
+
let base = a_slot * INTER + k_idx + col;
|
| 71 |
if (row < rows) {
|
| 72 |
for (var i = 0u; i < 8u; i = i + 1u) {
|
| 73 |
tile_A[row * TILE_K + col + i] = hidden_act[base + i];
|
|
|
|
| 101 |
fn storeCol(col_base: u32, row: u32, cc: u32, slot: u32, src_slot: u32, out_slot: u32) {
|
| 102 |
let c = cc + slot * 8u;
|
| 103 |
if (col_base + c < HIDDEN) {
|
| 104 |
+
slot_out[out_slot * HIDDEN + col_base + c] = tile_A[(src_slot * 4u + slot) * 64u + row * 8u + cc];
|
| 105 |
}
|
| 106 |
}
|
| 107 |
|
| 108 |
+
fn storeOutput(col_base: u32, row: u32, col: u32, src_slot: u32, row_base: u32, rows: u32, slice_base: u32) {
|
| 109 |
let m = row_base + row;
|
| 110 |
if (m < rows) {
|
| 111 |
let col2 = col + 1u;
|
| 112 |
+
let out_slot = slot_list[slice_base + m];
|
| 113 |
{% for blockCol in [0, 1, 2, 3] %}
|
| 114 |
storeCol(col_base, row, col, {{ blockCol }}u, src_slot, out_slot);
|
| 115 |
storeCol(col_base, row, col2, {{ blockCol }}u, src_slot, out_slot);
|
|
|
|
| 133 |
let rows = tile_meta[1u + tile * 3u + 2u];
|
| 134 |
let col_base = wid.y * TILE_COLS;
|
| 135 |
|
| 136 |
+
let a_slot = select(0u, slot_list[slice_base + local_idx / 4u], local_idx / 4u < rows);
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
let subtile_id = local_idx / sg_size;
|
| 139 |
let subtile_idx = subtile_id / {{ sgmatRowSubtiles }}u;
|
|
|
|
| 146 |
{% endfor %}{% endfor %}
|
| 147 |
|
| 148 |
for (var kidx = 0u; kidx < INTER; kidx = kidx + TILE_K) {
|
| 149 |
+
loadSHMA(kidx, rows, local_idx / 4u, local_idx % 4u, a_slot);
|
| 150 |
loadSHMB(expert, col_base, kidx, local_idx / 2u, local_idx % 2u);
|
| 151 |
workgroupBarrier();
|
| 152 |
|
| 153 |
for (var step = 0u; step < TILE_K; step = step + 8u) {
|
| 154 |
+
{% set operandScalar = "f32" %}{% set directInputs = directMatrixInputs is defined and directMatrixInputs %}
|
| 155 |
+
let matrix_a_offset = subtile_idy * SUB_ROWS * TILE_K + step;
|
| 156 |
+
{% for r in range(2) %}
|
| 157 |
+
var matA{{ r }}: subgroup_matrix_left<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_left<{{ operandScalar }}, 8, 8>, row_major>(&{{ "w" if directInputs else "tile_A" }}, matrix_a_offset{% if r > 0 %} + 8u * {{ "K" if directInputs else "TILE_K" }}{% endif %}, {{ "K" if directInputs else "TILE_K" }});
|
| 158 |
+
{% endfor %}
|
| 159 |
|
| 160 |
let matrix_b_offset = subtile_idx * SUB_COLS * TILE_K + step;
|
| 161 |
+
{% for c in range(4) %}
|
| 162 |
+
var matB{{ c }}: subgroup_matrix_right<{{ operandScalar }}, 8, 8> = subgroupMatrixLoad<subgroup_matrix_right<{{ operandScalar }}, 8, 8>, col_major>(&{{ "xm" if directInputs else "tile_B" }}, matrix_b_offset{% if c > 0 %} + {{ c * 8 }}u * TILE_K{% endif %}, {{ "N" if directInputs else "TILE_K" }});
|
| 163 |
+
{% endfor %}
|
|
|
|
| 164 |
|
| 165 |
matC00 = subgroupMatrixMultiplyAccumulate(matA0, matB0, matC00);
|
| 166 |
matC01 = subgroupMatrixMultiplyAccumulate(matA0, matB1, matC01);
|
|
|
|
| 177 |
// Each scratch bank is written by a collective store and then read across lanes, and
|
| 178 |
// the two row groups reuse the same banks, so both barriers are load-bearing.
|
| 179 |
{% for col in [0, 1, 2, 3] %}
|
| 180 |
+
subgroupMatrixStore<row_major>(&tile_A, (subtile_id * 4u + {{ col }}u) * 64u, matC0{{ col }}, 8u);
|
| 181 |
{% endfor %}
|
| 182 |
workgroupBarrier();
|
| 183 |
let row = sg_id / 4u;
|
| 184 |
let col = (sg_id % 4u) * 2u;
|
| 185 |
+
storeOutput(col_base + base_B, row, col, subtile_id, base_A, rows, slice_base);
|
| 186 |
workgroupBarrier();
|
| 187 |
|
| 188 |
{% for col in [0, 1, 2, 3] %}
|
| 189 |
+
subgroupMatrixStore<row_major>(&tile_A, (subtile_id * 4u + {{ col }}u) * 64u, matC1{{ col }}, 8u);
|
| 190 |
{% endfor %}
|
| 191 |
workgroupBarrier();
|
| 192 |
+
storeOutput(col_base + base_B, row, col, subtile_id, base_A + 8u, rows, slice_base);
|
| 193 |
}
|
build/webgpu/qmoe-output-grouped.wgsl.jinja
CHANGED
|
@@ -41,7 +41,7 @@ fn fc2_weight(expert: u32, row: u32, col: u32) -> f32 {
|
|
| 41 |
}
|
| 42 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 43 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 44 |
-
//
|
| 45 |
fn fc2_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 46 |
let base = expert * HIDDEN + row;
|
| 47 |
{% if packSize == 2 %}
|
|
|
|
| 41 |
}
|
| 42 |
// Decode the four-code group consumed by the staging loop. The aligned codes
|
| 43 |
// share one quantization scale: blocks are powers of two of at least 16, and
|
| 44 |
+
// this helper requires `col` to be a multiple of four.
|
| 45 |
fn fc2_weight_vec4(expert: u32, row: u32, col: u32) -> vec4<f32> {
|
| 46 |
let base = expert * HIDDEN + row;
|
| 47 |
{% if packSize == 2 %}
|
build/webgpu/qmoe-output-stage.wgsl.jinja
CHANGED
|
@@ -35,11 +35,10 @@ fn fc2_weight(expert: u32, row: u32, col: u32) -> f32 {
|
|
| 35 |
|
| 36 |
|
| 37 |
@compute @workgroup_size(WG, 1, 1)
|
| 38 |
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>
|
| 39 |
-
|
| 40 |
-
//
|
| 41 |
-
|
| 42 |
-
let index = gid.x + gid.y * nwg.x * WG;
|
| 43 |
let total = params.tokenCount * HIDDEN;
|
| 44 |
if (index >= total) {
|
| 45 |
return;
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
@compute @workgroup_size(WG, 1, 1)
|
| 38 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 39 |
+
// 2D-folded flat index: gid.y carries the high bits past the per-axis dispatch fold width.
|
| 40 |
+
// Reduces to gid.x when the dispatch does not fold.
|
| 41 |
+
let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
|
|
|
|
| 42 |
let total = params.tokenCount * HIDDEN;
|
| 43 |
if (index >= total) {
|
| 44 |
return;
|
build/webgpu/qmoe-output-zero-inter.wgsl.jinja
CHANGED
|
@@ -3,10 +3,9 @@
|
|
| 3 |
const WG: u32 = {{ workgroupSize }}u;
|
| 4 |
|
| 5 |
@compute @workgroup_size(WG, 1, 1)
|
| 6 |
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
if (index < {{ source.outputElementCount }}u) {
|
| 10 |
output[index] = 0.0;
|
| 11 |
}
|
| 12 |
}
|
|
|
|
| 3 |
const WG: u32 = {{ workgroupSize }}u;
|
| 4 |
|
| 5 |
@compute @workgroup_size(WG, 1, 1)
|
| 6 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 7 |
+
let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
|
| 8 |
+
if (index < {{ outputElementCount }}u) {
|
|
|
|
| 9 |
output[index] = 0.0;
|
| 10 |
}
|
| 11 |
}
|
build/webgpu/qmoe-route-stage.wgsl.jinja
CHANGED
|
@@ -79,11 +79,10 @@ fn top1_route(token: u32) -> Top1Route {
|
|
| 79 |
{% endif %}
|
| 80 |
|
| 81 |
@compute @workgroup_size(WG, 1, 1)
|
| 82 |
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>
|
| 83 |
-
|
| 84 |
-
//
|
| 85 |
-
|
| 86 |
-
let token = gid.x + gid.y * nwg.x * WG;
|
| 87 |
if (token >= TOKENS) {
|
| 88 |
return;
|
| 89 |
}
|
|
|
|
| 79 |
{% endif %}
|
| 80 |
|
| 81 |
@compute @workgroup_size(WG, 1, 1)
|
| 82 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 83 |
+
// 2D-folded flat index: gid.y carries the high bits past the per-axis dispatch fold width.
|
| 84 |
+
// Reduces to gid.x when the dispatch does not fold.
|
| 85 |
+
let token = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
|
|
|
|
| 86 |
if (token >= TOKENS) {
|
| 87 |
return;
|
| 88 |
}
|
build/webgpu/test.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
| 1 |
{
|
| 2 |
-
"op": "com.microsoft.QMoE",
|
| 3 |
"cases": [
|
| 4 |
{
|
| 5 |
"name": "q4_colwise_default_relu",
|
| 6 |
"provenance": {
|
| 7 |
-
"notes": "Exercises every exact default at once: 4-bit integer weights, k=1, ReLU, column-wise scales, dense routing, and raw
|
| 8 |
},
|
| 9 |
"inputs": {
|
| 10 |
"inputT": {
|
|
@@ -39,7 +38,7 @@
|
|
| 39 |
{
|
| 40 |
"name": "q4_top1_tie_selects_higher_expert",
|
| 41 |
"provenance": {
|
| 42 |
-
"notes": "
|
| 43 |
},
|
| 44 |
"attrs": { "normalize_routing_weights": 1 },
|
| 45 |
"inputs": {
|
|
@@ -227,28 +226,16 @@
|
|
| 227 |
{
|
| 228 |
"name": "q4_zero_inter",
|
| 229 |
"provenance": {
|
| 230 |
-
"notes": "A zero intermediate dimension has an exact zero result without binding empty weight buffers in a shader."
|
| 231 |
},
|
| 232 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1 },
|
| 233 |
"inputs": {
|
| 234 |
-
"inputT": {
|
| 235 |
-
|
| 236 |
-
"shape": [3, 4],
|
| 237 |
-
"data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.3 }
|
| 238 |
-
},
|
| 239 |
-
"routerT": {
|
| 240 |
-
"dtype": "float32",
|
| 241 |
-
"shape": [3, 3],
|
| 242 |
-
"data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.19, "scale": 0.7 }
|
| 243 |
-
},
|
| 244 |
"fc1T": { "dtype": "uint8", "shape": [3, 0, 2], "data": { "kind": "values", "values": [] } },
|
| 245 |
"fc1ScalesT": { "dtype": "float32", "shape": [3, 0], "data": { "kind": "values", "values": [] } },
|
| 246 |
"fc2T": { "dtype": "uint8", "shape": [3, 4, 0], "data": { "kind": "values", "values": [] } },
|
| 247 |
-
"fc2ScalesT": {
|
| 248 |
-
"dtype": "float32",
|
| 249 |
-
"shape": [3, 4],
|
| 250 |
-
"data": { "kind": "linspace", "start": 0.008, "end": 0.037 }
|
| 251 |
-
}
|
| 252 |
},
|
| 253 |
"outputs": {
|
| 254 |
"outputT": { "dtype": "float32", "shape": [3, 4], "data": { "kind": "constant", "value": 0.0 }, "tolerance": 0 }
|
|
@@ -274,7 +261,7 @@
|
|
| 274 |
{
|
| 275 |
"name": "q4_colwise_relu_deep_reduction",
|
| 276 |
"provenance": {
|
| 277 |
-
"notes": "A 128-
|
| 278 |
},
|
| 279 |
"attrs": { "weights_prepacked": 0 },
|
| 280 |
"inputs": {
|
|
@@ -527,7 +514,7 @@
|
|
| 527 |
{
|
| 528 |
"name": "q4_grouped_prefill_partial_tiles",
|
| 529 |
"provenance": {
|
| 530 |
-
"notes": "
|
| 531 |
},
|
| 532 |
"attrs": {
|
| 533 |
"activation_type": "swiglu",
|
|
@@ -573,7 +560,7 @@
|
|
| 573 |
{
|
| 574 |
"name": "q8_grouped_prefill_swiglu_blockwise",
|
| 575 |
"provenance": {
|
| 576 |
-
"notes": "Enough routed slots per expert
|
| 577 |
},
|
| 578 |
"attrs": {
|
| 579 |
"activation_type": "swiglu",
|
|
@@ -617,6 +604,196 @@
|
|
| 617 |
}
|
| 618 |
},
|
| 619 |
"outputs": { "outputT": { "dtype": "float32", "shape": [48, 64], "tolerance": 0.0003 } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 620 |
}
|
| 621 |
]
|
| 622 |
}
|
|
|
|
| 1 |
{
|
|
|
|
| 2 |
"cases": [
|
| 3 |
{
|
| 4 |
"name": "q4_colwise_default_relu",
|
| 5 |
"provenance": {
|
| 6 |
+
"notes": "Exercises every exact default at once: 4-bit integer weights, k=1, ReLU, column-wise scales, dense routing, and the raw uint8-packed weight layout."
|
| 7 |
},
|
| 8 |
"inputs": {
|
| 9 |
"inputT": {
|
|
|
|
| 38 |
{
|
| 39 |
"name": "q4_top1_tie_selects_higher_expert",
|
| 40 |
"provenance": {
|
| 41 |
+
"notes": "Descending `(logit, expert_index)` ordering with selected-weight normalization: equal logits select expert 1, whose output is [16, 32], rather than the all-zero expert 0."
|
| 42 |
},
|
| 43 |
"attrs": { "normalize_routing_weights": 1 },
|
| 44 |
"inputs": {
|
|
|
|
| 226 |
{
|
| 227 |
"name": "q4_zero_inter",
|
| 228 |
"provenance": {
|
| 229 |
+
"notes": "A zero intermediate dimension has an exact zero result without binding empty weight buffers in a shader. No token, routing or scale value can reach the result once the intermediate width is zero, so the fill values are uniform."
|
| 230 |
},
|
| 231 |
"attrs": { "activation_type": "swiglu", "swiglu_fusion": 1 },
|
| 232 |
"inputs": {
|
| 233 |
+
"inputT": { "dtype": "float32", "shape": [3, 4], "data": { "kind": "constant", "value": 0.3 } },
|
| 234 |
+
"routerT": { "dtype": "float32", "shape": [3, 3], "data": { "kind": "constant", "value": 0.7 } },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
"fc1T": { "dtype": "uint8", "shape": [3, 0, 2], "data": { "kind": "values", "values": [] } },
|
| 236 |
"fc1ScalesT": { "dtype": "float32", "shape": [3, 0], "data": { "kind": "values", "values": [] } },
|
| 237 |
"fc2T": { "dtype": "uint8", "shape": [3, 4, 0], "data": { "kind": "values", "values": [] } },
|
| 238 |
+
"fc2ScalesT": { "dtype": "float32", "shape": [3, 4], "data": { "kind": "constant", "value": 0.02 } }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
},
|
| 240 |
"outputs": {
|
| 241 |
"outputT": { "dtype": "float32", "shape": [3, 4], "data": { "kind": "constant", "value": 0.0 }, "tolerance": 0 }
|
|
|
|
| 261 |
{
|
| 262 |
"name": "q4_colwise_relu_deep_reduction",
|
| 263 |
"provenance": {
|
| 264 |
+
"notes": "A 128-element reduction on both projections exercises the cooperative multi-lane schedule for q4 weights."
|
| 265 |
},
|
| 266 |
"attrs": { "weights_prepacked": 0 },
|
| 267 |
"inputs": {
|
|
|
|
| 514 |
{
|
| 515 |
"name": "q4_grouped_prefill_partial_tiles",
|
| 516 |
"provenance": {
|
| 517 |
+
"notes": "Three experts share 64 routed slots across 32-row tiles, so most lanes address padding and the store guard discards more rows than it writes."
|
| 518 |
},
|
| 519 |
"attrs": {
|
| 520 |
"activation_type": "swiglu",
|
|
|
|
| 560 |
{
|
| 561 |
"name": "q8_grouped_prefill_swiglu_blockwise",
|
| 562 |
"provenance": {
|
| 563 |
+
"notes": "Enough routed slots per expert fill grouped tiles, allowing each dequantized weight tile to serve several slots. The tolerance covers f32 reassociation in the tiled reduction while remaining far below a 10% error on outputs near 0.4."
|
| 564 |
},
|
| 565 |
"attrs": {
|
| 566 |
"activation_type": "swiglu",
|
|
|
|
| 604 |
}
|
| 605 |
},
|
| 606 |
"outputs": { "outputT": { "dtype": "float32", "shape": [48, 64], "tolerance": 0.0003 } }
|
| 607 |
+
},
|
| 608 |
+
{
|
| 609 |
+
"name": "q4_grouped_stage_t33_h96_i96_block0_swiglu",
|
| 610 |
+
"attrs": {
|
| 611 |
+
"activation_type": "swiglu",
|
| 612 |
+
"expert_weight_bits": 4,
|
| 613 |
+
"block_size": 0,
|
| 614 |
+
"k": 2,
|
| 615 |
+
"normalize_routing_weights": 1,
|
| 616 |
+
"weights_prepacked": 0,
|
| 617 |
+
"swiglu_fusion": 1
|
| 618 |
+
},
|
| 619 |
+
"inputs": {
|
| 620 |
+
"inputT": {
|
| 621 |
+
"dtype": "float32",
|
| 622 |
+
"shape": [33, 96],
|
| 623 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.17, "scale": 0.05 }
|
| 624 |
+
},
|
| 625 |
+
"routerT": {
|
| 626 |
+
"dtype": "float32",
|
| 627 |
+
"shape": [33, 3],
|
| 628 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.44, "cosStep": 0.12, "scale": 0.6 }
|
| 629 |
+
},
|
| 630 |
+
"fc1T": {
|
| 631 |
+
"dtype": "uint8",
|
| 632 |
+
"shape": [3, 192, 48],
|
| 633 |
+
"data": { "kind": "cycle", "values": [97, 182, 45, 213, 131, 68, 159, 22, 241] }
|
| 634 |
+
},
|
| 635 |
+
"fc2T": {
|
| 636 |
+
"dtype": "uint8",
|
| 637 |
+
"shape": [3, 96, 48],
|
| 638 |
+
"data": { "kind": "cycle", "values": [241, 22, 159, 68, 131, 213, 45, 182, 97] }
|
| 639 |
+
},
|
| 640 |
+
"fc1ScalesT": {
|
| 641 |
+
"dtype": "float32",
|
| 642 |
+
"shape": [3, 192],
|
| 643 |
+
"data": { "kind": "linspace", "start": 0.002, "end": 0.02 }
|
| 644 |
+
},
|
| 645 |
+
"fc2ScalesT": {
|
| 646 |
+
"dtype": "float32",
|
| 647 |
+
"shape": [3, 96],
|
| 648 |
+
"data": { "kind": "linspace", "start": 0.003, "end": 0.018 }
|
| 649 |
+
}
|
| 650 |
+
},
|
| 651 |
+
"outputs": { "outputT": { "dtype": "float32", "shape": [33, 96], "tolerance": 0.000002 } },
|
| 652 |
+
"provenance": {
|
| 653 |
+
"notes": "Cooperative quantized weight staging across partial 64-column output tiles and routed row tails, with 4/8-bit codes and column/block scales. Small deterministic inputs keep the existing strict f32 tolerance meaningful."
|
| 654 |
+
}
|
| 655 |
+
},
|
| 656 |
+
{
|
| 657 |
+
"name": "q4_grouped_stage_t31_h96_i32_block16_relu",
|
| 658 |
+
"attrs": {
|
| 659 |
+
"activation_type": "relu",
|
| 660 |
+
"expert_weight_bits": 4,
|
| 661 |
+
"block_size": 16,
|
| 662 |
+
"k": 2,
|
| 663 |
+
"normalize_routing_weights": 1,
|
| 664 |
+
"weights_prepacked": 0
|
| 665 |
+
},
|
| 666 |
+
"inputs": {
|
| 667 |
+
"inputT": {
|
| 668 |
+
"dtype": "float32",
|
| 669 |
+
"shape": [31, 96],
|
| 670 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.17, "scale": 0.05 }
|
| 671 |
+
},
|
| 672 |
+
"routerT": {
|
| 673 |
+
"dtype": "float32",
|
| 674 |
+
"shape": [31, 3],
|
| 675 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.44, "cosStep": 0.12, "scale": 0.6 }
|
| 676 |
+
},
|
| 677 |
+
"fc1T": {
|
| 678 |
+
"dtype": "uint8",
|
| 679 |
+
"shape": [3, 32, 48],
|
| 680 |
+
"data": { "kind": "cycle", "values": [97, 182, 45, 213, 131, 68, 159, 22, 241] }
|
| 681 |
+
},
|
| 682 |
+
"fc2T": {
|
| 683 |
+
"dtype": "uint8",
|
| 684 |
+
"shape": [3, 96, 16],
|
| 685 |
+
"data": { "kind": "cycle", "values": [241, 22, 159, 68, 131, 213, 45, 182, 97] }
|
| 686 |
+
},
|
| 687 |
+
"fc1ScalesT": {
|
| 688 |
+
"dtype": "float32",
|
| 689 |
+
"shape": [3, 32, 6],
|
| 690 |
+
"data": { "kind": "linspace", "start": 0.002, "end": 0.02 }
|
| 691 |
+
},
|
| 692 |
+
"fc2ScalesT": {
|
| 693 |
+
"dtype": "float32",
|
| 694 |
+
"shape": [3, 96, 2],
|
| 695 |
+
"data": { "kind": "linspace", "start": 0.003, "end": 0.018 }
|
| 696 |
+
}
|
| 697 |
+
},
|
| 698 |
+
"outputs": { "outputT": { "dtype": "float32", "shape": [31, 96], "tolerance": 0.00005 } },
|
| 699 |
+
"provenance": {
|
| 700 |
+
"notes": "Cooperative quantized weight staging across partial 64-column output tiles and routed row tails, with 4/8-bit codes and column/block scales. Small deterministic inputs keep the existing strict f32 tolerance meaningful."
|
| 701 |
+
}
|
| 702 |
+
},
|
| 703 |
+
{
|
| 704 |
+
"name": "q8_grouped_stage_t65_h128_i96_block32_swiglu",
|
| 705 |
+
"attrs": {
|
| 706 |
+
"activation_type": "swiglu",
|
| 707 |
+
"expert_weight_bits": 8,
|
| 708 |
+
"block_size": 32,
|
| 709 |
+
"k": 2,
|
| 710 |
+
"normalize_routing_weights": 1,
|
| 711 |
+
"weights_prepacked": 0,
|
| 712 |
+
"swiglu_fusion": 1
|
| 713 |
+
},
|
| 714 |
+
"inputs": {
|
| 715 |
+
"inputT": {
|
| 716 |
+
"dtype": "float32",
|
| 717 |
+
"shape": [65, 128],
|
| 718 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.17, "scale": 0.05 }
|
| 719 |
+
},
|
| 720 |
+
"routerT": {
|
| 721 |
+
"dtype": "float32",
|
| 722 |
+
"shape": [65, 4],
|
| 723 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.44, "cosStep": 0.12, "scale": 0.6 }
|
| 724 |
+
},
|
| 725 |
+
"fc1T": {
|
| 726 |
+
"dtype": "uint8",
|
| 727 |
+
"shape": [4, 192, 128],
|
| 728 |
+
"data": { "kind": "cycle", "values": [97, 182, 45, 213, 131, 68, 159, 22, 241] }
|
| 729 |
+
},
|
| 730 |
+
"fc2T": {
|
| 731 |
+
"dtype": "uint8",
|
| 732 |
+
"shape": [4, 128, 96],
|
| 733 |
+
"data": { "kind": "cycle", "values": [241, 22, 159, 68, 131, 213, 45, 182, 97] }
|
| 734 |
+
},
|
| 735 |
+
"fc1ScalesT": {
|
| 736 |
+
"dtype": "float32",
|
| 737 |
+
"shape": [4, 192, 4],
|
| 738 |
+
"data": { "kind": "linspace", "start": 0.002, "end": 0.02 }
|
| 739 |
+
},
|
| 740 |
+
"fc2ScalesT": {
|
| 741 |
+
"dtype": "float32",
|
| 742 |
+
"shape": [4, 128, 3],
|
| 743 |
+
"data": { "kind": "linspace", "start": 0.003, "end": 0.018 }
|
| 744 |
+
}
|
| 745 |
+
},
|
| 746 |
+
"outputs": { "outputT": { "dtype": "float32", "shape": [65, 128], "tolerance": 0.00005 } },
|
| 747 |
+
"provenance": {
|
| 748 |
+
"notes": "Cooperative quantized weight staging across partial 64-column output tiles and routed row tails, with 4/8-bit codes and column/block scales. Small deterministic inputs keep the existing strict f32 tolerance meaningful."
|
| 749 |
+
}
|
| 750 |
+
},
|
| 751 |
+
{
|
| 752 |
+
"name": "q8_grouped_stage_t32_h96_i160_block0_relu",
|
| 753 |
+
"attrs": {
|
| 754 |
+
"activation_type": "relu",
|
| 755 |
+
"expert_weight_bits": 8,
|
| 756 |
+
"block_size": 0,
|
| 757 |
+
"k": 1,
|
| 758 |
+
"normalize_routing_weights": 1,
|
| 759 |
+
"weights_prepacked": 0
|
| 760 |
+
},
|
| 761 |
+
"inputs": {
|
| 762 |
+
"inputT": {
|
| 763 |
+
"dtype": "float32",
|
| 764 |
+
"shape": [32, 96],
|
| 765 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.17, "scale": 0.05 }
|
| 766 |
+
},
|
| 767 |
+
"routerT": {
|
| 768 |
+
"dtype": "float32",
|
| 769 |
+
"shape": [32, 3],
|
| 770 |
+
"data": { "kind": "fillFloat32", "sinStep": 0.44, "cosStep": 0.12, "scale": 0.6 }
|
| 771 |
+
},
|
| 772 |
+
"fc1T": {
|
| 773 |
+
"dtype": "uint8",
|
| 774 |
+
"shape": [3, 160, 96],
|
| 775 |
+
"data": { "kind": "cycle", "values": [97, 182, 45, 213, 131, 68, 159, 22, 241] }
|
| 776 |
+
},
|
| 777 |
+
"fc2T": {
|
| 778 |
+
"dtype": "uint8",
|
| 779 |
+
"shape": [3, 96, 160],
|
| 780 |
+
"data": { "kind": "cycle", "values": [241, 22, 159, 68, 131, 213, 45, 182, 97] }
|
| 781 |
+
},
|
| 782 |
+
"fc1ScalesT": {
|
| 783 |
+
"dtype": "float32",
|
| 784 |
+
"shape": [3, 160],
|
| 785 |
+
"data": { "kind": "linspace", "start": 0.002, "end": 0.02 }
|
| 786 |
+
},
|
| 787 |
+
"fc2ScalesT": {
|
| 788 |
+
"dtype": "float32",
|
| 789 |
+
"shape": [3, 96],
|
| 790 |
+
"data": { "kind": "linspace", "start": 0.003, "end": 0.018 }
|
| 791 |
+
}
|
| 792 |
+
},
|
| 793 |
+
"outputs": { "outputT": { "dtype": "float32", "shape": [32, 96], "tolerance": 0.00005 } },
|
| 794 |
+
"provenance": {
|
| 795 |
+
"notes": "Cooperative quantized weight staging across partial 64-column output tiles and routed row tails, with 4/8-bit codes and column/block scales. Small deterministic inputs keep the existing strict f32 tolerance meaningful."
|
| 796 |
+
}
|
| 797 |
}
|
| 798 |
]
|
| 799 |
}
|