sync 91d990483a17
Browse files- README.md +17 -12
- build/webgpu/bench.json +0 -1
- build/webgpu/manifest.json +270 -233
- build/webgpu/metadata.json +20 -7
- build/webgpu/rotary-embedding-quad.wgsl.jinja +88 -0
- build/webgpu/rotary-embedding.wgsl.jinja +17 -17
- build/webgpu/test.json +108 -3
README.md
CHANGED
|
@@ -12,24 +12,24 @@ tags:
|
|
| 12 |
|
| 13 |
## Description
|
| 14 |
|
| 15 |
-
Implements ONNX opset-23 RotaryEmbedding for float16 and float32 tensors. Applies rotary positional embeddings (RoPE) by rotating each head's embedding vector using precomputed `cos_cache` and `sin_cache` values. A partial rotation can be applied by setting `rotary_embedding_dim` to rotate only a prefix of the head dimension. `position_ids` keeps its standard logical int64 type; valid positions are non-negative and bounded by the WebGPU-addressable cache, so the backend stores them losslessly as uint32. Other ONNX floating-point input types are
|
| 16 |
|
| 17 |
See the [ONNX `RotaryEmbedding` spec](https://onnx.ai/onnx/operators/onnx__RotaryEmbedding.html) for the reference semantics.
|
| 18 |
|
| 19 |
## Inputs
|
| 20 |
|
| 21 |
-
| Name |
|
| 22 |
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
-
| `
|
| 24 |
-
| `
|
| 25 |
-
| `
|
| 26 |
-
| `
|
| 27 |
|
| 28 |
## Outputs
|
| 29 |
|
| 30 |
-
| Name |
|
| 31 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 32 |
-
| `
|
| 33 |
|
| 34 |
## Attributes
|
| 35 |
|
|
@@ -38,8 +38,8 @@ Attributes and default values (overridable per request):
|
|
| 38 |
| Attribute | Default | Description |
|
| 39 |
| --- | --- | --- |
|
| 40 |
| `interleaved` | `0` | Set to 1 to rotate using an interleaved pattern (even/odd elements), or 0 to split the head dimension into two contiguous halves. Default is 0. |
|
| 41 |
-
| `rotary_embedding_dim` | `0` | Number of head-dimension elements to rotate; `0` means rotate the full head dimension. When set, only the leading `rotary_embedding_dim` elements are rotated and the rest are passed through unchanged. |
|
| 42 |
| `num_heads` | — | Optional number of attention heads. ONNX requires this attribute when `X` is rank 3; it is unnecessary for rank-4 input because the head count is explicit in the shape. |
|
|
|
|
| 43 |
|
| 44 |
## Type constraints
|
| 45 |
|
|
@@ -50,18 +50,23 @@ Attributes and default values (overridable per request):
|
|
| 50 |
|
| 51 |
## Files
|
| 52 |
|
| 53 |
-
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
|
| 54 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 55 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 56 |
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
|
|
|
| 57 |
- [`rotary-embedding.wgsl.jinja`](build/webgpu/rotary-embedding.wgsl.jinja)
|
| 58 |
|
| 59 |
## Use with `@huggingface/kernels`
|
| 60 |
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
|
|
|
| 65 |
|
| 66 |
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 67 |
|
|
|
|
| 12 |
|
| 13 |
## Description
|
| 14 |
|
| 15 |
+
Implements ONNX opset-23 RotaryEmbedding for float16 and float32 tensors. Applies rotary positional embeddings (RoPE) by rotating each head's embedding vector using precomputed `cos_cache` and `sin_cache` values. A partial rotation can be applied by setting `rotary_embedding_dim` to rotate only a prefix of the head dimension. `position_ids` keeps its standard logical int64 type; valid positions are non-negative and bounded by the WebGPU-addressable cache, so the backend stores them losslessly as uint32. Other ONNX floating-point input types are unsupported.
|
| 16 |
|
| 17 |
See the [ONNX `RotaryEmbedding` spec](https://onnx.ai/onnx/operators/onnx__RotaryEmbedding.html) for the reference semantics.
|
| 18 |
|
| 19 |
## Inputs
|
| 20 |
|
| 21 |
+
| Name | Upstream name | Logical dtype | WebGPU storage | Rank | Shape | Description | Presence |
|
| 22 |
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| 23 |
+
| `x` | `X` | `T` | same as logical dtype | — | — | Input token embeddings. Shape is `(batch_size, sequence_length, hidden_size)` for rank 3 or `(batch_size, num_heads, sequence_length, head_size)` for rank 4. `head_size` must be even, and the `num_heads` attribute is required for rank-3 input. | required |
|
| 24 |
+
| `cos` | `cos_cache` | `T` | same as logical dtype | — | — | Precomputed cosine values. Without `position_ids`, shape is `(batch_size, sequence_length, rotary_dim/2)`; with `position_ids`, shape is `(max_sequence_length, rotary_dim/2)`. | required |
|
| 25 |
+
| `sin` | `sin_cache` | `T` | same as logical dtype | — | — | Precomputed sine values with the same shape and type as `cos_cache`. | required |
|
| 26 |
+
| `positionIds` | `position_ids` | `M` | `uint32` | `2` | — | Optional logical int64 per-token position indices of shape `(batch_size, sequence_length)`. Valid positions are non-negative cache-row indices and use uint32 WebGPU storage. When supplied, the 2D cache tables are gathered at these positions. | optional |
|
| 27 |
|
| 28 |
## Outputs
|
| 29 |
|
| 30 |
+
| Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
|
| 31 |
| --- | --- | --- | --- | --- | --- | --- |
|
| 32 |
+
| `y` | `Y` | `T` | same as `x` | same as `x` | Rotary-position-encoded tensor with the same shape and type as `X`. | required |
|
| 33 |
|
| 34 |
## Attributes
|
| 35 |
|
|
|
|
| 38 |
| Attribute | Default | Description |
|
| 39 |
| --- | --- | --- |
|
| 40 |
| `interleaved` | `0` | Set to 1 to rotate using an interleaved pattern (even/odd elements), or 0 to split the head dimension into two contiguous halves. Default is 0. |
|
|
|
|
| 41 |
| `num_heads` | — | Optional number of attention heads. ONNX requires this attribute when `X` is rank 3; it is unnecessary for rank-4 input because the head count is explicit in the shape. |
|
| 42 |
+
| `rotary_embedding_dim` | `0` | Number of head-dimension elements to rotate; `0` means rotate the full head dimension. When set, only the leading `rotary_embedding_dim` elements are rotated and the rest are passed through unchanged. |
|
| 43 |
|
| 44 |
## Type constraints
|
| 45 |
|
|
|
|
| 50 |
|
| 51 |
## Files
|
| 52 |
|
| 53 |
+
- [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
|
| 54 |
- [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
|
| 55 |
- [`test.json`](build/webgpu/test.json) — correctness cases
|
| 56 |
- [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
|
| 57 |
+
- [`rotary-embedding-quad.wgsl.jinja`](build/webgpu/rotary-embedding-quad.wgsl.jinja)
|
| 58 |
- [`rotary-embedding.wgsl.jinja`](build/webgpu/rotary-embedding.wgsl.jinja)
|
| 59 |
|
| 60 |
## Use with `@huggingface/kernels`
|
| 61 |
|
| 62 |
+
```sh
|
| 63 |
+
npm install --save-exact @huggingface/kernels@0.0.1-preview.2
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
|
| 67 |
|
| 68 |
The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
|
| 69 |
+
It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
|
| 70 |
|
| 71 |
Replace each `*Data` placeholder with a typed array containing the corresponding input data.
|
| 72 |
|
build/webgpu/bench.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
{
|
| 2 |
-
"op": "ai.onnx.RotaryEmbedding",
|
| 3 |
"tunableSpace": { "WORKGROUP_SIZE": [128, 256] },
|
| 4 |
"cases": [
|
| 5 |
{
|
|
|
|
| 1 |
{
|
|
|
|
| 2 |
"tunableSpace": { "WORKGROUP_SIZE": [128, 256] },
|
| 3 |
"cases": [
|
| 4 |
{
|
build/webgpu/manifest.json
CHANGED
|
@@ -2,287 +2,324 @@
|
|
| 2 |
"domain": "ai.onnx",
|
| 3 |
"name": "RotaryEmbedding",
|
| 4 |
"sinceVersion": 23,
|
| 5 |
-
"
|
| 6 |
-
|
| 7 |
-
{
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
"
|
| 11 |
-
},
|
| 12 |
-
{
|
| 13 |
-
"role": "cos_cache",
|
| 14 |
-
"dtype": "T",
|
| 15 |
-
"description": "Precomputed cosine values. Without `position_ids`, shape is `(batch_size, sequence_length, rotary_dim/2)`; with `position_ids`, shape is `(max_sequence_length, rotary_dim/2)`."
|
| 16 |
-
},
|
| 17 |
-
{
|
| 18 |
-
"role": "sin_cache",
|
| 19 |
-
"dtype": "T",
|
| 20 |
-
"description": "Precomputed sine values with the same shape and type as `cos_cache`."
|
| 21 |
-
},
|
| 22 |
-
{
|
| 23 |
-
"role": "position_ids",
|
| 24 |
"dtype": "M",
|
| 25 |
"rank": 2,
|
| 26 |
"optional": true,
|
| 27 |
-
"
|
| 28 |
-
|
| 29 |
-
],
|
| 30 |
-
"outputs": [
|
| 31 |
-
{
|
| 32 |
-
"role": "Y",
|
| 33 |
-
"dtype": "T",
|
| 34 |
-
"rank": "ranks.X",
|
| 35 |
-
"shape": "shapes.X",
|
| 36 |
-
"description": "Rotary-position-encoded tensor with the same shape and type as `X`."
|
| 37 |
}
|
| 38 |
-
],
|
| 39 |
-
"attributes": { "interleaved": 0, "rotary_embedding_dim": 0 },
|
| 40 |
-
"attributeDescriptions": {
|
| 41 |
-
"interleaved": "Set to 1 to rotate using an interleaved pattern (even/odd elements), or 0 to split the head dimension into two contiguous halves. Default is 0.",
|
| 42 |
-
"num_heads": "Optional number of attention heads. ONNX requires this attribute when `X` is rank 3; it is unnecessary for rank-4 input because the head count is explicit in the shape.",
|
| 43 |
-
"rotary_embedding_dim": "Number of head-dimension elements to rotate; `0` means rotate the full head dimension. When set, only the leading `rotary_embedding_dim` elements are rotated and the rest are passed through unchanged."
|
| 44 |
},
|
|
|
|
|
|
|
| 45 |
"attributeConstraints": { "interleaved": { "values": [0, 1] } },
|
| 46 |
"typeConstraints": { "T": ["float32", "float16"], "M": ["int64"] },
|
| 47 |
-
"
|
| 48 |
-
"x": { "kind": "tensor", "semantic": "X", "role": "input" },
|
| 49 |
-
"cos": { "kind": "tensor", "semantic": "cos_cache", "role": "input" },
|
| 50 |
-
"sin": { "kind": "tensor", "semantic": "sin_cache", "role": "input" },
|
| 51 |
-
"positionIds": {
|
| 52 |
-
"kind": "tensor",
|
| 53 |
-
"semantic": "position_ids",
|
| 54 |
-
"role": "input",
|
| 55 |
-
"dtype": "uint32",
|
| 56 |
-
"narrowing": "checked",
|
| 57 |
-
"required": false
|
| 58 |
-
},
|
| 59 |
-
"y": { "kind": "tensor", "semantic": "Y", "role": "output" }
|
| 60 |
-
},
|
| 61 |
-
"tunables": { "WORKGROUP_SIZE": 256 },
|
| 62 |
"derive": {
|
| 63 |
-
"pairDispatchOk": "ceilDiv(numel(shapes.
|
| 64 |
-
"rank3HeadSize": "dim(shapes.
|
| 65 |
-
"rank3Contract": "f16Ok(dtypes.T) and ranks.
|
| 66 |
-
"rank4Contract": "f16Ok(dtypes.T) and ranks.
|
| 67 |
-
"rank3PosContract": "f16Ok(dtypes.T) and ranks.
|
| 68 |
-
"rank4PosContract": "f16Ok(dtypes.T) and ranks.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
},
|
| 70 |
-
"
|
| 71 |
-
"
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
"name": "
|
| 82 |
-
"
|
| 83 |
-
"
|
| 84 |
-
"
|
| 85 |
-
"
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
"name": "
|
| 114 |
-
"
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
"
|
| 122 |
-
"
|
| 123 |
-
"
|
| 124 |
-
"
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
"name": "
|
| 136 |
-
"
|
| 137 |
-
"
|
| 138 |
-
"
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
}
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
"name": "
|
| 162 |
-
"
|
| 163 |
-
"
|
| 164 |
-
"
|
| 165 |
-
"
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
"name": "
|
| 177 |
-
"
|
| 178 |
-
"
|
| 179 |
-
"
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
{ "name": "numHeads", "type": "u32", "value": "dim(shapes.X, 1)" },
|
| 184 |
-
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.X, 2)" },
|
| 185 |
-
{ "name": "headSize", "type": "u32", "value": "dim(shapes.X, 3)" },
|
| 186 |
-
{ "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 1) * 2" },
|
| 187 |
-
{ "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 1)" }
|
| 188 |
-
]
|
| 189 |
-
}
|
| 190 |
-
}
|
| 191 |
-
],
|
| 192 |
-
"rank4": [
|
| 193 |
-
{ "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "$T" },
|
| 194 |
-
{
|
| 195 |
-
"name": "cos_cache",
|
| 196 |
-
"arg": "cos",
|
| 197 |
-
"semantic": "cos_cache",
|
| 198 |
-
"buffer": { "type": "read-only-storage" },
|
| 199 |
-
"elementType": "$T"
|
| 200 |
-
},
|
| 201 |
-
{
|
| 202 |
-
"name": "sin_cache",
|
| 203 |
-
"arg": "sin",
|
| 204 |
-
"semantic": "sin_cache",
|
| 205 |
-
"buffer": { "type": "read-only-storage" },
|
| 206 |
-
"elementType": "$T"
|
| 207 |
-
},
|
| 208 |
-
{ "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "$T" },
|
| 209 |
-
{
|
| 210 |
-
"name": "params",
|
| 211 |
-
"semantic": "kernel.params",
|
| 212 |
-
"buffer": { "type": "uniform" },
|
| 213 |
-
"struct": {
|
| 214 |
-
"name": "Params",
|
| 215 |
-
"fields": [
|
| 216 |
-
{ "name": "pairCount", "type": "u32", "value": "numel(shapes.X) / 2" },
|
| 217 |
-
{ "name": "numHeads", "type": "u32", "value": "dim(shapes.X, 1)" },
|
| 218 |
-
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.X, 2)" },
|
| 219 |
-
{ "name": "headSize", "type": "u32", "value": "dim(shapes.X, 3)" },
|
| 220 |
-
{ "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 2) * 2" },
|
| 221 |
-
{ "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos_cache, 2)" },
|
| 222 |
-
{
|
| 223 |
-
"name": "cacheBatchStride",
|
| 224 |
-
"type": "u32",
|
| 225 |
-
"value": "dim(shapes.cos_cache, 1) * dim(shapes.cos_cache, 2)"
|
| 226 |
-
}
|
| 227 |
-
]
|
| 228 |
-
}
|
| 229 |
-
}
|
| 230 |
-
]
|
| 231 |
},
|
| 232 |
"variants": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
{
|
| 234 |
"id": "rank3_cache2_pos",
|
|
|
|
| 235 |
"when": ["rank3PosContract", "pairDispatchOk"],
|
| 236 |
-
"
|
| 237 |
"passes": [
|
| 238 |
{
|
| 239 |
"id": "main",
|
| 240 |
"name": "rotary_embedding3d_pos",
|
| 241 |
-
"
|
| 242 |
-
"
|
| 243 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
}
|
| 245 |
]
|
| 246 |
},
|
| 247 |
{
|
| 248 |
"id": "rank4_cache2_pos",
|
|
|
|
| 249 |
"when": ["rank4PosContract", "pairDispatchOk"],
|
| 250 |
-
"
|
| 251 |
"passes": [
|
| 252 |
{
|
| 253 |
"id": "main",
|
| 254 |
"name": "rotary_embedding4d_pos",
|
| 255 |
-
"
|
| 256 |
-
"
|
| 257 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
}
|
| 259 |
]
|
| 260 |
},
|
| 261 |
{
|
| 262 |
"id": "rank3_cache3",
|
|
|
|
| 263 |
"when": ["rank3Contract", "pairDispatchOk"],
|
| 264 |
-
"
|
| 265 |
"passes": [
|
| 266 |
{
|
| 267 |
"id": "main",
|
| 268 |
"name": "rotary_embedding3d",
|
| 269 |
-
"
|
| 270 |
-
"
|
| 271 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
}
|
| 273 |
]
|
| 274 |
},
|
| 275 |
{
|
| 276 |
"id": "rank4_cache3",
|
|
|
|
| 277 |
"when": ["rank4Contract", "pairDispatchOk"],
|
| 278 |
-
"
|
| 279 |
"passes": [
|
| 280 |
{
|
| 281 |
"id": "main",
|
| 282 |
"name": "rotary_embedding4d",
|
| 283 |
-
"
|
| 284 |
-
"
|
| 285 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
}
|
| 287 |
]
|
| 288 |
}
|
|
|
|
| 2 |
"domain": "ai.onnx",
|
| 3 |
"name": "RotaryEmbedding",
|
| 4 |
"sinceVersion": 23,
|
| 5 |
+
"inputs": {
|
| 6 |
+
"x": { "onnx": "X", "dtype": "T" },
|
| 7 |
+
"cos": { "onnx": "cos_cache", "dtype": "T" },
|
| 8 |
+
"sin": { "onnx": "sin_cache", "dtype": "T" },
|
| 9 |
+
"positionIds": {
|
| 10 |
+
"onnx": "position_ids",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
"dtype": "M",
|
| 12 |
"rank": 2,
|
| 13 |
"optional": true,
|
| 14 |
+
"storage": "uint32",
|
| 15 |
+
"narrowing": "checked"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
},
|
| 18 |
+
"outputs": { "y": { "onnx": "Y", "dtype": "T", "rank": "ranks.x", "shape": "shapes.x" } },
|
| 19 |
+
"attributes": { "interleaved": { "default": 0 }, "rotary_embedding_dim": { "default": 0 }, "num_heads": {} },
|
| 20 |
"attributeConstraints": { "interleaved": { "values": [0, 1] } },
|
| 21 |
"typeConstraints": { "T": ["float32", "float16"], "M": ["int64"] },
|
| 22 |
+
"tunables": { "WORKGROUP_SIZE": { "default": 256 } },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
"derive": {
|
| 24 |
+
"pairDispatchOk": "ceilDiv(numel(shapes.x) / 2, tunables.WORKGROUP_SIZE) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) * min(device.limits.maxComputeWorkgroupsPerDimension, 65535)",
|
| 25 |
+
"rank3HeadSize": "dim(shapes.x, 2) / attrs.num_heads if attrs.num_heads is defined and attrs.num_heads > 0 else 0",
|
| 26 |
+
"rank3Contract": "f16Ok(dtypes.T) and ranks.x == 3 and ranks.cos == 3 and ranks.sin == 3 and ranks.y == 3 and sameShape(shapes.x, shapes.y) and attrs.num_heads is defined and attrs.num_heads >= 1 and dim(shapes.x, 2) % attrs.num_heads == 0 and rank3HeadSize % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= rank3HeadSize) and dim(shapes.cos, 2) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else rank3HeadSize) and sameShape(shapes.cos, shapes.sin) and dim(shapes.cos, 0) == dim(shapes.x, 0) and dim(shapes.cos, 1) == dim(shapes.x, 1) and not present.positionIds",
|
| 27 |
+
"rank4Contract": "f16Ok(dtypes.T) and ranks.x == 4 and ranks.cos == 3 and ranks.sin == 3 and ranks.y == 4 and dim(shapes.x, 3) % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= dim(shapes.x, 3)) and dim(shapes.cos, 2) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else dim(shapes.x, 3)) and dim(shapes.sin, 2) == dim(shapes.cos, 2) and sameShape(shapes.x, shapes.y) and dim(shapes.cos, 0) == dim(shapes.x, 0) and dim(shapes.sin, 0) == dim(shapes.x, 0) and dim(shapes.cos, 1) == dim(shapes.x, 2) and dim(shapes.sin, 1) == dim(shapes.x, 2) and not present.positionIds",
|
| 28 |
+
"rank3PosContract": "f16Ok(dtypes.T) and ranks.x == 3 and ranks.cos == 2 and ranks.sin == 2 and ranks.y == 3 and sameShape(shapes.x, shapes.y) and attrs.num_heads is defined and attrs.num_heads >= 1 and dim(shapes.x, 2) % attrs.num_heads == 0 and rank3HeadSize % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= rank3HeadSize) and dim(shapes.cos, 1) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else rank3HeadSize) and sameShape(shapes.cos, shapes.sin) and present.positionIds and ranks.positionIds == 2 and dim(shapes.positionIds, 0) == dim(shapes.x, 0) and dim(shapes.positionIds, 1) == dim(shapes.x, 1)",
|
| 29 |
+
"rank4PosContract": "f16Ok(dtypes.T) and ranks.x == 4 and ranks.cos == 2 and ranks.sin == 2 and ranks.y == 4 and dim(shapes.x, 3) % 2 == 0 and (attrs.rotary_embedding_dim == 0 or attrs.rotary_embedding_dim <= dim(shapes.x, 3)) and dim(shapes.cos, 1) * 2 == (attrs.rotary_embedding_dim if attrs.rotary_embedding_dim != 0 else dim(shapes.x, 3)) and sameShape(shapes.cos, shapes.sin) and sameShape(shapes.x, shapes.y) and present.positionIds and ranks.positionIds == 2 and dim(shapes.positionIds, 0) == dim(shapes.x, 0) and dim(shapes.positionIds, 1) == dim(shapes.x, 2)",
|
| 30 |
+
"quadCacheOk": "ranks.cos >= 1 and dim(shapes.cos, ranks.cos - 1) % 4 == 0",
|
| 31 |
+
"quad3Ok": "rank3HeadSize % 8 == 0 and quadCacheOk",
|
| 32 |
+
"quad4Ok": "ranks.x == 4 and dim(shapes.x, 3) % 8 == 0 and quadCacheOk",
|
| 33 |
+
"quadDispatchOk": "ceilDiv(numel(shapes.x) / 8, tunables.WORKGROUP_SIZE) <= min(device.limits.maxComputeWorkgroupsPerDimension, 65535) * min(device.limits.maxComputeWorkgroupsPerDimension, 65535)"
|
| 34 |
},
|
| 35 |
+
"bindings": {
|
| 36 |
+
"x": { "buffer": "read-only-storage", "elementType": "$vector" },
|
| 37 |
+
"cos_cache": { "arg": "cos", "buffer": "read-only-storage", "elementType": "$vector" },
|
| 38 |
+
"sin_cache": { "arg": "sin", "buffer": "read-only-storage", "elementType": "$vector" },
|
| 39 |
+
"position_ids": { "arg": "positionIds", "buffer": "read-only-storage", "elementType": "$M" },
|
| 40 |
+
"y": { "buffer": "storage", "elementType": "$vector" },
|
| 41 |
+
"params": {
|
| 42 |
+
"buffer": "uniform",
|
| 43 |
+
"struct": [
|
| 44 |
+
{ "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
|
| 45 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 46 |
+
{ "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
|
| 47 |
+
{ "name": "headVec4", "type": "u32", "value": "rank3HeadSize / 4" },
|
| 48 |
+
{ "name": "headQuads", "type": "u32", "value": "rank3HeadSize / 8" },
|
| 49 |
+
{ "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 2" },
|
| 50 |
+
{ "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 4" }
|
| 51 |
+
]
|
| 52 |
+
},
|
| 53 |
+
"x_2": { "name": "x", "buffer": "read-only-storage", "elementType": "$T" },
|
| 54 |
+
"cos_cache_2": { "arg": "cos", "name": "cos_cache", "buffer": "read-only-storage", "elementType": "$T" },
|
| 55 |
+
"sin_cache_2": { "arg": "sin", "name": "sin_cache", "buffer": "read-only-storage", "elementType": "$T" },
|
| 56 |
+
"y_2": { "name": "y", "buffer": "storage", "elementType": "$T" },
|
| 57 |
+
"params_2": {
|
| 58 |
+
"name": "params",
|
| 59 |
+
"buffer": "uniform",
|
| 60 |
+
"struct": [
|
| 61 |
+
{ "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
|
| 62 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 63 |
+
{ "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
|
| 64 |
+
{ "name": "headSize", "type": "u32", "value": "rank3HeadSize" },
|
| 65 |
+
{ "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 1) * 2" },
|
| 66 |
+
{ "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 1)" }
|
| 67 |
+
]
|
| 68 |
+
},
|
| 69 |
+
"params_3": {
|
| 70 |
+
"name": "params",
|
| 71 |
+
"buffer": "uniform",
|
| 72 |
+
"struct": [
|
| 73 |
+
{ "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
|
| 74 |
+
{ "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 75 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
|
| 76 |
+
{ "name": "headVec4", "type": "u32", "value": "dim(shapes.x, 3) / 4" },
|
| 77 |
+
{ "name": "headQuads", "type": "u32", "value": "dim(shapes.x, 3) / 8" },
|
| 78 |
+
{ "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 2" },
|
| 79 |
+
{ "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 1) / 4" }
|
| 80 |
+
]
|
| 81 |
+
},
|
| 82 |
+
"params_4": {
|
| 83 |
+
"name": "params",
|
| 84 |
+
"buffer": "uniform",
|
| 85 |
+
"struct": [
|
| 86 |
+
{ "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
|
| 87 |
+
{ "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 88 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
|
| 89 |
+
{ "name": "headSize", "type": "u32", "value": "dim(shapes.x, 3)" },
|
| 90 |
+
{ "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 1) * 2" },
|
| 91 |
+
{ "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 1)" }
|
| 92 |
+
]
|
| 93 |
+
},
|
| 94 |
+
"params_5": {
|
| 95 |
+
"name": "params",
|
| 96 |
+
"buffer": "uniform",
|
| 97 |
+
"struct": [
|
| 98 |
+
{ "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
|
| 99 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 100 |
+
{ "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
|
| 101 |
+
{ "name": "headVec4", "type": "u32", "value": "rank3HeadSize / 4" },
|
| 102 |
+
{ "name": "headQuads", "type": "u32", "value": "rank3HeadSize / 8" },
|
| 103 |
+
{ "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 2" },
|
| 104 |
+
{ "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 4" },
|
| 105 |
+
{ "name": "cacheBatchStrideVec4", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2) / 4" }
|
| 106 |
+
]
|
| 107 |
+
},
|
| 108 |
+
"params_6": {
|
| 109 |
+
"name": "params",
|
| 110 |
+
"buffer": "uniform",
|
| 111 |
+
"struct": [
|
| 112 |
+
{ "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
|
| 113 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 114 |
+
{ "name": "numHeads", "type": "u32", "value": "attrs.num_heads" },
|
| 115 |
+
{ "name": "headSize", "type": "u32", "value": "rank3HeadSize" },
|
| 116 |
+
{ "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 2) * 2" },
|
| 117 |
+
{ "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 2)" },
|
| 118 |
+
{ "name": "cacheBatchStride", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2)" }
|
| 119 |
+
]
|
| 120 |
+
},
|
| 121 |
+
"params_7": {
|
| 122 |
+
"name": "params",
|
| 123 |
+
"buffer": "uniform",
|
| 124 |
+
"struct": [
|
| 125 |
+
{ "name": "quadCount", "type": "u32", "value": "numel(shapes.x) / 8" },
|
| 126 |
+
{ "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 127 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
|
| 128 |
+
{ "name": "headVec4", "type": "u32", "value": "dim(shapes.x, 3) / 4" },
|
| 129 |
+
{ "name": "headQuads", "type": "u32", "value": "dim(shapes.x, 3) / 8" },
|
| 130 |
+
{ "name": "rotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 2" },
|
| 131 |
+
{ "name": "halfRotaryVec4", "type": "u32", "value": "dim(shapes.cos, 2) / 4" },
|
| 132 |
+
{ "name": "cacheBatchStrideVec4", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2) / 4" }
|
| 133 |
+
]
|
| 134 |
+
},
|
| 135 |
+
"params_8": {
|
| 136 |
+
"name": "params",
|
| 137 |
+
"buffer": "uniform",
|
| 138 |
+
"struct": [
|
| 139 |
+
{ "name": "pairCount", "type": "u32", "value": "numel(shapes.x) / 2" },
|
| 140 |
+
{ "name": "numHeads", "type": "u32", "value": "dim(shapes.x, 1)" },
|
| 141 |
+
{ "name": "sequenceLength", "type": "u32", "value": "dim(shapes.x, 2)" },
|
| 142 |
+
{ "name": "headSize", "type": "u32", "value": "dim(shapes.x, 3)" },
|
| 143 |
+
{ "name": "rotaryDim", "type": "u32", "value": "dim(shapes.cos, 2) * 2" },
|
| 144 |
+
{ "name": "halfRotaryDim", "type": "u32", "value": "dim(shapes.cos, 2)" },
|
| 145 |
+
{ "name": "cacheBatchStride", "type": "u32", "value": "dim(shapes.cos, 1) * dim(shapes.cos, 2)" }
|
| 146 |
+
]
|
| 147 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
},
|
| 149 |
"variants": [
|
| 150 |
+
{
|
| 151 |
+
"id": "rank3_cache2_pos_quad",
|
| 152 |
+
"priority": 10,
|
| 153 |
+
"when": ["rank3PosContract", "quad3Ok", "quadDispatchOk"],
|
| 154 |
+
"derive": {
|
| 155 |
+
"interleaved": "attrs.interleaved != 0",
|
| 156 |
+
"usesF16": "dtypes.T == \"f16\"",
|
| 157 |
+
"vector": "\"vec4<\" ~ dtypes.T ~ \">\""
|
| 158 |
+
},
|
| 159 |
+
"passes": [
|
| 160 |
+
{
|
| 161 |
+
"id": "main",
|
| 162 |
+
"name": "rotary_embedding3d_pos_quad",
|
| 163 |
+
"shader": "rotary-embedding-quad.wgsl.jinja",
|
| 164 |
+
"derive": { "rank": 3, "hasPositionIds": "true" },
|
| 165 |
+
"bindings": ["x", "cos_cache", "sin_cache", "position_ids", "y", "params"],
|
| 166 |
+
"dispatch": {
|
| 167 |
+
"x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 168 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 169 |
+
"z": 1
|
| 170 |
+
}
|
| 171 |
+
}
|
| 172 |
+
]
|
| 173 |
+
},
|
| 174 |
{
|
| 175 |
"id": "rank3_cache2_pos",
|
| 176 |
+
"priority": 0,
|
| 177 |
"when": ["rank3PosContract", "pairDispatchOk"],
|
| 178 |
+
"derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
|
| 179 |
"passes": [
|
| 180 |
{
|
| 181 |
"id": "main",
|
| 182 |
"name": "rotary_embedding3d_pos",
|
| 183 |
+
"shader": "rotary-embedding.wgsl.jinja",
|
| 184 |
+
"derive": { "rank": 3, "hasPositionIds": "true" },
|
| 185 |
+
"bindings": ["x_2", "cos_cache_2", "sin_cache_2", "position_ids", "y_2", "params_2"],
|
| 186 |
+
"dispatch": {
|
| 187 |
+
"x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 188 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 189 |
+
"z": 1
|
| 190 |
+
}
|
| 191 |
+
}
|
| 192 |
+
]
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"id": "rank4_cache2_pos_quad",
|
| 196 |
+
"priority": 10,
|
| 197 |
+
"when": ["rank4PosContract", "quad4Ok", "quadDispatchOk"],
|
| 198 |
+
"derive": {
|
| 199 |
+
"interleaved": "attrs.interleaved != 0",
|
| 200 |
+
"usesF16": "dtypes.T == \"f16\"",
|
| 201 |
+
"vector": "\"vec4<\" ~ dtypes.T ~ \">\""
|
| 202 |
+
},
|
| 203 |
+
"passes": [
|
| 204 |
+
{
|
| 205 |
+
"id": "main",
|
| 206 |
+
"name": "rotary_embedding4d_pos_quad",
|
| 207 |
+
"shader": "rotary-embedding-quad.wgsl.jinja",
|
| 208 |
+
"derive": { "rank": 4, "hasPositionIds": "true" },
|
| 209 |
+
"bindings": ["x", "cos_cache", "sin_cache", "position_ids", "y", "params_3"],
|
| 210 |
+
"dispatch": {
|
| 211 |
+
"x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 212 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 213 |
+
"z": 1
|
| 214 |
+
}
|
| 215 |
}
|
| 216 |
]
|
| 217 |
},
|
| 218 |
{
|
| 219 |
"id": "rank4_cache2_pos",
|
| 220 |
+
"priority": 0,
|
| 221 |
"when": ["rank4PosContract", "pairDispatchOk"],
|
| 222 |
+
"derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
|
| 223 |
"passes": [
|
| 224 |
{
|
| 225 |
"id": "main",
|
| 226 |
"name": "rotary_embedding4d_pos",
|
| 227 |
+
"shader": "rotary-embedding.wgsl.jinja",
|
| 228 |
+
"derive": { "rank": 4, "hasPositionIds": "true" },
|
| 229 |
+
"bindings": ["x_2", "cos_cache_2", "sin_cache_2", "position_ids", "y_2", "params_4"],
|
| 230 |
+
"dispatch": {
|
| 231 |
+
"x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 232 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 233 |
+
"z": 1
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
]
|
| 237 |
+
},
|
| 238 |
+
{
|
| 239 |
+
"id": "rank3_cache3_quad",
|
| 240 |
+
"priority": 10,
|
| 241 |
+
"when": ["rank3Contract", "quad3Ok", "quadDispatchOk"],
|
| 242 |
+
"derive": {
|
| 243 |
+
"interleaved": "attrs.interleaved != 0",
|
| 244 |
+
"usesF16": "dtypes.T == \"f16\"",
|
| 245 |
+
"vector": "\"vec4<\" ~ dtypes.T ~ \">\""
|
| 246 |
+
},
|
| 247 |
+
"passes": [
|
| 248 |
+
{
|
| 249 |
+
"id": "main",
|
| 250 |
+
"name": "rotary_embedding3d_quad",
|
| 251 |
+
"shader": "rotary-embedding-quad.wgsl.jinja",
|
| 252 |
+
"derive": { "rank": 3 },
|
| 253 |
+
"bindings": ["x", "cos_cache", "sin_cache", "y", "params_5"],
|
| 254 |
+
"dispatch": {
|
| 255 |
+
"x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 256 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 257 |
+
"z": 1
|
| 258 |
+
}
|
| 259 |
}
|
| 260 |
]
|
| 261 |
},
|
| 262 |
{
|
| 263 |
"id": "rank3_cache3",
|
| 264 |
+
"priority": 0,
|
| 265 |
"when": ["rank3Contract", "pairDispatchOk"],
|
| 266 |
+
"derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
|
| 267 |
"passes": [
|
| 268 |
{
|
| 269 |
"id": "main",
|
| 270 |
"name": "rotary_embedding3d",
|
| 271 |
+
"shader": "rotary-embedding.wgsl.jinja",
|
| 272 |
+
"derive": { "rank": 3 },
|
| 273 |
+
"bindings": ["x_2", "cos_cache_2", "sin_cache_2", "y_2", "params_6"],
|
| 274 |
+
"dispatch": {
|
| 275 |
+
"x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 276 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 277 |
+
"z": 1
|
| 278 |
+
}
|
| 279 |
+
}
|
| 280 |
+
]
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"id": "rank4_cache3_quad",
|
| 284 |
+
"priority": 10,
|
| 285 |
+
"when": ["rank4Contract", "quad4Ok", "quadDispatchOk"],
|
| 286 |
+
"derive": {
|
| 287 |
+
"interleaved": "attrs.interleaved != 0",
|
| 288 |
+
"usesF16": "dtypes.T == \"f16\"",
|
| 289 |
+
"vector": "\"vec4<\" ~ dtypes.T ~ \">\""
|
| 290 |
+
},
|
| 291 |
+
"passes": [
|
| 292 |
+
{
|
| 293 |
+
"id": "main",
|
| 294 |
+
"name": "rotary_embedding4d_quad",
|
| 295 |
+
"shader": "rotary-embedding-quad.wgsl.jinja",
|
| 296 |
+
"derive": { "rank": 4 },
|
| 297 |
+
"bindings": ["x", "cos_cache", "sin_cache", "y", "params_7"],
|
| 298 |
+
"dispatch": {
|
| 299 |
+
"x": "min(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 300 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 8), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 301 |
+
"z": 1
|
| 302 |
+
}
|
| 303 |
}
|
| 304 |
]
|
| 305 |
},
|
| 306 |
{
|
| 307 |
"id": "rank4_cache3",
|
| 308 |
+
"priority": 0,
|
| 309 |
"when": ["rank4Contract", "pairDispatchOk"],
|
| 310 |
+
"derive": { "interleaved": "attrs.interleaved != 0", "usesF16": "dtypes.T == \"f16\"", "scalar": "dtypes.T" },
|
| 311 |
"passes": [
|
| 312 |
{
|
| 313 |
"id": "main",
|
| 314 |
"name": "rotary_embedding4d",
|
| 315 |
+
"shader": "rotary-embedding.wgsl.jinja",
|
| 316 |
+
"derive": { "rank": 4 },
|
| 317 |
+
"bindings": ["x_2", "cos_cache_2", "sin_cache_2", "y_2", "params_8"],
|
| 318 |
+
"dispatch": {
|
| 319 |
+
"x": "min(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 320 |
+
"y": "ceilDiv(ceilDiv((numel(shapes.x) / 2), (tunables.WORKGROUP_SIZE)), 65535)",
|
| 321 |
+
"z": 1
|
| 322 |
+
}
|
| 323 |
}
|
| 324 |
]
|
| 325 |
}
|
build/webgpu/metadata.json
CHANGED
|
@@ -1,18 +1,31 @@
|
|
| 1 |
{
|
| 2 |
"name": "ai.onnx.RotaryEmbedding",
|
| 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 |
-
"manifest.json": "
|
| 12 |
-
"rotary-embedding.wgsl.jinja": "
|
| 13 |
-
"
|
|
|
|
| 14 |
}
|
| 15 |
},
|
| 16 |
-
"provenance": { "kernel": { "sha": "
|
| 17 |
-
"webgpu": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"name": "ai.onnx.RotaryEmbedding",
|
| 3 |
+
"id": "_ai_onnx_rotaryembedding_webgpu_5abb823",
|
| 4 |
"version": 1,
|
| 5 |
"license": "Apache-2.0",
|
| 6 |
"backend": { "type": "webgpu" },
|
| 7 |
"digest": {
|
| 8 |
"algorithm": "sha256",
|
| 9 |
"files": {
|
| 10 |
+
"bench.json": "Ii8Ve/7TIfVkcj95zDTEbsfND6RUQkTEHTKUFQhHaEk=",
|
| 11 |
+
"manifest.json": "OeUM+N0V8ex5eCGPP+KmT0LDzPNRCc50xnAuaYDS8o0=",
|
| 12 |
+
"rotary-embedding-quad.wgsl.jinja": "vGNZeSSXSpPhhBz/0Wh9Z1zY5lSqp0VlCsfWmIM98CQ=",
|
| 13 |
+
"rotary-embedding.wgsl.jinja": "DNauIDqC9Gs5N07X/rqra/Q72p6LnkcYueWx0UAaXlo=",
|
| 14 |
+
"test.json": "394nZIOecEWid/Qm/+4eUAtAw0l+WnhmDdAuuFb96IA="
|
| 15 |
}
|
| 16 |
},
|
| 17 |
+
"provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
|
| 18 |
+
"webgpu": {
|
| 19 |
+
"manifestSpec": "2.0",
|
| 20 |
+
"variants": {
|
| 21 |
+
"rank3_cache2_pos_quad": ["rotary-embedding-quad.wgsl.jinja"],
|
| 22 |
+
"rank3_cache2_pos": ["rotary-embedding.wgsl.jinja"],
|
| 23 |
+
"rank4_cache2_pos_quad": ["rotary-embedding-quad.wgsl.jinja"],
|
| 24 |
+
"rank4_cache2_pos": ["rotary-embedding.wgsl.jinja"],
|
| 25 |
+
"rank3_cache3_quad": ["rotary-embedding-quad.wgsl.jinja"],
|
| 26 |
+
"rank3_cache3": ["rotary-embedding.wgsl.jinja"],
|
| 27 |
+
"rank4_cache3_quad": ["rotary-embedding-quad.wgsl.jinja"],
|
| 28 |
+
"rank4_cache3": ["rotary-embedding.wgsl.jinja"]
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
}
|
build/webgpu/rotary-embedding-quad.wgsl.jinja
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
|
| 2 |
+
{% if note == "dispatch-limit" %}
|
| 3 |
+
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 4 |
+
// per-axis workgroup fold width (outputs > 16.7M elements).
|
| 5 |
+
{% elif note == "limit" %}
|
| 6 |
+
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 7 |
+
// per-axis workgroup fold width.
|
| 8 |
+
{% elif note == "device-axis" %}
|
| 9 |
+
// The flat dispatch is folded across x/y at a fixed per-axis workgroup
|
| 10 |
+
// width; gid.y carries the high portion of the output index.
|
| 11 |
+
{% elif note == "vec4-limit" %}
|
| 12 |
+
// 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
|
| 13 |
+
// per-axis workgroup fold width (the dispatch caps x and spills into y).
|
| 14 |
+
{% elif note == "element-limit" %}
|
| 15 |
+
// 2D-folded flat element index: gid.y carries the high bits past the
|
| 16 |
+
// dispatch's per-axis workgroup fold width.
|
| 17 |
+
{% elif note == "dispatch" %}
|
| 18 |
+
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 19 |
+
// per-axis workgroup fold width.
|
| 20 |
+
{% endif %}
|
| 21 |
+
{% if bound == "" %}
|
| 22 |
+
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 23 |
+
{%- elif guardInline %}
|
| 24 |
+
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 25 |
+
if ({{ name }} >= {{ bound }}) { return; }
|
| 26 |
+
{%- else %}
|
| 27 |
+
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 28 |
+
if ({{ name }} >= {{ bound }}) {
|
| 29 |
+
return;
|
| 30 |
+
}
|
| 31 |
+
{%- endif %}
|
| 32 |
+
{% endmacro %}
|
| 33 |
+
|
| 34 |
+
{% if usesF16 %}enable f16;
|
| 35 |
+
{% endif %}{{ env.wgsl.resourceDeclarations }}
|
| 36 |
+
// One invocation owns four consecutive rotated pairs, so x, cos and sin are read as vec4s and
|
| 37 |
+
// both rotated outputs are written as vec4s; the head size and rotary dim are multiples of
|
| 38 |
+
// eight, so no quad straddles a head or the rotated/tail boundary. Partial rotations use the
|
| 39 |
+
// remaining quad lanes to copy two unchanged tail vec4s. Every index below counts vec4s.
|
| 40 |
+
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 41 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 42 |
+
{{ flat_index_2d("q", "params.quadCount", note="") }}
|
| 43 |
+
|
| 44 |
+
let task = q % params.headQuads;
|
| 45 |
+
let headFlat = q / params.headQuads;
|
| 46 |
+
let base = headFlat * params.headVec4;
|
| 47 |
+
|
| 48 |
+
if (task >= params.halfRotaryVec4) {
|
| 49 |
+
let tail = params.rotaryVec4 + (task - params.halfRotaryVec4) * 2u;
|
| 50 |
+
y[base + tail] = x[base + tail];
|
| 51 |
+
y[base + tail + 1u] = x[base + tail + 1u];
|
| 52 |
+
return;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
let pair = task;
|
| 56 |
+
{% if rank == 3 %}
|
| 57 |
+
let token = (headFlat / params.numHeads) % params.sequenceLength;
|
| 58 |
+
let batch = headFlat / (params.numHeads * params.sequenceLength);
|
| 59 |
+
{% else %}
|
| 60 |
+
let token = headFlat % params.sequenceLength;
|
| 61 |
+
let batch = (headFlat / params.sequenceLength) / params.numHeads;
|
| 62 |
+
{% endif %}
|
| 63 |
+
{% if hasPositionIds is defined and hasPositionIds %}
|
| 64 |
+
// Gather the 2D cache tables through this token's position index.
|
| 65 |
+
let cache = u32(position_ids[batch * params.sequenceLength + token]) * params.halfRotaryVec4 + pair;
|
| 66 |
+
{% else %}
|
| 67 |
+
// The standard no-index cache owns one row per (batch, token).
|
| 68 |
+
let cache = batch * params.cacheBatchStrideVec4 + token * params.halfRotaryVec4 + pair;
|
| 69 |
+
{% endif %}
|
| 70 |
+
let cf = vec4<f32>(cos_cache[cache]);
|
| 71 |
+
let sf = vec4<f32>(sin_cache[cache]);
|
| 72 |
+
{% if interleaved %}
|
| 73 |
+
// Four interleaved pairs span two vec4s: (x0.x, x0.y), (x0.z, x0.w), (x1.x, x1.y), (x1.z, x1.w).
|
| 74 |
+
let x0 = vec4<f32>(x[base + pair * 2u]);
|
| 75 |
+
let x1 = vec4<f32>(x[base + pair * 2u + 1u]);
|
| 76 |
+
let a = vec4<f32>(x0.x, x0.z, x1.x, x1.z);
|
| 77 |
+
let b = vec4<f32>(x0.y, x0.w, x1.y, x1.w);
|
| 78 |
+
let ra = a * cf - b * sf;
|
| 79 |
+
let rb = a * sf + b * cf;
|
| 80 |
+
y[base + pair * 2u] = {{ vector }}(vec4<f32>(ra.x, rb.x, ra.y, rb.y));
|
| 81 |
+
y[base + pair * 2u + 1u] = {{ vector }}(vec4<f32>(ra.z, rb.z, ra.w, rb.w));
|
| 82 |
+
{% else %}
|
| 83 |
+
let a = vec4<f32>(x[base + pair]);
|
| 84 |
+
let b = vec4<f32>(x[base + params.halfRotaryVec4 + pair]);
|
| 85 |
+
y[base + pair] = {{ vector }}(a * cf - b * sf);
|
| 86 |
+
y[base + params.halfRotaryVec4 + pair] = {{ vector }}(a * sf + b * cf);
|
| 87 |
+
{% endif %}
|
| 88 |
+
}
|
build/webgpu/rotary-embedding.wgsl.jinja
CHANGED
|
@@ -1,30 +1,30 @@
|
|
| 1 |
{% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
|
| 2 |
{% if note == "dispatch-limit" %}
|
| 3 |
-
// 2D-folded flat index: gid.y carries the high bits past the
|
| 4 |
-
//
|
| 5 |
{% elif note == "limit" %}
|
| 6 |
-
// 2D-folded flat index: gid.y carries the high bits past the
|
| 7 |
-
//
|
| 8 |
{% elif note == "device-axis" %}
|
| 9 |
-
// The flat dispatch is folded across x/y at
|
| 10 |
-
//
|
| 11 |
{% elif note == "vec4-limit" %}
|
| 12 |
-
// 2D-folded flat vec4 index: gid.y carries the high bits past the
|
| 13 |
-
//
|
| 14 |
{% elif note == "element-limit" %}
|
| 15 |
// 2D-folded flat element index: gid.y carries the high bits past the
|
| 16 |
-
//
|
| 17 |
{% elif note == "dispatch" %}
|
| 18 |
-
// 2D-folded flat index: gid.y carries the high bits past the
|
| 19 |
-
//
|
| 20 |
{% endif %}
|
| 21 |
{% if bound == "" %}
|
| 22 |
-
let {{ name }} = gid.x + gid.y *
|
| 23 |
{%- elif guardInline %}
|
| 24 |
-
let {{ name }} = gid.x + gid.y *
|
| 25 |
if ({{ name }} >= {{ bound }}) { return; }
|
| 26 |
{%- else %}
|
| 27 |
-
let {{ name }} = gid.x + gid.y *
|
| 28 |
if ({{ name }} >= {{ bound }}) {
|
| 29 |
return;
|
| 30 |
}
|
|
@@ -36,7 +36,7 @@
|
|
| 36 |
// One invocation owns both outputs of a rotated pair, so x/cos/sin are read once.
|
| 37 |
// Partial rotations use the remaining pair lanes to copy two unchanged tail values.
|
| 38 |
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 39 |
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>
|
| 40 |
{{ flat_index_2d("p", "params.pairCount", note="") }}
|
| 41 |
|
| 42 |
let tasksPerHead = params.headSize / 2u;
|
|
@@ -52,14 +52,14 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
|
|
| 52 |
}
|
| 53 |
|
| 54 |
let pair = task;
|
| 55 |
-
{% if
|
| 56 |
let token = (headFlat / params.numHeads) % params.sequenceLength;
|
| 57 |
let batch = headFlat / (params.numHeads * params.sequenceLength);
|
| 58 |
{% else %}
|
| 59 |
let token = headFlat % params.sequenceLength;
|
| 60 |
let batch = (headFlat / params.sequenceLength) / params.numHeads;
|
| 61 |
{% endif %}
|
| 62 |
-
{% if
|
| 63 |
// Gather the 2D cache tables through this token's position index.
|
| 64 |
let cache = u32(position_ids[batch * params.sequenceLength + token]) * params.halfRotaryDim + pair;
|
| 65 |
{% else %}
|
|
|
|
| 1 |
{% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
|
| 2 |
{% if note == "dispatch-limit" %}
|
| 3 |
+
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 4 |
+
// per-axis workgroup fold width (outputs > 16.7M elements).
|
| 5 |
{% elif note == "limit" %}
|
| 6 |
+
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 7 |
+
// per-axis workgroup fold width.
|
| 8 |
{% elif note == "device-axis" %}
|
| 9 |
+
// The flat dispatch is folded across x/y at a fixed per-axis workgroup
|
| 10 |
+
// width; gid.y carries the high portion of the output index.
|
| 11 |
{% elif note == "vec4-limit" %}
|
| 12 |
+
// 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
|
| 13 |
+
// per-axis workgroup fold width (the dispatch caps x and spills into y).
|
| 14 |
{% elif note == "element-limit" %}
|
| 15 |
// 2D-folded flat element index: gid.y carries the high bits past the
|
| 16 |
+
// dispatch's per-axis workgroup fold width.
|
| 17 |
{% elif note == "dispatch" %}
|
| 18 |
+
// 2D-folded flat index: gid.y carries the high bits past the dispatch's
|
| 19 |
+
// per-axis workgroup fold width.
|
| 20 |
{% endif %}
|
| 21 |
{% if bound == "" %}
|
| 22 |
+
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 23 |
{%- elif guardInline %}
|
| 24 |
+
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 25 |
if ({{ name }} >= {{ bound }}) { return; }
|
| 26 |
{%- else %}
|
| 27 |
+
let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
|
| 28 |
if ({{ name }} >= {{ bound }}) {
|
| 29 |
return;
|
| 30 |
}
|
|
|
|
| 36 |
// One invocation owns both outputs of a rotated pair, so x/cos/sin are read once.
|
| 37 |
// Partial rotations use the remaining pair lanes to copy two unchanged tail values.
|
| 38 |
@compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
|
| 39 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 40 |
{{ flat_index_2d("p", "params.pairCount", note="") }}
|
| 41 |
|
| 42 |
let tasksPerHead = params.headSize / 2u;
|
|
|
|
| 52 |
}
|
| 53 |
|
| 54 |
let pair = task;
|
| 55 |
+
{% if rank == 3 %}
|
| 56 |
let token = (headFlat / params.numHeads) % params.sequenceLength;
|
| 57 |
let batch = headFlat / (params.numHeads * params.sequenceLength);
|
| 58 |
{% else %}
|
| 59 |
let token = headFlat % params.sequenceLength;
|
| 60 |
let batch = (headFlat / params.sequenceLength) / params.numHeads;
|
| 61 |
{% endif %}
|
| 62 |
+
{% if hasPositionIds is defined and hasPositionIds %}
|
| 63 |
// Gather the 2D cache tables through this token's position index.
|
| 64 |
let cache = u32(position_ids[batch * params.sequenceLength + token]) * params.halfRotaryDim + pair;
|
| 65 |
{% else %}
|
build/webgpu/test.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
{
|
| 2 |
-
"op": "ai.onnx.RotaryEmbedding",
|
| 3 |
"fixtureArrays": {
|
| 4 |
"position_id_cos_table": [1, 0.994, 0.9553, 0.9171, 0.8253, 0.7584, 0.6216, 0.5319, 0.3624, 0.2579, 0.0707, -0.0392, -0.2272, -0.3327, -0.5048, -0.5966, -0.7374, -0.8071, -0.9041, -0.9455, -0.99, -0.9995, -0.9875, -0.9642, -0.8968, -0.8428, -0.7259, -0.646, -0.4903, -0.3916, -0.2108, -0.1022],
|
| 5 |
"position_id_sin_table": [0, 0.1692, 0.2955, 0.4529, 0.5646, 0.6961, 0.7833, 0.8772, 0.932, 0.9799, 0.9975, 0.9951, 0.9738, 0.9214, 0.8632, 0.7654, 0.6755, 0.541, 0.4274, 0.2683, 0.1411, -0.0284, -0.1577, -0.3225, -0.4425, -0.5879, -0.6878, -0.8007, -0.8716, -0.942, -0.9775, -0.9991],
|
|
@@ -34,7 +33,7 @@
|
|
| 34 |
"provenance": {
|
| 35 |
"source": "onnxruntime/test/providers/cpu/llm/rotary_embedding_op_test.cc",
|
| 36 |
"test": "RotaryEmbedding",
|
| 37 |
-
"notes": "
|
| 38 |
},
|
| 39 |
"inputs": {
|
| 40 |
"x": { "dtype": "float32", "shape": [1, 1, 1, 2], "data": { "kind": "values", "values": [1e-40, -1e-40] } },
|
|
@@ -628,7 +627,7 @@
|
|
| 628 |
{
|
| 629 |
"name": "dispatch_fold_rank4_pair_last_row_seq65540",
|
| 630 |
"provenance": {
|
| 631 |
-
"notes": "
|
| 632 |
},
|
| 633 |
"inputs": {
|
| 634 |
"x": {
|
|
@@ -825,6 +824,112 @@
|
|
| 825 |
"test": "RotaryEmbedding position_ids gather (opset-23 input 3)",
|
| 826 |
"notes": "A standard [1,1] position table selects cache row 9 for the single decode token; both heads use that row."
|
| 827 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 828 |
}
|
| 829 |
]
|
| 830 |
}
|
|
|
|
| 1 |
{
|
|
|
|
| 2 |
"fixtureArrays": {
|
| 3 |
"position_id_cos_table": [1, 0.994, 0.9553, 0.9171, 0.8253, 0.7584, 0.6216, 0.5319, 0.3624, 0.2579, 0.0707, -0.0392, -0.2272, -0.3327, -0.5048, -0.5966, -0.7374, -0.8071, -0.9041, -0.9455, -0.99, -0.9995, -0.9875, -0.9642, -0.8968, -0.8428, -0.7259, -0.646, -0.4903, -0.3916, -0.2108, -0.1022],
|
| 4 |
"position_id_sin_table": [0, 0.1692, 0.2955, 0.4529, 0.5646, 0.6961, 0.7833, 0.8772, 0.932, 0.9799, 0.9975, 0.9951, 0.9738, 0.9214, 0.8632, 0.7654, 0.6755, 0.541, 0.4274, 0.2683, 0.1411, -0.0284, -0.1577, -0.3225, -0.4425, -0.5879, -0.6878, -0.8007, -0.8716, -0.942, -0.9775, -0.9991],
|
|
|
|
| 33 |
"provenance": {
|
| 34 |
"source": "onnxruntime/test/providers/cpu/llm/rotary_embedding_op_test.cc",
|
| 35 |
"test": "RotaryEmbedding",
|
| 36 |
+
"notes": "A rank-4 identity-angle rotary embedding must preserve subnormal activations."
|
| 37 |
},
|
| 38 |
"inputs": {
|
| 39 |
"x": { "dtype": "float32", "shape": [1, 1, 1, 2], "data": { "kind": "values", "values": [1e-40, -1e-40] } },
|
|
|
|
| 627 |
{
|
| 628 |
"name": "dispatch_fold_rank4_pair_last_row_seq65540",
|
| 629 |
"provenance": {
|
| 630 |
+
"notes": "Shape [1,1,65540,512] requires 65,540 workgroups for paired rotation, so dispatch folds into two dimensions. The highest pair indices must still be written with the correct periodic values."
|
| 631 |
},
|
| 632 |
"inputs": {
|
| 633 |
"x": {
|
|
|
|
| 824 |
"test": "RotaryEmbedding position_ids gather (opset-23 input 3)",
|
| 825 |
"notes": "A standard [1,1] position table selects cache row 9 for the single decode token; both heads use that row."
|
| 826 |
}
|
| 827 |
+
},
|
| 828 |
+
{
|
| 829 |
+
"name": "quad_interleaved_rank3_head16_multihead_f16",
|
| 830 |
+
"provenance": {
|
| 831 |
+
"notes": "Interleaved rotation on the quad route: head size 16 over 2 heads with f16 storage, so each invocation rotates four interleaved pairs read as two vec4s and writes them back in the same interleaving."
|
| 832 |
+
},
|
| 833 |
+
"attrs": { "interleaved": 1, "num_heads": 2 },
|
| 834 |
+
"inputs": {
|
| 835 |
+
"x": {
|
| 836 |
+
"dtype": "float16",
|
| 837 |
+
"shape": [1, 3, 32],
|
| 838 |
+
"data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.13, "cosStep": 0.21 }
|
| 839 |
+
},
|
| 840 |
+
"cos": {
|
| 841 |
+
"dtype": "float16",
|
| 842 |
+
"shape": [1, 3, 8],
|
| 843 |
+
"data": { "kind": "rotaryCos", "thetaStart": 0.05, "thetaStep": 0.07 }
|
| 844 |
+
},
|
| 845 |
+
"sin": {
|
| 846 |
+
"dtype": "float16",
|
| 847 |
+
"shape": [1, 3, 8],
|
| 848 |
+
"data": { "kind": "rotarySin", "thetaStart": 0.05, "thetaStep": 0.07 }
|
| 849 |
+
}
|
| 850 |
+
},
|
| 851 |
+
"outputs": { "y": { "dtype": "float16", "shape": [1, 3, 32], "tolerance": 0.002 } }
|
| 852 |
+
},
|
| 853 |
+
{
|
| 854 |
+
"name": "quad_partial_rotary_tail_rank4_head24_rd8",
|
| 855 |
+
"provenance": {
|
| 856 |
+
"notes": "Partial rotation on the quad route: rotary dim 8 of head size 24 leaves a 16-element tail that the remaining quad lanes copy as two vec4s each, with a batch-strided cache."
|
| 857 |
+
},
|
| 858 |
+
"attrs": { "rotary_embedding_dim": 8 },
|
| 859 |
+
"inputs": {
|
| 860 |
+
"x": {
|
| 861 |
+
"dtype": "float32",
|
| 862 |
+
"shape": [2, 2, 3, 24],
|
| 863 |
+
"data": { "kind": "fillFloat32", "scale": 0.45, "sinStep": 0.19, "cosStep": 0.11 }
|
| 864 |
+
},
|
| 865 |
+
"cos": {
|
| 866 |
+
"dtype": "float32",
|
| 867 |
+
"shape": [2, 3, 4],
|
| 868 |
+
"data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.31, "cosStep": 0.17 }
|
| 869 |
+
},
|
| 870 |
+
"sin": {
|
| 871 |
+
"dtype": "float32",
|
| 872 |
+
"shape": [2, 3, 4],
|
| 873 |
+
"data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.23, "cosStep": 0.29 }
|
| 874 |
+
}
|
| 875 |
+
},
|
| 876 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 2, 3, 24], "tolerance": 0.000001 } }
|
| 877 |
+
},
|
| 878 |
+
{
|
| 879 |
+
"name": "quad_pos_ids_rank3_head16_gather",
|
| 880 |
+
"provenance": {
|
| 881 |
+
"notes": "Position-id gather on the quad route: 2D cache tables of half width 8 are gathered per (batch, token) as vec4 rows, head size 16 over 2 heads."
|
| 882 |
+
},
|
| 883 |
+
"attrs": { "num_heads": 2 },
|
| 884 |
+
"inputs": {
|
| 885 |
+
"x": {
|
| 886 |
+
"dtype": "float32",
|
| 887 |
+
"shape": [2, 3, 32],
|
| 888 |
+
"data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.23 }
|
| 889 |
+
},
|
| 890 |
+
"cos": {
|
| 891 |
+
"dtype": "float32",
|
| 892 |
+
"shape": [12, 8],
|
| 893 |
+
"data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.27, "cosStep": 0.13 }
|
| 894 |
+
},
|
| 895 |
+
"sin": {
|
| 896 |
+
"dtype": "float32",
|
| 897 |
+
"shape": [12, 8],
|
| 898 |
+
"data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.19, "cosStep": 0.31 }
|
| 899 |
+
},
|
| 900 |
+
"positionIds": {
|
| 901 |
+
"dtype": "uint32",
|
| 902 |
+
"shape": [2, 3],
|
| 903 |
+
"data": { "kind": "values", "values": [4, 5, 11, 0, 2, 9] }
|
| 904 |
+
}
|
| 905 |
+
},
|
| 906 |
+
"outputs": { "y": { "dtype": "float32", "shape": [2, 3, 32], "tolerance": 0.0001 } }
|
| 907 |
+
},
|
| 908 |
+
{
|
| 909 |
+
"name": "quad_pos_ids_rank4_interleaved_head8",
|
| 910 |
+
"provenance": {
|
| 911 |
+
"notes": "Position-id gather with interleaved rotation on the rank-4 quad route: head size 8 is exactly one quad per head, so the rotate lanes cover every element and the tail branch never runs."
|
| 912 |
+
},
|
| 913 |
+
"attrs": { "interleaved": 1 },
|
| 914 |
+
"inputs": {
|
| 915 |
+
"x": {
|
| 916 |
+
"dtype": "float32",
|
| 917 |
+
"shape": [1, 2, 3, 8],
|
| 918 |
+
"data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.11, "cosStep": 0.29 }
|
| 919 |
+
},
|
| 920 |
+
"cos": {
|
| 921 |
+
"dtype": "float32",
|
| 922 |
+
"shape": [8, 4],
|
| 923 |
+
"data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.33, "cosStep": 0.21 }
|
| 924 |
+
},
|
| 925 |
+
"sin": {
|
| 926 |
+
"dtype": "float32",
|
| 927 |
+
"shape": [8, 4],
|
| 928 |
+
"data": { "kind": "fillFloat32", "scale": 1.0, "sinStep": 0.15, "cosStep": 0.37 }
|
| 929 |
+
},
|
| 930 |
+
"positionIds": { "dtype": "uint32", "shape": [1, 3], "data": { "kind": "values", "values": [7, 0, 3] } }
|
| 931 |
+
},
|
| 932 |
+
"outputs": { "y": { "dtype": "float32", "shape": [1, 2, 3, 8], "tolerance": 0.0001 } }
|
| 933 |
}
|
| 934 |
]
|
| 935 |
}
|