Xenova HF Staff commited on
Commit
cc4bd23
·
verified ·
1 Parent(s): bbbd4e6

sync 2e7068faf55e

Browse files
README.md CHANGED
@@ -1,3 +1,75 @@
1
  ---
 
2
  license: apache-2.0
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ library_name: kernels
3
  license: apache-2.0
4
+ tags:
5
+ - kernel
6
+ - webgpu
7
+ - wgsl
8
  ---
9
+ # com.microsoft.GemmaRotaryEmbedding
10
+
11
+ `com.microsoft` · ONNX Runtime contrib operator · contrib since_version 1
12
+
13
+ ## Description
14
+
15
+ Fuses the Gemma rotary-embedding tail: computes `sin` and `cos` from float32 `emb`, casts them to float16, then evaluates `q * cos + q_rot * sin` and the corresponding expression for `k`. `emb` has shape `(batch, seq, dim)` and is broadcast over the head axis of the `(batch, heads, seq, dim)` operands. Each product is rounded to float16 before the addition.
16
+
17
+ See the [ONNX Runtime `GemmaRotaryEmbedding` contrib-operator spec](https://github.com/microsoft/onnxruntime/blob/main/docs/ContribOperators.md#com.microsoft.GemmaRotaryEmbedding) for the reference semantics.
18
+
19
+ ## Inputs
20
+
21
+ | Name | Bind key | Logical dtype | WebGPU storage | Rank | Shape | Description | Presence |
22
+ | --- | --- | --- | --- | --- | --- | --- | --- |
23
+ | `emb` | `embT` | `U` | `float32` | `3` | — | Rotary angles with shape `(batch_size, seq_len, dim)`, shared by every head. | required |
24
+ | `q` | `qT` | `T` | same as logical dtype | `4` | — | Query state with shape `(batch_size, num_heads, seq_len, dim)`. | required |
25
+ | `q_rot` | `qRotT` | `T` | same as logical dtype | `4` | — | Half-rotated query state, same shape as `q`. | required |
26
+ | `k` | `kT` | `T` | same as logical dtype | `4` | — | Key state, same shape as `q`. | required |
27
+ | `k_rot` | `kRotT` | `T` | same as logical dtype | `4` | — | Half-rotated key state, same shape as `q`. | required |
28
+
29
+ ## Outputs
30
+
31
+ | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
32
+ | --- | --- | --- | --- | --- | --- | --- |
33
+ | `output1` | `output1T` | `T` | same as `q` | same as `q` | Rotary-embedded query, same shape as `q`. | required |
34
+ | `output2` | `output2T` | `T` | same as `q` | same as `q` | Rotary-embedded key, same shape as `q`. | required |
35
+
36
+ ## Type constraints
37
+
38
+ | Variable | Allowed dtypes |
39
+ | --- | --- |
40
+ | `T` | `float16` |
41
+ | `U` | `float32` |
42
+
43
+ ## Device requirements
44
+
45
+ Every implementation variant requires `shader-f16`; the package has no variant-level fallback without that capability.
46
+
47
+ ## Files
48
+
49
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
50
+ - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
51
+ - [`test.json`](build/webgpu/test.json) — correctness cases
52
+ - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
53
+ - [`gemma-rotary-embedding.wgsl.jinja`](build/webgpu/gemma-rotary-embedding.wgsl.jinja)
54
+
55
+ ## Use with `@huggingface/kernels`
56
+
57
+ The loader derives every required output's shape and logical dtype from the manifest contract and this call.
58
+ It then allocates the result tensors automatically.
59
+
60
+ The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
61
+
62
+ Replace each `*Data` placeholder with a typed array containing the corresponding input data.
63
+
64
+ ```js
65
+ import { getKernel } from "@huggingface/kernels";
66
+
67
+ const kernel = await getKernel("webgpu-kernels/com.microsoft.GemmaRotaryEmbedding", { version: 1 });
68
+ const { output1T, output2T } = await kernel({
69
+ embT: { data: embTData, shape: [1, 2, 4] },
70
+ qT: { data: qTData, shape: [1, 1, 2, 4] },
71
+ qRotT: { data: qRotTData, shape: [1, 1, 2, 4] },
72
+ kT: { data: kTData, shape: [1, 1, 2, 4] },
73
+ kRotT: { data: kRotTData, shape: [1, 1, 2, 4] },
74
+ });
75
+ ```
build/webgpu/bench.json ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "com.microsoft.GemmaRotaryEmbedding",
3
+ "tunableSpace": { "workgroupSize": [64, 128, 256] },
4
+ "cases": [
5
+ {
6
+ "name": "gemma-rotary-f16-b1-h16-s2048-d256",
7
+ "preset": "smoke",
8
+ "vars": { "dtype": "float16" },
9
+ "inputs": {
10
+ "embT": { "shape": [1, 2048, 256], "dtype": "float32", "dist": "normal", "seed": 8100, "scale": 2 },
11
+ "qT": { "shape": [1, 16, 2048, 256], "dtype": "float16", "dist": "normal", "seed": 8101, "scale": 1 },
12
+ "qRotT": { "shape": [1, 16, 2048, 256], "dtype": "float16", "dist": "normal", "seed": 8102, "scale": 1 },
13
+ "kT": { "shape": [1, 16, 2048, 256], "dtype": "float16", "dist": "normal", "seed": 8103, "scale": 1 },
14
+ "kRotT": { "shape": [1, 16, 2048, 256], "dtype": "float16", "dist": "normal", "seed": 8104, "scale": 1 }
15
+ },
16
+ "outputs": {
17
+ "output1T": { "shape": [1, 16, 2048, 256], "dtype": "float16" },
18
+ "output2T": { "shape": [1, 16, 2048, 256], "dtype": "float16" }
19
+ },
20
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "6 * 8388608 * dtypeBytes(args.dtype) + 524288 * 4" }] }
21
+ },
22
+ {
23
+ "name": "gemma-rotary-f16-b1-h16-s1-d256",
24
+ "preset": "smoke",
25
+ "vars": { "dtype": "float16" },
26
+ "inputs": {
27
+ "embT": { "shape": [1, 1, 256], "dtype": "float32", "dist": "normal", "seed": 8110, "scale": 2 },
28
+ "qT": { "shape": [1, 16, 1, 256], "dtype": "float16", "dist": "normal", "seed": 8111, "scale": 1 },
29
+ "qRotT": { "shape": [1, 16, 1, 256], "dtype": "float16", "dist": "normal", "seed": 8112, "scale": 1 },
30
+ "kT": { "shape": [1, 16, 1, 256], "dtype": "float16", "dist": "normal", "seed": 8113, "scale": 1 },
31
+ "kRotT": { "shape": [1, 16, 1, 256], "dtype": "float16", "dist": "normal", "seed": 8114, "scale": 1 }
32
+ },
33
+ "outputs": {
34
+ "output1T": { "shape": [1, 16, 1, 256], "dtype": "float16" },
35
+ "output2T": { "shape": [1, 16, 1, 256], "dtype": "float16" }
36
+ },
37
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "6 * 4096 * dtypeBytes(args.dtype) + 256 * 4" }] }
38
+ },
39
+ {
40
+ "name": "gemma-rotary-f16-b4-h8-s512-d256",
41
+ "preset": "model",
42
+ "vars": { "dtype": "float16" },
43
+ "inputs": {
44
+ "embT": { "shape": [4, 512, 256], "dtype": "float32", "dist": "normal", "seed": 8130, "scale": 2 },
45
+ "qT": { "shape": [4, 8, 512, 256], "dtype": "float16", "dist": "normal", "seed": 8131, "scale": 1 },
46
+ "qRotT": { "shape": [4, 8, 512, 256], "dtype": "float16", "dist": "normal", "seed": 8132, "scale": 1 },
47
+ "kT": { "shape": [4, 8, 512, 256], "dtype": "float16", "dist": "normal", "seed": 8133, "scale": 1 },
48
+ "kRotT": { "shape": [4, 8, 512, 256], "dtype": "float16", "dist": "normal", "seed": 8134, "scale": 1 }
49
+ },
50
+ "outputs": {
51
+ "output1T": { "shape": [4, 8, 512, 256], "dtype": "float16" },
52
+ "output2T": { "shape": [4, 8, 512, 256], "dtype": "float16" }
53
+ },
54
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "6 * 4194304 * dtypeBytes(args.dtype) + 524288 * 4" }] }
55
+ },
56
+ {
57
+ "name": "gemma-rotary-f16-b2-h16-s512-d250-scalar",
58
+ "preset": "model",
59
+ "vars": { "dtype": "float16" },
60
+ "inputs": {
61
+ "embT": { "shape": [2, 512, 250], "dtype": "float32", "dist": "normal", "seed": 8140, "scale": 2 },
62
+ "qT": { "shape": [2, 16, 512, 250], "dtype": "float16", "dist": "normal", "seed": 8141, "scale": 1 },
63
+ "qRotT": { "shape": [2, 16, 512, 250], "dtype": "float16", "dist": "normal", "seed": 8142, "scale": 1 },
64
+ "kT": { "shape": [2, 16, 512, 250], "dtype": "float16", "dist": "normal", "seed": 8143, "scale": 1 },
65
+ "kRotT": { "shape": [2, 16, 512, 250], "dtype": "float16", "dist": "normal", "seed": 8144, "scale": 1 }
66
+ },
67
+ "outputs": {
68
+ "output1T": { "shape": [2, 16, 512, 250], "dtype": "float16" },
69
+ "output2T": { "shape": [2, 16, 512, 250], "dtype": "float16" }
70
+ },
71
+ "bench": { "metrics": [{ "type": "bandwidth", "value": "6 * 4096000 * dtypeBytes(args.dtype) + 256000 * 4" }] }
72
+ }
73
+ ]
74
+ }
build/webgpu/gemma-rotary-embedding.wgsl.jinja ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ enable f16;
2
+ {{ env.wgsl.resourceDeclarations }}
3
+
4
+ // output1 = q * cos(emb) + q_rot * sin(emb); output2 applies the same
5
+ // rotation to K. The operator is elementwise over (batch, heads, seq, dim),
6
+ // while emb omits the head axis and is shared by every head.
7
+ const WG: u32 = {{ workgroupSize }}u;
8
+ {% if vec4 %}
9
+ const ZERO: {{ vector }} = {{ vector }}(0.0);
10
+ {% else %}
11
+ const ZERO: {{ scalar }} = {{ scalar }}(0.0);
12
+ {% endif %}
13
+
14
+ @compute @workgroup_size(WG, 1, 1)
15
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
16
+ @builtin(num_workgroups) nwg: vec3<u32>) {
17
+ // 2D-folded flat index: gid.y carries the high bits past the
18
+ // maxComputeWorkgroupsPerDimension limit. Reduces to gid.x when nwg.y == 1.
19
+ let index = gid.x + gid.y * nwg.x * WG;
20
+ if (index >= params.count) {
21
+ return;
22
+ }
23
+ // (batch, head, seq, dim) -> (batch, seq, dim): divide out the head-major stride and
24
+ // keep the remainder, which is exactly the (seq, dim) offset shared by every head.
25
+ let emb_index = (index / params.headSeqDim) * params.seqDim + index % params.seqDim;
26
+
27
+ // emb is float32 for every T. Compute sine and cosine at full width, then
28
+ // round them to T before multiplication as required by the operator graph.
29
+ let angle = emb[emb_index];
30
+ {% if vec4 %}
31
+ let sin_val = {{ vector }}(sin(angle));
32
+ let cos_val = {{ vector }}(cos(angle));
33
+ {% else %}
34
+ let sin_val = {{ scalar }}(sin(angle));
35
+ let cos_val = {{ scalar }}(cos(angle));
36
+ {% endif %}
37
+
38
+ // Round both products to T before addition. Explicit fma(..., 0) prevents a
39
+ // backend from contracting the final add and retaining wider intermediates.
40
+ output1[index] = fma(q[index], cos_val, ZERO) + fma(q_rot[index], sin_val, ZERO);
41
+ output2[index] = fma(k[index], cos_val, ZERO) + fma(k_rot[index], sin_val, ZERO);
42
+ }
build/webgpu/manifest.json ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "domain": "com.microsoft",
3
+ "name": "GemmaRotaryEmbedding",
4
+ "sinceVersion": 1,
5
+ "description": "Fuses the Gemma rotary-embedding tail: computes `sin` and `cos` from float32 `emb`, casts them to float16, then evaluates `q * cos + q_rot * sin` and the corresponding expression for `k`. `emb` has shape `(batch, seq, dim)` and is broadcast over the head axis of the `(batch, heads, seq, dim)` operands. Each product is rounded to float16 before the addition.",
6
+ "inputs": [
7
+ {
8
+ "role": "emb",
9
+ "dtype": "U",
10
+ "rank": 3,
11
+ "description": "Rotary angles with shape `(batch_size, seq_len, dim)`, shared by every head."
12
+ },
13
+ {
14
+ "role": "q",
15
+ "dtype": "T",
16
+ "rank": 4,
17
+ "description": "Query state with shape `(batch_size, num_heads, seq_len, dim)`."
18
+ },
19
+ { "role": "q_rot", "dtype": "T", "rank": 4, "description": "Half-rotated query state, same shape as `q`." },
20
+ { "role": "k", "dtype": "T", "rank": 4, "description": "Key state, same shape as `q`." },
21
+ { "role": "k_rot", "dtype": "T", "rank": 4, "description": "Half-rotated key state, same shape as `q`." }
22
+ ],
23
+ "outputs": [
24
+ {
25
+ "role": "output1",
26
+ "dtype": "T",
27
+ "rank": "ranks.qT",
28
+ "shape": "shapes.qT",
29
+ "description": "Rotary-embedded query, same shape as `q`."
30
+ },
31
+ {
32
+ "role": "output2",
33
+ "dtype": "T",
34
+ "rank": "ranks.qT",
35
+ "shape": "shapes.qT",
36
+ "description": "Rotary-embedded key, same shape as `q`."
37
+ }
38
+ ],
39
+ "typeConstraints": { "T": ["float16"], "U": ["float32"] },
40
+ "args": {
41
+ "embT": { "kind": "tensor", "semantic": "emb", "role": "input", "dtype": "float32" },
42
+ "qT": { "kind": "tensor", "semantic": "q", "role": "input" },
43
+ "qRotT": { "kind": "tensor", "semantic": "q_rot", "role": "input" },
44
+ "kT": { "kind": "tensor", "semantic": "k", "role": "input" },
45
+ "kRotT": { "kind": "tensor", "semantic": "k_rot", "role": "input" },
46
+ "output1T": { "kind": "tensor", "semantic": "output1", "role": "output" },
47
+ "output2T": { "kind": "tensor", "semantic": "output2", "role": "output" }
48
+ },
49
+ "tunables": { "workgroupSize": 256 },
50
+ "derive": {
51
+ "batchSize": "dim(shapes.qT, 0)",
52
+ "numHeads": "dim(shapes.qT, 1)",
53
+ "seqLen": "dim(shapes.qT, 2)",
54
+ "headDim": "dim(shapes.qT, 3)",
55
+ "sameAsQ": "ranks.qRotT == 4 and ranks.kT == 4 and ranks.kRotT == 4 and sameShape(shapes.qRotT, shapes.qT) and sameShape(shapes.kT, shapes.qT) and sameShape(shapes.kRotT, shapes.qT)",
56
+ "dtypesMatch": "tensorDtypes.qRotT == tensorDtypes.qT and tensorDtypes.kT == tensorDtypes.qT and tensorDtypes.kRotT == tensorDtypes.qT and tensorDtypes.output1T == tensorDtypes.qT and tensorDtypes.output2T == tensorDtypes.qT",
57
+ "contract": "ranks.qT == 4 and ranks.embT == 3 and sameAsQ and dtypesMatch and tensorDtypes.qT == \"float16\" and f16Ok(dtypes.T) and tensorDtypes.embT == \"float32\" and dim(shapes.embT, 0) == batchSize and dim(shapes.embT, 1) == seqLen and dim(shapes.embT, 2) == headDim and sameShape(shapes.output1T, shapes.qT) and sameShape(shapes.output2T, shapes.qT)",
58
+ "vec4Ok": "headDim % 4 == 0"
59
+ },
60
+ "bindingSets": {
61
+ "scalar": [
62
+ {
63
+ "name": "emb",
64
+ "arg": "embT",
65
+ "semantic": "emb",
66
+ "buffer": { "type": "read-only-storage" },
67
+ "elementType": "f32"
68
+ },
69
+ { "name": "q", "arg": "qT", "semantic": "q", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
70
+ {
71
+ "name": "q_rot",
72
+ "arg": "qRotT",
73
+ "semantic": "q_rot",
74
+ "buffer": { "type": "read-only-storage" },
75
+ "elementType": "$scalar"
76
+ },
77
+ { "name": "k", "arg": "kT", "semantic": "k", "buffer": { "type": "read-only-storage" }, "elementType": "$scalar" },
78
+ {
79
+ "name": "k_rot",
80
+ "arg": "kRotT",
81
+ "semantic": "k_rot",
82
+ "buffer": { "type": "read-only-storage" },
83
+ "elementType": "$scalar"
84
+ },
85
+ {
86
+ "name": "output1",
87
+ "arg": "output1T",
88
+ "semantic": "output1",
89
+ "buffer": { "type": "storage" },
90
+ "elementType": "$scalar"
91
+ },
92
+ {
93
+ "name": "output2",
94
+ "arg": "output2T",
95
+ "semantic": "output2",
96
+ "buffer": { "type": "storage" },
97
+ "elementType": "$scalar"
98
+ },
99
+ {
100
+ "name": "params",
101
+ "semantic": "kernel.params",
102
+ "buffer": { "type": "uniform" },
103
+ "struct": {
104
+ "name": "Params",
105
+ "fields": [
106
+ { "name": "count", "type": "u32", "value": "numel(shapes.qT)" },
107
+ { "name": "seqDim", "type": "u32", "value": "(seqLen * headDim)" },
108
+ { "name": "headSeqDim", "type": "u32", "value": "(numHeads * seqLen * headDim)" }
109
+ ]
110
+ }
111
+ }
112
+ ],
113
+ "vec4": [
114
+ {
115
+ "name": "emb",
116
+ "arg": "embT",
117
+ "semantic": "emb",
118
+ "buffer": { "type": "read-only-storage" },
119
+ "elementType": "vec4<f32>"
120
+ },
121
+ { "name": "q", "arg": "qT", "semantic": "q", "buffer": { "type": "read-only-storage" }, "elementType": "$vector" },
122
+ {
123
+ "name": "q_rot",
124
+ "arg": "qRotT",
125
+ "semantic": "q_rot",
126
+ "buffer": { "type": "read-only-storage" },
127
+ "elementType": "$vector"
128
+ },
129
+ { "name": "k", "arg": "kT", "semantic": "k", "buffer": { "type": "read-only-storage" }, "elementType": "$vector" },
130
+ {
131
+ "name": "k_rot",
132
+ "arg": "kRotT",
133
+ "semantic": "k_rot",
134
+ "buffer": { "type": "read-only-storage" },
135
+ "elementType": "$vector"
136
+ },
137
+ {
138
+ "name": "output1",
139
+ "arg": "output1T",
140
+ "semantic": "output1",
141
+ "buffer": { "type": "storage" },
142
+ "elementType": "$vector"
143
+ },
144
+ {
145
+ "name": "output2",
146
+ "arg": "output2T",
147
+ "semantic": "output2",
148
+ "buffer": { "type": "storage" },
149
+ "elementType": "$vector"
150
+ },
151
+ {
152
+ "name": "params",
153
+ "semantic": "kernel.params",
154
+ "buffer": { "type": "uniform" },
155
+ "struct": {
156
+ "name": "Params",
157
+ "fields": [
158
+ { "name": "count", "type": "u32", "value": "numel(shapes.qT) / 4" },
159
+ { "name": "seqDim", "type": "u32", "value": "(seqLen * headDim) / 4" },
160
+ { "name": "headSeqDim", "type": "u32", "value": "(numHeads * seqLen * headDim) / 4" }
161
+ ]
162
+ }
163
+ }
164
+ ]
165
+ },
166
+ "variants": [
167
+ {
168
+ "id": "vec4",
169
+ "priority": 10,
170
+ "requires": { "features": ["shader-f16"] },
171
+ "when": ["contract", "vec4Ok", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
172
+ "constants": {
173
+ "vec4": true,
174
+ "scalar": "dtypes.T",
175
+ "vector": "\"vec4<f16>\"",
176
+ "workgroupSize": "tunables.workgroupSize"
177
+ },
178
+ "passes": [
179
+ {
180
+ "id": "main",
181
+ "name": "GemmaRotaryEmbedding.Vec4",
182
+ "shader": "gemma-rotary-embedding.wgsl.jinja",
183
+ "bindings": "vec4",
184
+ "dispatch": { "threads": "numel(shapes.qT) / 4", "workgroupSize": "constants.workgroupSize" }
185
+ }
186
+ ]
187
+ },
188
+ {
189
+ "id": "scalar",
190
+ "priority": 0,
191
+ "requires": { "features": ["shader-f16"] },
192
+ "when": ["contract", "tunables.workgroupSize >= 1", "floor(tunables.workgroupSize) == tunables.workgroupSize", "tunables.workgroupSize <= device.limits.maxComputeInvocationsPerWorkgroup", "tunables.workgroupSize <= device.limits.maxComputeWorkgroupSizeX"],
193
+ "constants": {
194
+ "vec4": false,
195
+ "scalar": "dtypes.T",
196
+ "vector": "\"vec4<f16>\"",
197
+ "workgroupSize": "tunables.workgroupSize"
198
+ },
199
+ "passes": [
200
+ {
201
+ "id": "main",
202
+ "name": "GemmaRotaryEmbedding",
203
+ "shader": "gemma-rotary-embedding.wgsl.jinja",
204
+ "bindings": "scalar",
205
+ "dispatch": { "threads": "max(1, numel(shapes.qT))", "workgroupSize": "constants.workgroupSize" }
206
+ }
207
+ ]
208
+ }
209
+ ]
210
+ }
build/webgpu/metadata.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "com.microsoft.GemmaRotaryEmbedding",
3
+ "id": "_com_microsoft_gemmarotaryembedding_webgpu_c64044f",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "backend": { "type": "webgpu" },
7
+ "digest": {
8
+ "algorithm": "sha256",
9
+ "files": {
10
+ "bench.json": "/3JoilqqNUy3rMDPsbi0npsxk9oZgl6Db/2k+439H0c=",
11
+ "gemma-rotary-embedding.wgsl.jinja": "w+t56Q+FUBau7k0eWwrulSaGWFEKYch4aCunpG3sbBs=",
12
+ "manifest.json": "FK8aZrRWKnUBl9hUi7MrNAX7nE9/6pz89Wh/AL5kCDQ=",
13
+ "test.json": "KnP1fOvNgEvi3e9lrItvga2K6sLswyE5trl1BhX72Js="
14
+ }
15
+ },
16
+ "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
17
+ "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.GemmaRotaryEmbedding" }
18
+ }
build/webgpu/test.json ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "op": "com.microsoft.GemmaRotaryEmbedding",
3
+ "cases": [
4
+ {
5
+ "name": "f16_vec4",
6
+ "provenance": {
7
+ "notes": "float16 operands with a float32 emb, which is the type pair ONNX Runtime constrains this operator to. Sin and cos are computed at float32 and rounded once."
8
+ },
9
+ "inputs": {
10
+ "embT": {
11
+ "dtype": "float32",
12
+ "shape": [2, 4, 8],
13
+ "data": { "kind": "fillFloat32", "scale": 2.5, "sinStep": 0.23, "cosStep": 0.37 }
14
+ },
15
+ "qT": {
16
+ "dtype": "float16",
17
+ "shape": [2, 2, 4, 8],
18
+ "data": {
19
+ "kind": "fillFloat32",
20
+ "scale": 0.8,
21
+ "sinStep": 0.16999999999999998,
22
+ "cosStep": 0.29000000000000004
23
+ }
24
+ },
25
+ "qRotT": {
26
+ "dtype": "float16",
27
+ "shape": [2, 2, 4, 8],
28
+ "data": { "kind": "fillFloat32", "scale": 0.8, "sinStep": 0.43, "cosStep": 0.13 }
29
+ },
30
+ "kT": {
31
+ "dtype": "float16",
32
+ "shape": [2, 2, 4, 8],
33
+ "data": { "kind": "fillFloat32", "scale": 0.8, "sinStep": 0.35, "cosStep": 0.19 }
34
+ },
35
+ "kRotT": {
36
+ "dtype": "float16",
37
+ "shape": [2, 2, 4, 8],
38
+ "data": { "kind": "fillFloat32", "scale": 0.8, "sinStep": 0.47, "cosStep": 0.25 }
39
+ }
40
+ },
41
+ "outputs": {
42
+ "output1T": { "dtype": "float16", "shape": [2, 2, 4, 8], "tolerance": 0.005 },
43
+ "output2T": { "dtype": "float16", "shape": [2, 2, 4, 8], "tolerance": 0.005 }
44
+ }
45
+ },
46
+ {
47
+ "name": "f16_scalar",
48
+ "provenance": { "notes": "float16 on the scalar variant, dim 5." },
49
+ "inputs": {
50
+ "embT": {
51
+ "dtype": "float32",
52
+ "shape": [2, 3, 5],
53
+ "data": { "kind": "fillFloat32", "scale": 2.5, "sinStep": 0.24000000000000002, "cosStep": 0.38 }
54
+ },
55
+ "qT": {
56
+ "dtype": "float16",
57
+ "shape": [2, 2, 3, 5],
58
+ "data": { "kind": "fillFloat32", "scale": 0.8, "sinStep": 0.18, "cosStep": 0.30000000000000004 }
59
+ },
60
+ "qRotT": {
61
+ "dtype": "float16",
62
+ "shape": [2, 2, 3, 5],
63
+ "data": { "kind": "fillFloat32", "scale": 0.8, "sinStep": 0.44, "cosStep": 0.14 }
64
+ },
65
+ "kT": {
66
+ "dtype": "float16",
67
+ "shape": [2, 2, 3, 5],
68
+ "data": { "kind": "fillFloat32", "scale": 0.8, "sinStep": 0.36, "cosStep": 0.2 }
69
+ },
70
+ "kRotT": {
71
+ "dtype": "float16",
72
+ "shape": [2, 2, 3, 5],
73
+ "data": { "kind": "fillFloat32", "scale": 0.8, "sinStep": 0.48, "cosStep": 0.26 }
74
+ }
75
+ },
76
+ "outputs": {
77
+ "output1T": { "dtype": "float16", "shape": [2, 2, 3, 5], "tolerance": 0.005 },
78
+ "output2T": { "dtype": "float16", "shape": [2, 2, 3, 5], "tolerance": 0.005 }
79
+ }
80
+ },
81
+ {
82
+ "name": "pinned_f16_round_before_add",
83
+ "provenance": {
84
+ "notes": "Pins the round-to-T-before-add rule at float16. Every element is a near-cancellation: the two products are around 100 in magnitude and their sum is under 1, so rounding each product to float16 before the add moves the result by 150 to 900 ulps, while a backend that contracted the multiply into an fma and kept a float32 product would land somewhere else entirely. The angles were chosen so their float16 sin and cos survive several ulps of perturbation, so this does not test the GPU's transcendental."
85
+ },
86
+ "inputs": {
87
+ "embT": {
88
+ "dtype": "float32",
89
+ "shape": [1, 2, 4],
90
+ "data": {
91
+ "kind": "values",
92
+ "values": [0.896741, -0.7510284, 2.9392502, 2.4113605, 2.7308347, -2.6883833, -0.7908616, -0.4171633]
93
+ }
94
+ },
95
+ "qT": {
96
+ "dtype": "float16",
97
+ "shape": [1, 1, 2, 4],
98
+ "data": { "kind": "values", "values": [-105.625, 248.375, 232.875, 109.75, 214.75, 205.75, -98.5625, -142.0] }
99
+ },
100
+ "qRotT": {
101
+ "dtype": "float16",
102
+ "shape": [1, 1, 2, 4],
103
+ "data": { "kind": "values", "values": [84.4375, 266.0, 1135.0, 122.25, 493.25, -421.0, -98.0625, -322.25] }
104
+ },
105
+ "kT": {
106
+ "dtype": "float16",
107
+ "shape": [1, 1, 2, 4],
108
+ "data": {
109
+ "kind": "values",
110
+ "values": [-239.625, 217.875, 249.125, -202.125, -127.8125, -135.75, 118.4375, 208.75]
111
+ }
112
+ },
113
+ "kRotT": {
114
+ "dtype": "float16",
115
+ "shape": [1, 1, 2, 4],
116
+ "data": { "kind": "values", "values": [-165.25, 194.25, -411.75, 281.25, 252.375, 151.25, -194.0, -455.5] }
117
+ }
118
+ },
119
+ "outputs": {
120
+ "output1T": {
121
+ "dtype": "float16",
122
+ "shape": [1, 1, 2, 4],
123
+ "data": { "kind": "values", "values": [0.0, -0.125, -0.125, -0.1875, 0.125, -0.75, 0.375, 0.875] },
124
+ "tolerance": 0.005
125
+ },
126
+ "output2T": {
127
+ "dtype": "float16",
128
+ "shape": [1, 1, 2, 4],
129
+ "data": { "kind": "values", "values": [-278.5, 26.625, -326.75, 338.25, 218.0, 55.8125, 221.125, 375.5] },
130
+ "tolerance": 0.005
131
+ }
132
+ }
133
+ },
134
+ {
135
+ "name": "pinned_batch2_emb_broadcast",
136
+ "provenance": {
137
+ "notes": "Hand-computed by an independent model that Unsqueezes emb to (batch, 1, seq, dim) and broadcasts with real array shapes, never forming a flat index -- which is the step ONNX Runtime's own unit test gets wrong: it indexes emb by num_heads where the CUDA kernel uses seq_len, and its only case has batch_size 1, where the wrong term is multiplied by zero. heads (3) and seq_len (2) differ here and batch is 2, so the two disagree."
138
+ },
139
+ "inputs": {
140
+ "embT": {
141
+ "dtype": "float32",
142
+ "shape": [2, 2, 4],
143
+ "data": {
144
+ "kind": "values",
145
+ "values": [-2.75, -2.383333, -2.016667, -1.65, -1.283333, -0.916667, -0.55, -0.183333, 0.183333, 0.55, 0.916667, 1.283333, 1.65, 2.016667, 2.383333, 2.75]
146
+ }
147
+ },
148
+ "qT": {
149
+ "dtype": "float16",
150
+ "shape": [2, 3, 2, 4],
151
+ "data": {
152
+ "kind": "values",
153
+ "values": [0.686035, -0.222046, -0.056, -0.728027, 0.777832, -0.014, 0.219971, -0.970215, 0.512207, -0.260986, -0.564941, 0.10498, -0.898926, -0.805176, -0.737793, 0.758789, 0.099976, -0.890137, 0.366943, 0.452881, -0.427979, 0.220947, -0.284912, -0.829102, 0.36499, 0.771973, 0.406006, 0.899902, 0.604004, 0.124023, 0.341064, 0.088013, 0.03299, -0.908203, 0.726074, 0.077026, -0.650879, -0.657227, 0.290039, 0.905762, 0.600098, -0.36499, -0.568848, 0.399902, -0.366943, 0.542969, 0.333984, -0.650879]
154
+ }
155
+ },
156
+ "qRotT": {
157
+ "dtype": "float16",
158
+ "shape": [2, 3, 2, 4],
159
+ "data": {
160
+ "kind": "values",
161
+ "values": [0.368896, 0.120972, 0.058014, 0.370117, -0.188965, 0.052002, 0.64209, -0.974121, -0.845215, -0.566895, 0.748047, 0.915039, -0.520996, -0.717773, 0.74707, -0.085022, -0.570801, 0.869141, 0.112976, -0.671875, -0.346924, -0.794922, -0.644043, -0.182007, 0.862793, -0.569824, -0.189941, -0.253906, -0.791016, -0.698242, 0.540039, 0.003, -0.426025, 0.733887, -0.112, -0.364014, -0.724121, 0.167969, -0.540039, 0.088989, 0.011002, 0.203003, -0.213989, -0.292969, -0.151001, 0.895996, 0.682129, 0.910156]
162
+ }
163
+ },
164
+ "kT": {
165
+ "dtype": "float16",
166
+ "shape": [2, 3, 2, 4],
167
+ "data": {
168
+ "kind": "values",
169
+ "values": [-0.395996, -0.052002, 0.120972, -0.955078, -0.782227, 0.232056, -0.702148, 0.06897, 0.581055, 0.923828, -0.251953, 0.258057, -0.813965, -0.799805, 0.491943, -0.418945, 0.583008, -0.597168, 0.333008, 0.731934, 0.852051, -0.009003, -0.476074, 0.206055, -0.527832, 0.714844, 0.948242, -0.370117, 0.729004, 0.481934, -0.527832, -0.467041, -0.711914, 0.515137, 0.551758, -0.099976, -0.858887, -0.544922, 0.580078, 0.621094, -0.52002, -0.331055, 0.326904, -0.25293, 0.430908, -0.585938, -0.588867, -0.177002]
170
+ }
171
+ },
172
+ "kRotT": {
173
+ "dtype": "float16",
174
+ "shape": [2, 3, 2, 4],
175
+ "data": {
176
+ "kind": "values",
177
+ "values": [0.106018, 0.551758, 0.952148, -0.405029, 0.814941, -0.312012, -0.819824, 0.955078, -0.234009, 0.085022, 0.008003, 0.471924, 0.798828, -0.641113, 0.679199, -0.859863, -0.974121, -0.881836, -0.122986, -0.63623, 0.18396, 0.156006, 0.094971, 0.619141, -0.366943, 0.041992, 0.945801, -0.298096, 0.036011, 0.471924, 0.223999, 0.214966, 0.890137, 0.748047, 0.523926, 0.665039, 0.189941, 0.49292, -0.488037, -0.437012, 0.37207, -0.284912, 0.765137, 0.020996, -0.462891, -0.111023, -0.63916, 0.460938]
178
+ }
179
+ }
180
+ },
181
+ "outputs": {
182
+ "output1T": {
183
+ "dtype": "float16",
184
+ "shape": [2, 3, 2, 4],
185
+ "data": {
186
+ "kind": "values",
187
+ "values": [-0.774902, 0.078064, -0.028198, -0.311523, 0.401855, -0.049774, -0.147949, -0.776367, -0.150879, 0.579102, -0.431152, -0.920898, 0.244629, 0.07959, -1.019531, 0.761719, 0.125366, 0.048828, -0.260254, 0.634277, 0.211426, 0.765137, 0.093506, -0.782227, 0.516113, 0.360596, 0.096313, 0.011597, -0.836426, -0.683594, 0.123657, -0.0802, -0.045197, -0.390869, 0.352783, -0.327393, -0.670898, 0.435059, -0.582031, -0.803223, 0.592285, -0.2052, -0.516113, -0.167725, -0.12146, 0.574219, 0.22644, 0.949219]
188
+ },
189
+ "tolerance": 0.005
190
+ },
191
+ "output2T": {
192
+ "dtype": "float16",
193
+ "shape": [2, 3, 2, 4],
194
+ "data": {
195
+ "kind": "values",
196
+ "values": [0.325439, -0.341553, -0.911621, 0.479492, -1.003906, 0.388672, -0.17041, -0.106262, -0.447754, -0.729492, 0.10144, -0.490967, -0.99707, 0.022217, 0.064697, -0.255371, -0.167236, 1.040039, -0.032593, 0.576172, 0.065063, -0.129272, -0.455322, 0.089783, -0.585938, 0.631348, 1.328125, -0.390869, -0.021759, 0.218018, 0.537109, 0.513672, -0.538086, 0.830078, 0.751465, 0.609375, 0.257324, 0.679688, -0.756836, -0.741211, -0.443359, -0.431152, 0.805664, -0.051575, -0.495605, 0.152588, -0.011963, 0.339355]
197
+ },
198
+ "tolerance": 0.005
199
+ }
200
+ }
201
+ }
202
+ ]
203
+ }