Xenova HF Staff commited on
Commit
2fad9e5
·
verified ·
1 Parent(s): f96162c

sync c928d21e6cc1

Browse files
README.md CHANGED
@@ -75,7 +75,7 @@ Attributes and default values (overridable per request):
75
 
76
  ## Device requirements
77
 
78
- Some implementation variants require `shader-f16` and `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
79
 
80
  ## Files
81
 
@@ -89,6 +89,8 @@ Some implementation variants require `shader-f16` and `subgroups`. These are rou
89
  - [`attn-flash-online.wgsl.jinja`](build/webgpu/attn-flash-online.wgsl.jinja)
90
  - [`attn-flash-prefill-cluster.wgsl.jinja`](build/webgpu/attn-flash-prefill-cluster.wgsl.jinja)
91
  - [`attn-flash-q32-broadcast.wgsl.jinja`](build/webgpu/attn-flash-q32-broadcast.wgsl.jinja)
 
 
92
  - [`attn-online-scalar.wgsl.jinja`](build/webgpu/attn-online-scalar.wgsl.jinja)
93
  - [`gqa-attention.wgsl.jinja`](build/webgpu/gqa-attention.wgsl.jinja)
94
  - [`gqa-present.wgsl.jinja`](build/webgpu/gqa-present.wgsl.jinja)
 
75
 
76
  ## Device requirements
77
 
78
+ Some implementation variants require `subgroup-matrix`, `shader-f16`, and `subgroups`. These are route-specific capabilities, not package-wide requirements; availability also depends on the request shape and dtype.
79
 
80
  ## Files
81
 
 
89
  - [`attn-flash-online.wgsl.jinja`](build/webgpu/attn-flash-online.wgsl.jinja)
90
  - [`attn-flash-prefill-cluster.wgsl.jinja`](build/webgpu/attn-flash-prefill-cluster.wgsl.jinja)
91
  - [`attn-flash-q32-broadcast.wgsl.jinja`](build/webgpu/attn-flash-q32-broadcast.wgsl.jinja)
92
+ - [`attn-materialized-rowstats-combine-f32.wgsl.jinja`](build/webgpu/attn-materialized-rowstats-combine-f32.wgsl.jinja)
93
+ - [`attn-materialized-sgmat-f32.wgsl.jinja`](build/webgpu/attn-materialized-sgmat-f32.wgsl.jinja)
94
  - [`attn-online-scalar.wgsl.jinja`](build/webgpu/attn-online-scalar.wgsl.jinja)
95
  - [`gqa-attention.wgsl.jinja`](build/webgpu/gqa-attention.wgsl.jinja)
96
  - [`gqa-present.wgsl.jinja`](build/webgpu/gqa-present.wgsl.jinja)
build/webgpu/attn-materialized-rowstats-combine-f32.wgsl.jinja ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Folds the per-tile softmax constants the score pass emitted into one (m, d)
2
+ // pair per query row, for the apply pass to normalize with.
3
+ //
4
+ // The score pass publishes one partial per row, key tile, and subgroup column,
5
+ // so this pass can combine the constants without rescanning the materialized
6
+ // score matrix. It uses the same online merge rule as the row-local reduction.
7
+ //
8
+ // One thread per row. Each row's partials are contiguous along the row axis, so
9
+ // consecutive threads read consecutive pairs; the fold walks slots in index
10
+ // order, which is fixed for a given shape, so the result does not depend on how
11
+ // the GPU schedules anything.
12
+ // `maxOnly` means the producer published only a row max per slot because
13
+ // computing the denominator there would double the exp count. Fold maxima and
14
+ // leave the denominator to the apply pass, which sees every row element anyway.
15
+ {{ env.wgsl.resourceDeclarations }}
16
+
17
+ const SLOTS: u32 = {{ statSlots }}u;
18
+ const Q_SEQ: u32 = {{ statQuerySeq }}u;
19
+ const WG: u32 = {{ materializedRowStatsWg }}u;
20
+
21
+ // FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
22
+ // `m - m` finite so an empty lane / all--inf row contributes the exact
23
+ // accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
24
+ // a zero final denominator according to their public semantics. Using -inf
25
+ // here changes +inf-row behavior.
26
+ const FLT_MAX: f32 = 3.4028234663852886e38;
27
+
28
+ fn is_finite_f32(value: f32) -> bool {
29
+ return select(false, value <= FLT_MAX, value >= -FLT_MAX);
30
+ }
31
+
32
+ // x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
33
+ // exactly at the row max. `x - x` on an infinite max is a legal fast-math
34
+ // fold to 0, which would silently turn +inf rows finite — the explicit
35
+ // equality test keeps the NaN propagation of the serial kernels.
36
+ fn shifted_value(value: f32, maxValue: f32) -> f32 {
37
+ let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
38
+ return select(value - maxValue, 0.0, equalFiniteMax);
39
+ }
40
+ fn exp_shift(value: f32, maxValue: f32) -> f32 {
41
+ return exp(shifted_value(value, maxValue));
42
+ }
43
+
44
+ @compute @workgroup_size(WG, 1, 1)
45
+ fn main(
46
+ @builtin(global_invocation_id) gid: vec3<u32>,
47
+ @builtin(num_workgroups) nwg: vec3<u32>
48
+ ) {
49
+ let row = gid.x + gid.y * nwg.x * WG;
50
+ if (row >= params.rows) { return; }
51
+
52
+ // `row` already runs over (batch, head, query) together, and the partial
53
+ // layout puts that same product one axis out from the slot, so the stride
54
+ // between a row's slots is the number of rows in its (batch, head) plane.
55
+ let plane = row / Q_SEQ;
56
+ let inPlane = row % Q_SEQ;
57
+ let base = ((plane * SLOTS) * Q_SEQ + inPlane) * 2u;
58
+
59
+ var m = -FLT_MAX;
60
+ var d = 0.0;
61
+ for (var slot = 0u; slot < SLOTS; slot = slot + 1u) {
62
+ let index = base + slot * Q_SEQ * 2u;
63
+ let slotM = scorePartials[index];
64
+ let slotD = scorePartials[index + 1u];
65
+ let merged = max(m, slotM);
66
+ d = d * exp_shift(m, merged) + slotD * exp_shift(slotM, merged);
67
+ m = merged;
68
+ }
69
+
70
+ rowStats[row * 2u] = m;
71
+ rowStats[row * 2u + 1u] = d;
72
+ }
build/webgpu/attn-materialized-sgmat-f32.wgsl.jinja ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% set MT = "f16" if (operandF16 is defined and operandF16) else "f32" %}
2
+ {% if MT == "f16" %}
3
+ enable f16;
4
+ {% endif %}
5
+ enable subgroups;
6
+ {% if pinSubgroupSize32 %}
7
+ enable subgroup_size_control;
8
+ {% endif %}
9
+ enable chromium_experimental_subgroup_matrix;
10
+ diagnostic(off, chromium.subgroup_matrix_uniformity);
11
+
12
+ {{ env.wgsl.resourceDeclarations }}
13
+
14
+ {% set layout = source.layout | default("bsh") %}
15
+ {% set headMajor = layout == "bhsd" %}
16
+ {% set kvHeadMajor = (source.kvLayout | default(layout)) == "bhsd" %}
17
+ {% set CAUSAL_UPPER_LEFT = source.causalUpperLeft is defined and source.causalUpperLeft %}
18
+ {% set CAUSAL = (source.causalRightAlign is defined and source.causalRightAlign) or CAUSAL_UPPER_LEFT %}
19
+ {% macro q_index(row, d) %}(b * params.qSeq + {{ row }}) * HIDDEN + h * HEAD_DIM + {{ d }}{% endmacro %}
20
+ {% macro kv_index(seq, d) %}{% if kvHeadMajor %}((b * KV_HEADS + h_kv) * params.kvSeq + {{ seq }}) * HEAD_DIM + {{ d }}{% else %}(b * params.kvSeq + {{ seq }}) * KV_HIDDEN + h_kv * HEAD_DIM + {{ d }}{% endif %}{% endmacro %}
21
+ {% set OUT_ROW_STRIDE = "HEAD_DIM" if headMajor else "HIDDEN" %}
22
+ {% set KV_ROW_STRIDE = "HEAD_DIM" if kvHeadMajor else "KV_HIDDEN" %}
23
+ {% set scorePhase = source.phase == "score" %}
24
+ {% set SCORE_BIAS = scorePhase and source.scoreBias is defined and source.scoreBias %}
25
+ {% set SCORE_WINDOW = CAUSAL and source.scoreWindow is defined and source.scoreWindow %}
26
+ {% set USE_SEQLENS = source.useSeqlens is defined and source.useSeqlens %}
27
+ {% set FUSED_SOFTMAX = source.fusedSoftmax is defined and source.fusedSoftmax %}
28
+ {% set PRIVATE_ROW_STATS = FUSED_SOFTMAX and (materializedSgmatPrivateRowStats is defined and materializedSgmatPrivateRowStats) %}
29
+ {% macro score_value(index, guard) %}
30
+ {% if FUSED_SOFTMAX %}
31
+ {% if PRIVATE_ROW_STATS %}
32
+ select(0.0, exp_shift(scores[{{ index }}], private_softmax_m) / private_softmax_d, {{ guard[1] }})
33
+ {%- else %}
34
+ select(0.0, exp_shift(scores[{{ index }}], softmax_m[{{ guard[0] }}]) / softmax_d[{{ guard[0] }}], {{ guard[1] }})
35
+ {%- endif %}
36
+ {% else %}
37
+ select(0.0, scores[{{ index }}], {{ guard[1] }})
38
+ {%- endif %}
39
+ {% endmacro %}
40
+ {% set TILE_M_VALUE = materializedSgmatQueryTile %}
41
+ {% set TILE_N_VALUE = materializedSgmatKeyTile %}
42
+ {% set TILE_K_VALUE = materializedSgmatInnerTile %}
43
+ {% set SUB_ROWS_VALUE = materializedSgmatSubgroupTileRows if materializedSgmatSubgroupTileRows is defined else 16 %}
44
+ {% set SUB_COLS_VALUE = materializedSgmatSubgroupTileCols if materializedSgmatSubgroupTileCols is defined else 32 %}
45
+ {% set ROW_BLOCKS = (SUB_ROWS_VALUE / 8)|int %}
46
+ {% set COL_BLOCKS = (SUB_COLS_VALUE / 8)|int %}
47
+ {% set SUBGROUP_ROWS = (TILE_M_VALUE / SUB_ROWS_VALUE)|int %}
48
+ {% set SUBGROUP_COLS = (TILE_N_VALUE / SUB_COLS_VALUE)|int %}
49
+ {% set SUBGROUP_COUNT = SUBGROUP_ROWS * SUBGROUP_COLS %}
50
+ {% set WORKGROUP_THREADS = SUBGROUP_COUNT * 32 %}
51
+ {% if hasBias is not defined %}{% set hasBias = false %}{% endif %}
52
+ {% set EMIT_ROW_STATS = source.emitRowStats is defined and source.emitRowStats %}
53
+ {% set DIRECT_SCORE_STORE = materializedSgmatDirectScoreStore and not EMIT_ROW_STATS %}
54
+ {% set DIRECT_APPLY_STORE = materializedSgmatDirectApplyStore and not hasBias and MT == "f32" %}
55
+ {% set DIRECT_OUTPUT_STORE = DIRECT_SCORE_STORE if scorePhase else DIRECT_APPLY_STORE %}
56
+ {% set RUNTIME_DIRECT_STORE = (not DIRECT_OUTPUT_STORE)
57
+ and materializedSgmatRuntimeDirectStore
58
+ and (scorePhase or (not hasBias and MT == "f32"))
59
+ and not EMIT_ROW_STATS %}
60
+ {% set ANY_DIRECT_STORE = DIRECT_OUTPUT_STORE or RUNTIME_DIRECT_STORE %}
61
+ {% set SCALE_IN_Q = DIRECT_SCORE_STORE or (RUNTIME_DIRECT_STORE and scorePhase) %}
62
+ {% macro q_tile_value(index) %}{% if hasBias %}(query[{{ index }}] + bias[h * HEAD_DIM + k]){% else %}query[{{ index }}]{% endif %}{% if SCALE_IN_Q %} * score_scale{% endif %}{% endmacro %}
63
+
64
+ const HEADS: u32 = {{ qNumHeads }}u;
65
+ {% if kvNumHeads is defined %}
66
+ const KV_HEADS: u32 = {{ kvNumHeads }}u;
67
+ {% else %}
68
+ const KV_HEADS: u32 = HEADS;
69
+ {% endif %}
70
+ const HEAD_DIM: u32 = {{ headDim }}u;
71
+ const HIDDEN: u32 = {{ qHidden }}u;
72
+ {% if hasBias %}
73
+ /* Packed [Q; K; V] bias. Q folds into the query tile before the GEMM. K is
74
+ * omitted: expanding (q + bq).(k + bk) leaves a (q + bq).bk term that is
75
+ * constant across every key in the row, and softmax is invariant under that
76
+ * constant. V is token-independent, so sum_k p[k] * bv = bv after
77
+ * normalization; it is added after the apply GEMM. */
78
+ {% endif %}
79
+ {% if not kvHeadMajor %}
80
+ // Packed K/V rows span KV_HEADS heads, so they are narrower than the query
81
+ // row whenever queries are grouped. Q and the output keep HIDDEN.
82
+ const KV_HIDDEN: u32 = KV_HEADS * HEAD_DIM;
83
+ {% endif %}
84
+ {% if EMIT_ROW_STATS %}
85
+ const STAT_SLOTS: u32 = {{ statSlots }}u;
86
+ {% endif %}
87
+ const TILE_M: u32 = {{ TILE_M_VALUE }}u;
88
+ const TILE_N: u32 = {{ TILE_N_VALUE }}u;
89
+ const TILE_K: u32 = {{ TILE_K_VALUE }}u;
90
+ const SUB_ROWS: u32 = {{ SUB_ROWS_VALUE }}u;
91
+ const SUB_COLS: u32 = {{ SUB_COLS_VALUE }}u;
92
+ const SUBGROUP_ROWS: u32 = {{ SUBGROUP_ROWS }}u;
93
+ {% if (FUSED_SOFTMAX and not PRIVATE_ROW_STATS)
94
+ or not (TILE_M_VALUE == 64 and TILE_N_VALUE == 64 and TILE_K_VALUE == 32 and WORKGROUP_THREADS == 256) %}
95
+ const WORKGROUP_THREADS: u32 = {{ WORKGROUP_THREADS }}u;
96
+ {% endif %}
97
+
98
+ var<workgroup> tile_A: array<{{ MT }}, {{ TILE_M_VALUE * TILE_K_VALUE }}>;
99
+ var<workgroup> tile_B: array<{{ MT }}, {{ TILE_N_VALUE * TILE_K_VALUE }}>;
100
+ {% set SCRATCH = "tile_A" if MT == "f32" else "store_scratch" %}
101
+ {% if MT == "f16" %}
102
+ // The compact epilogue banks f32 result fragments; an f16 operand tile cannot
103
+ // alias them, so the f16 build carries a dedicated store scratch.
104
+ var<workgroup> store_scratch: array<f32, {{ TILE_M_VALUE * TILE_K_VALUE }}>;
105
+ {% endif %}
106
+ {% if FUSED_SOFTMAX and not PRIVATE_ROW_STATS %}
107
+ // The tile's rows own their softmax constants for the whole k loop, so they are
108
+ // read once per workgroup rather than once per staged element.
109
+ var<workgroup> softmax_m: array<f32, {{ TILE_M_VALUE }}>;
110
+ var<workgroup> softmax_d: array<f32, {{ TILE_M_VALUE }}>;
111
+ {% endif %}
112
+ {% if FUSED_SOFTMAX or EMIT_ROW_STATS %}
113
+ {% set stableUsage = stableHelperUsage if stableHelperUsage is defined else "all" %}
114
+ // FLT_MAX, not -inf, as the online (m, d) accumulator init: merges must keep
115
+ // `m - m` finite so an empty lane / all--inf row contributes the exact
116
+ // accumulator identity (m, d) = (-FLT_MAX, 0). Operator epilogues interpret
117
+ // a zero final denominator according to their public semantics. Using -inf
118
+ // here changes +inf-row behavior.
119
+ const FLT_MAX: f32 = 3.4028234663852886e38;
120
+ {% if stableUsage != "constant" %}
121
+
122
+ fn is_finite_f32(value: f32) -> bool {
123
+ return select(false, value <= FLT_MAX, value >= -FLT_MAX);
124
+ }
125
+
126
+ // x - m that is exactly 0 when x equals a finite m, so exp(shifted) == 1
127
+ // exactly at the row max. `x - x` on an infinite max is a legal fast-math
128
+ // fold to 0, which would silently turn +inf rows finite — the explicit
129
+ // equality test keeps the NaN propagation of the serial kernels.
130
+ fn shifted_value(value: f32, maxValue: f32) -> f32 {
131
+ let equalFiniteMax = select(false, value == maxValue, is_finite_f32(maxValue));
132
+ return select(value - maxValue, 0.0, equalFiniteMax);
133
+ }
134
+ {%- endif %}
135
+ {% if stableUsage == "all" %}
136
+
137
+ fn exp_shift(value: f32, maxValue: f32) -> f32 {
138
+ return exp(shifted_value(value, maxValue));
139
+ }
140
+ {%- endif %}
141
+
142
+ {% endif %}
143
+
144
+ @compute @workgroup_size({{ WORKGROUP_THREADS }}, 1, 1){{ " @subgroup_size(32)" if pinSubgroupSize32 else "" }}
145
+ fn main(
146
+ @builtin(workgroup_id) wg: vec3<u32>,
147
+ @builtin(local_invocation_index) li: u32{% if not DIRECT_OUTPUT_STORE %},
148
+ @builtin(subgroup_invocation_id) lane: u32{% endif %}
149
+ ) {
150
+ let h = wg.z % HEADS;
151
+ let b = wg.z / HEADS;
152
+ let h_kv = h / (HEADS / KV_HEADS);
153
+ let m_base = wg.y * TILE_M;
154
+ let n_base = wg.x * TILE_N;
155
+ let subgroup = li / 32u;
156
+ let subtile_idy = subgroup % SUBGROUP_ROWS;
157
+ let subtile_idx = subgroup / SUBGROUP_ROWS;
158
+ let base_A = subtile_idy * SUB_ROWS;
159
+ let base_B = subtile_idx * SUB_COLS;
160
+ {% if USE_SEQLENS %}
161
+ // Rows the cache-update pass left resident: the survivors live in
162
+ // [0, kv_active), and every causal/window bound below uses this live length
163
+ // while params.kvSeq keeps the allocated capacity for strides.
164
+ let kv_active = min(params.kvSeq, u32(seqlens_k[b]) + 1u);
165
+ {% endif %}
166
+ {% if CAUSAL and scorePhase %}
167
+ // Workgroup-uniform causal skip: a key tile starting past the last row's
168
+ // bound holds no valid column. Publish the stats identity for its slots so
169
+ // the combine pass reads initialized pairs, then leave before any loads.
170
+ {% if SCORE_WINDOW %}
171
+ // A tile whose last column sits behind the FIRST row's window floor is dead
172
+ // the same way: later rows only move the floor further right.
173
+ {% endif %}
174
+ let kv_causal_off = i32({% if USE_SEQLENS %}kv_active{% else %}params.kvSeq{% endif %}) - i32(params.qSeq);
175
+ if (i32(n_base) > kv_causal_off + i32(m_base + TILE_M) - 1{% if SCORE_WINDOW %}
176
+ || i32(n_base + TILE_N) <= kv_causal_off + i32(m_base) + 1 - i32(params.windowSize){% endif %}) {
177
+ for (var idx = li; idx < TILE_M * {{ SUBGROUP_COLS }}u; idx += {{ WORKGROUP_THREADS }}u) {
178
+ let stat_row = m_base + idx / {{ SUBGROUP_COLS }}u;
179
+ if (stat_row < params.qSeq) {
180
+ let slot = wg.x * {{ SUBGROUP_COLS }}u + idx % {{ SUBGROUP_COLS }}u;
181
+ let out_index = (((b * HEADS + h) * STAT_SLOTS + slot) * params.qSeq + stat_row) * 2u;
182
+ scorePartials[out_index] = -FLT_MAX;
183
+ scorePartials[out_index + 1u] = 0.0;
184
+ }
185
+ }
186
+ return;
187
+ }
188
+ {% endif %}
189
+
190
+ {% for row_block in range(ROW_BLOCKS) %}
191
+ {% for col_block in range(COL_BLOCKS) %}
192
+ var matC{{ row_block }}{{ col_block }}: subgroup_matrix_result<f32, 8, 8>;
193
+ {% endfor %}
194
+ {% endfor %}
195
+
196
+ {% if scorePhase %}
197
+ let inner = HEAD_DIM;
198
+ {% if SCALE_IN_Q %}
199
+ // The direct store has no epilogue, so apply the score scale to Q. Direct and
200
+ // guarded store paths then share one pre-scaled query tile.
201
+ let score_scale = {{ attentionScaleExpression }};
202
+ {% endif %}
203
+ {% else %}
204
+ let inner = {% if USE_SEQLENS %}kv_active{% else %}params.kvSeq{% endif %};
205
+ {% if CAUSAL %}
206
+ // Score tiles past this query tile's causal bound were never written; stop
207
+ // the reduction at the last written key column for the tile's rows.
208
+ let kv_causal_off = i32({% if USE_SEQLENS %}inner{% else %}params.kvSeq{% endif %}) - i32(params.qSeq);
209
+ let inner_bound = u32(clamp(kv_causal_off + i32(m_base + TILE_M), 0, i32(inner)));
210
+ {% if SCORE_WINDOW %}
211
+ // Score tiles behind every row's window floor were never written either. The
212
+ // floor of this tile's FIRST row is the leftmost any of its rows can reach,
213
+ // and rounding it down to a tile boundary only re-reads columns the score
214
+ // phase did write (its skip test uses whole key tiles).
215
+ let inner_start = (u32(max(kv_causal_off + i32(m_base) + 1 - i32(params.windowSize), 0)) / TILE_K) * TILE_K;
216
+ {% endif %}
217
+ {% endif %}
218
+ {% endif %}
219
+ {% if FUSED_SOFTMAX %}
220
+ {% if PRIVATE_ROW_STATS %}
221
+ // In the admitted BM64/BN64/BK32/WG256 loader, four adjacent lanes own the
222
+ // same query row for every reduction tile. Keep that row's constants private:
223
+ // this removes both 512 bytes of workgroup storage and the initialization
224
+ // barrier while preserving the exact exp/divide sequence of the shared-memory
225
+ // row-stats arm.
226
+ let private_stat_row =
227
+ (b * HEADS + h) * params.qSeq + min(m_base + li / 4u, params.qSeq - 1u);
228
+ let private_softmax_m = rowStats[private_stat_row * 2u];
229
+ let private_softmax_d = rowStats[private_stat_row * 2u + 1u];
230
+ {% else %}
231
+ // One row-stats pair per query row of the tile. A query tail clamps to the last
232
+ // real row rather than reading past the buffer; those lanes are discarded by the
233
+ // staging guard anyway, and the clamp keeps the denominator non-zero.
234
+ for (var r = li; r < TILE_M; r += WORKGROUP_THREADS) {
235
+ let stat_row = (b * HEADS + h) * params.qSeq + min(m_base + r, params.qSeq - 1u);
236
+ softmax_m[r] = rowStats[stat_row * 2u];
237
+ softmax_d[r] = rowStats[stat_row * 2u + 1u];
238
+ }
239
+ workgroupBarrier();
240
+ {% endif %}
241
+ {% endif %}
242
+ for (var k_base = {% if SCORE_WINDOW and not scorePhase %}inner_start{% else %}0u{% endif %}; k_base < {% if CAUSAL and not scorePhase %}inner_bound{% else %}inner{% endif %}; k_base += TILE_K) {
243
+ {% if source.phase == "apply" and not FUSED_SOFTMAX and MT == "f32" %}
244
+ // Full interior PV tiles can be loaded directly from storage. Query,
245
+ // reduction, and output-dimension tails use the guarded shared path below.
246
+ if (
247
+ k_base + TILE_K <= inner &&
248
+ m_base + TILE_M <= params.qSeq &&
249
+ n_base + TILE_N <= HEAD_DIM
250
+ ) {
251
+ for (var step = 0u; step < TILE_K; step += 8u) {
252
+ {% for row_block in range(ROW_BLOCKS) %}
253
+ let score_offset{{ row_block }} = (b * HEADS + h) * params.qSeq * params.kvSeq
254
+ + (m_base + base_A + {{ row_block * 8 }}u) * params.kvSeq + k_base + step;
255
+ var matA{{ row_block }}: subgroup_matrix_left<f32, 8, 8> =
256
+ subgroupMatrixLoad<subgroup_matrix_left<f32, 8, 8>, row_major>(
257
+ &scores, score_offset{{ row_block }}, params.kvSeq
258
+ );
259
+ {% endfor %}
260
+ {% for col_block in range(COL_BLOCKS) %}
261
+ let value_offset{{ col_block }} =
262
+ {{ kv_index("k_base + step", "n_base + base_B + " ~ (col_block * 8) ~ "u") }};
263
+ var matB{{ col_block }}: subgroup_matrix_right<f32, 8, 8> =
264
+ subgroupMatrixLoad<subgroup_matrix_right<f32, 8, 8>, row_major>(
265
+ &value, value_offset{{ col_block }}, {{ KV_ROW_STRIDE }}
266
+ );
267
+ {% endfor %}
268
+ {% for row_block in range(ROW_BLOCKS) %}
269
+ {% for col_block in range(COL_BLOCKS) %}
270
+ matC{{ row_block }}{{ col_block }} = subgroupMatrixMultiplyAccumulate(
271
+ matA{{ row_block }}, matB{{ col_block }}, matC{{ row_block }}{{ col_block }}
272
+ );
273
+ {% endfor %}
274
+ {% endfor %}
275
+ }
276
+ continue;
277
+ }
278
+ {% endif %}
279
+
280
+ {% if TILE_M_VALUE == 64 and TILE_N_VALUE == 64 and TILE_K_VALUE == 32 and WORKGROUP_THREADS == 256 %}
281
+ // BM64/BN64/WG256 is the high-throughput geometry on wide devices. Four
282
+ // adjacent threads each own one contiguous eight-element segment of a
283
+ // row, preserving the original coalesced load schedule while retaining
284
+ // the generalized K-tail guards.
285
+ let a_row = li / 4u;
286
+ let a_col = (li % 4u) * 8u;
287
+ for (var i = 0u; i < 8u; i++) {
288
+ let row = m_base + a_row;
289
+ let k = k_base + a_col + i;
290
+ {% if scorePhase %}
291
+ {% if headDim % 32 == 0 %}
292
+ tile_A[a_row * TILE_K + a_col + i] = select(
293
+ {{ "0.0h" if MT == "f16" else "0.0" }},
294
+ {{ q_tile_value(q_index("row", "k")) }},
295
+ row < params.qSeq
296
+ );
297
+ {% else %}
298
+ var loaded = {{ "0.0h" if MT == "f16" else "0.0" }};
299
+ if (row < params.qSeq && k < HEAD_DIM) {
300
+ loaded = {{ q_tile_value(q_index("row", "k")) }};
301
+ }
302
+ tile_A[a_row * TILE_K + a_col + i] = loaded;
303
+ {% endif %}
304
+ {% else %}
305
+ let score_base = (b * HEADS + h) * params.qSeq * params.kvSeq;
306
+ tile_A[a_row * TILE_K + a_col + i] =
307
+ {{ "f16(" if MT == "f16" else "" }}{{ score_value("score_base + row * params.kvSeq + k", ["a_row", "row < params.qSeq && k < params.kvSeq"]) }}{{ ")" if MT == "f16" else "" }};
308
+ {% endif %}
309
+ }
310
+
311
+ let b_row = li / 4u;
312
+ let b_col = (li % 4u) * 8u;
313
+ for (var i = 0u; i < 8u; i++) {
314
+ let col = n_base + b_row;
315
+ let k = k_base + b_col + i;
316
+ {% if scorePhase %}
317
+ {% if headDim % 32 == 0 %}
318
+ tile_B[b_row * TILE_K + b_col + i] = select(
319
+ {{ "0.0h" if MT == "f16" else "0.0" }},
320
+ key[{{ kv_index("col", "k") }}],
321
+ col < params.kvSeq
322
+ );
323
+ {% else %}
324
+ var loaded = {{ "0.0h" if MT == "f16" else "0.0" }};
325
+ if (col < params.kvSeq && k < HEAD_DIM) {
326
+ loaded = key[{{ kv_index("col", "k") }}];
327
+ }
328
+ tile_B[b_row * TILE_K + b_col + i] = loaded;
329
+ {% endif %}
330
+ {% else %}
331
+ tile_B[b_row * TILE_K + b_col + i] = select(
332
+ {{ "0.0h" if MT == "f16" else "0.0" }},
333
+ value[{{ kv_index("k", "col") }}],
334
+ k < params.kvSeq && col < HEAD_DIM
335
+ );
336
+ {% endif %}
337
+ }
338
+ {% else %}
339
+ // Cooperative linear loads make smaller tile dimensions independent of
340
+ // the selected workgroup size.
341
+ for (var idx = li; idx < TILE_M * TILE_K; idx += WORKGROUP_THREADS) {
342
+ let tile_row = idx / TILE_K;
343
+ let tile_k = idx % TILE_K;
344
+ let row = m_base + tile_row;
345
+ let k = k_base + tile_k;
346
+ {% if scorePhase %}
347
+ var loaded = {{ "0.0h" if MT == "f16" else "0.0" }};
348
+ if (row < params.qSeq && k < HEAD_DIM) {
349
+ loaded = {{ q_tile_value(q_index("row", "k")) }};
350
+ }
351
+ {% elif FUSED_SOFTMAX %}
352
+ let score_base = (b * HEADS + h) * params.qSeq * params.kvSeq;
353
+ let loaded =
354
+ {{ "f16(" if MT == "f16" else "" }}{{ score_value("score_base + row * params.kvSeq + k", ["tile_row", "row < params.qSeq && k < params.kvSeq"]) }}{{ ")" if MT == "f16" else "" }};
355
+ {% else %}
356
+ var loaded = {{ "0.0h" if MT == "f16" else "0.0" }};
357
+ if (row < params.qSeq && k < params.kvSeq) {
358
+ let score_base = (b * HEADS + h) * params.qSeq * params.kvSeq;
359
+ loaded = {{ "f16(" if MT == "f16" else "" }}scores[score_base + row * params.kvSeq + k]{{ ")" if MT == "f16" else "" }};
360
+ }
361
+ {% endif %}
362
+ tile_A[idx] = loaded;
363
+ }
364
+
365
+ for (var idx = li; idx < TILE_N * TILE_K; idx += WORKGROUP_THREADS) {
366
+ let tile_col = idx / TILE_K;
367
+ let tile_k = idx % TILE_K;
368
+ let col = n_base + tile_col;
369
+ let k = k_base + tile_k;
370
+ {% if scorePhase %}
371
+ var loaded = {{ "0.0h" if MT == "f16" else "0.0" }};
372
+ if (col < params.kvSeq && k < HEAD_DIM) {
373
+ loaded = key[{{ kv_index("col", "k") }}];
374
+ }
375
+ {% else %}
376
+ var loaded = {{ "0.0h" if MT == "f16" else "0.0" }};
377
+ if (k < params.kvSeq && col < HEAD_DIM) {
378
+ loaded = value[{{ kv_index("k", "col") }}];
379
+ }
380
+ {% endif %}
381
+ tile_B[idx] = loaded;
382
+ }
383
+ {% endif %}
384
+ workgroupBarrier();
385
+
386
+ for (var step = 0u; step < TILE_K; step += 8u) {
387
+ {% for row_block in range(ROW_BLOCKS) %}
388
+ let matrix_a_offset{{ row_block }} =
389
+ (subtile_idy * SUB_ROWS + {{ row_block * 8 }}u) * TILE_K + step;
390
+ var matA{{ row_block }}: subgroup_matrix_left<{{ MT }}, 8, 8> =
391
+ subgroupMatrixLoad<subgroup_matrix_left<{{ MT }}, 8, 8>, row_major>(
392
+ &tile_A, matrix_a_offset{{ row_block }}, TILE_K
393
+ );
394
+ {% endfor %}
395
+ {% for col_block in range(COL_BLOCKS) %}
396
+ let matrix_b_offset{{ col_block }} =
397
+ (subtile_idx * SUB_COLS + {{ col_block * 8 }}u) * TILE_K + step;
398
+ var matB{{ col_block }}: subgroup_matrix_right<{{ MT }}, 8, 8> =
399
+ subgroupMatrixLoad<subgroup_matrix_right<{{ MT }}, 8, 8>, col_major>(
400
+ &tile_B, matrix_b_offset{{ col_block }}, TILE_K
401
+ );
402
+ {% endfor %}
403
+ {% for row_block in range(ROW_BLOCKS) %}
404
+ {% for col_block in range(COL_BLOCKS) %}
405
+ matC{{ row_block }}{{ col_block }} = subgroupMatrixMultiplyAccumulate(
406
+ matA{{ row_block }}, matB{{ col_block }}, matC{{ row_block }}{{ col_block }}
407
+ );
408
+ {% endfor %}
409
+ {% endfor %}
410
+ }
411
+ workgroupBarrier();
412
+ }
413
+
414
+ {% if ANY_DIRECT_STORE %}
415
+ {% if RUNTIME_DIRECT_STORE %}
416
+ // Workgroup-uniform (both bases come from workgroup_id alone): an interior tile
417
+ // publishes through the subgroup-matrix collectives and is done; only an edge
418
+ // tile falls through to the scratch round trip and its barriers.
419
+ if (
420
+ m_base + TILE_M <= params.qSeq &&
421
+ n_base + TILE_N <= {% if scorePhase %}params.kvSeq{% else %}HEAD_DIM{% endif %}
422
+ ) {
423
+ {% else %}
424
+ // Every dispatched tile is interior, so the matrices can go straight to
425
+ // storage without the compact scratch/readback epilogue.
426
+ {% endif %}
427
+ {% for row_block in range(ROW_BLOCKS) %}
428
+ {% for col_block in range(COL_BLOCKS) %}
429
+ {% if scorePhase %}
430
+ let output_offset{{ row_block }}{{ col_block }} =
431
+ (b * HEADS + h) * params.qSeq * params.kvSeq
432
+ + (m_base + base_A + {{ row_block * 8 }}u) * params.kvSeq
433
+ + n_base + base_B + {{ col_block * 8 }}u;
434
+ subgroupMatrixStore<row_major>(
435
+ &scores, output_offset{{ row_block }}{{ col_block }},
436
+ matC{{ row_block }}{{ col_block }}, params.kvSeq
437
+ );
438
+ {% else %}
439
+ let output_offset{{ row_block }}{{ col_block }} =
440
+ {{ q_index("m_base + base_A + " ~ (row_block * 8) ~ "u", "n_base + base_B + " ~ (col_block * 8) ~ "u") }};
441
+ subgroupMatrixStore<row_major>(
442
+ &output, output_offset{{ row_block }}{{ col_block }},
443
+ matC{{ row_block }}{{ col_block }}, {{ OUT_ROW_STRIDE }}
444
+ );
445
+ {% endif %}
446
+ {% endfor %}
447
+ {% endfor %}
448
+ {% if RUNTIME_DIRECT_STORE %}
449
+ return;
450
+ }
451
+ {% endif %}
452
+ {% endif %}
453
+ {% if not DIRECT_OUTPUT_STORE %}
454
+ let row_in_block = lane / 4u;
455
+ let col_in_block = (lane % 4u) * 2u;
456
+ {% for row_block in range(ROW_BLOCKS) %}
457
+ {% if row_block > 0 %}
458
+ // The compact path aliases the input tile as matrix-store scratch, so every
459
+ // lane must finish reading the preceding row block before it is overwritten.
460
+ workgroupBarrier();
461
+ {% endif %}
462
+ {% if EMIT_ROW_STATS %}
463
+ // All four lanes of this quad carry the same row (row_in_block is lane / 4),
464
+ // so the accumulator below is a partial over one row and the butterfly that
465
+ // merges it is quad-uniform — no divergent shuffle even on a query tail.
466
+ var stat_m{{ row_block }} = -FLT_MAX;
467
+ var stat_d{{ row_block }} = 0.0;
468
+ {% endif %}
469
+ {% for col_block in range(COL_BLOCKS) %}
470
+ subgroupMatrixStore<row_major>(
471
+ &{{ SCRATCH }},
472
+ (subgroup * {{ COL_BLOCKS }}u + {{ col_block }}u) * 64u,
473
+ matC{{ row_block }}{{ col_block }},
474
+ 8u
475
+ );
476
+ {% endfor %}
477
+ workgroupBarrier();
478
+
479
+ {% for col_block in range(COL_BLOCKS) %}
480
+ for (var pair = 0u; pair < 2u; pair++) {
481
+ let row = m_base + base_A + {{ row_block * 8 }}u + row_in_block;
482
+ let col = n_base + base_B + {{ col_block * 8 }}u + col_in_block + pair;
483
+ if (
484
+ row < params.qSeq &&
485
+ col < {% if scorePhase %}params.kvSeq{% else %}HEAD_DIM{% endif %}
486
+ ) {
487
+ let result = {{ SCRATCH }}[
488
+ (subgroup * {{ COL_BLOCKS }}u + {{ col_block }}u) * 64u
489
+ + row_in_block * 8u + col_in_block + pair
490
+ ];
491
+ {% if scorePhase %}
492
+ {% if not SCALE_IN_Q %}
493
+ let scale = {{ attentionScaleExpression }};
494
+ {% endif %}
495
+ {% if CAUSAL %}
496
+ var scored = result * scale;
497
+ {% if SCORE_BIAS %}
498
+ let bias_b = select(b, 0u, params.biasBatch == 1u);
499
+ let bias_h = select(h, 0u, params.biasHeads == 1u);
500
+ scored += attention_bias[
501
+ ((bias_b * params.biasHeads + bias_h) * params.qSeq + row) * params.kvSeq + col
502
+ ];
503
+ {% endif %}
504
+ if (i32(col) > kv_causal_off + i32(row)) { scored = -FLT_MAX; }
505
+ {% if SCORE_WINDOW %}
506
+ if (i32(col) + i32(params.windowSize) <= kv_causal_off + i32(row)) { scored = -FLT_MAX; }
507
+ {% endif %}
508
+ {% else %}
509
+ let scored = result{% if not SCALE_IN_Q %} * scale{% endif %};
510
+ {% endif %}
511
+ scores[
512
+ (b * HEADS + h) * params.qSeq * params.kvSeq + row * params.kvSeq + col
513
+ ] = scored;
514
+ {% if EMIT_ROW_STATS %}
515
+ // Softmax sees the STORED value, so the statistics have to be taken on it
516
+ // and not on the raw accumulator.
517
+ let stat_prev = stat_m{{ row_block }};
518
+ stat_m{{ row_block }} = max(stat_m{{ row_block }}, scored);
519
+ stat_d{{ row_block }} = stat_d{{ row_block }} * exp_shift(stat_prev, stat_m{{ row_block }})
520
+ + exp_shift(scored, stat_m{{ row_block }});
521
+ {% endif %}
522
+ {% else %}
523
+ {% if hasBias %}
524
+ // V bias row base: skip the packed Q and K blocks, then index this head.
525
+ {% endif %}
526
+ output[{{ q_index("row", "col") }}] = {{ "f16(" if MT == "f16" else "" }}result{{ ")" if MT == "f16" else "" }}{% if hasBias %} + bias[2u * HIDDEN + h * HEAD_DIM + col]{% endif %};
527
+ {% endif %}
528
+ }
529
+ }
530
+ {% endfor %}
531
+ {% if EMIT_ROW_STATS %}
532
+ // Butterfly the quad UNCONDITIONALLY. A lane whose row ran past the query tail
533
+ // never entered the guard above and still carries the exact identity
534
+ // (-FLT_MAX, 0), which merges to a no-op — that is cheaper than making the
535
+ // shuffle conditional, and a subgroup operation under a guard that only some
536
+ // quads of the subgroup satisfy would not be uniform.
537
+ {% for xor in [1, 2] %}
538
+ {
539
+ let other_m = subgroupShuffleXor(stat_m{{ row_block }}, {{ xor }}u);
540
+ let other_d = subgroupShuffleXor(stat_d{{ row_block }}, {{ xor }}u);
541
+ let merged_m = max(stat_m{{ row_block }}, other_m);
542
+ stat_d{{ row_block }} = stat_d{{ row_block }} * exp_shift(stat_m{{ row_block }}, merged_m)
543
+ + other_d * exp_shift(other_m, merged_m);
544
+ stat_m{{ row_block }} = merged_m;
545
+ }
546
+ {% endfor %}
547
+ {
548
+ let stat_row = m_base + base_A + {{ row_block * 8 }}u + row_in_block;
549
+ if (lane % 4u == 0u && stat_row < params.qSeq) {
550
+ // Row is the FASTEST axis so the eight writing lanes of a subgroup land on
551
+ // eight consecutive pairs, and the combine pass reads a slot's whole column
552
+ // of rows contiguously.
553
+ let slot = wg.x * {{ SUBGROUP_COLS }}u + subtile_idx;
554
+ let out_index = (((b * HEADS + h) * STAT_SLOTS + slot) * params.qSeq + stat_row) * 2u;
555
+ scorePartials[out_index] = stat_m{{ row_block }};
556
+ scorePartials[out_index + 1u] = stat_d{{ row_block }};
557
+ }
558
+ }
559
+ {% endif %}
560
+ {% endfor %}
561
+ {% endif %}
562
+ }
build/webgpu/bench.json CHANGED
@@ -1560,6 +1560,29 @@
1560
  "source": "register-geometry gate asymmetry",
1561
  "notes": "Cached f32 prefill at headDim 256, above the shared-memory cluster's register-geometry boundary. This guards consistent route admission between no-past and cached prefill families."
1562
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1563
  }
1564
  ]
1565
  }
 
1560
  "source": "register-geometry gate asymmetry",
1561
  "notes": "Cached f32 prefill at headDim 256, above the shared-memory cluster's register-geometry boundary. This guards consistent route admission between no-past and cached prefill families."
1562
  }
1563
+ },
1564
+ {
1565
+ "name": "qwen3-prefill-32h8kv-d128-s256",
1566
+ "preset": "smoke",
1567
+ "vars": { "batch": 1, "qSeq": 256, "kvSeq": 256, "heads": 32, "kvHeads": 8, "headDim": 128 },
1568
+ "attrs": { "num_heads": 32, "kv_num_heads": 8, "scale": 0.08838834764831845, "causal": 0 },
1569
+ "inputs": {
1570
+ "queryT": { "shape": [1, 256, 4096], "dtype": "float32", "dist": "normal", "seed": 960, "scale": 0.2 },
1571
+ "keyT": { "shape": [1, 256, 1024], "dtype": "float32", "dist": "normal", "seed": 961, "scale": 0.2 },
1572
+ "valueT": { "shape": [1, 256, 1024], "dtype": "float32", "dist": "normal", "seed": 962, "scale": 0.2 },
1573
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [255] } },
1574
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [256] } }
1575
+ },
1576
+ "outputs": {
1577
+ "outputT": { "shape": [1, 256, 4096], "dtype": "float32" },
1578
+ "presentKeyT": { "shape": [1, 8, 256, 128], "dtype": "float32" },
1579
+ "presentValueT": { "shape": [1, 8, 256, 128], "dtype": "float32" }
1580
+ },
1581
+ "bench": {
1582
+ "metrics": [
1583
+ { "type": "gflops", "value": "4 * args.batch * args.qSeq * args.kvSeq * args.heads * args.headDim" }
1584
+ ]
1585
+ }
1586
  }
1587
  ]
1588
  }
build/webgpu/gqa-qprep.wgsl.jinja CHANGED
@@ -8,17 +8,22 @@
8
  // exactly (the present-cache K is already norm/rotary-applied, so only Q is touched),
9
  // so the flash result is bit-identical to the cooperative/threaded path.
10
  //
11
- // f16 queries are widened before cos/sin and norm/rotary arithmetic;
12
- // the qPrep output also stays f32 — RoPE precision matters at large positions, so
13
- // the rotated Q enters attention in full f32 (the cluster reads f32 Q + f16 K/V).
 
 
14
  const HEAD_DIM: u32 = {{ headDim }}u;
15
  const HEAD_DIM_V4: u32 = {{ headDim }}u / 4u;
16
  const Q_HEADS: u32 = {{ qHeads }}u;
17
  const Q_HIDDEN: u32 = {{ qHidden }}u;
18
  const Q_HIDDEN_V4: u32 = {{ qHidden }}u / 4u;
19
  const WG: u32 = {{ copyWorkgroupSize }}u;
 
20
  const HALF: u32 = {{ half }}u;
 
21
  const QK_EPS: f32 = {{ qkEps }};
 
22
 
23
  @compute @workgroup_size(WG)
24
  fn main(@builtin(global_invocation_id) gid: vec3<u32>,
@@ -37,10 +42,12 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
37
 
38
  var q: array<f32, HEAD_DIM>;
39
  for (var d = 0u; d < HEAD_DIM; d = d + 1u) { q[d] = f32(query[base + d]); }
 
40
  var ss = 0.0;
41
  for (var d = 0u; d < HEAD_DIM; d = d + 1u) { ss = ss + q[d] * q[d]; }
42
  let invRms = inverseSqrt(ss / f32(HEAD_DIM) + QK_EPS);
43
  for (var d = 0u; d < HEAD_DIM; d = d + 1u) { q[d] = q[d] * invRms * f32(q_norm_weight[d]); }
 
44
  for (var d = 0u; d < HALF; d = d + 1u) {
45
  let cs = f32(cos_cache[absPos * HALF + d]);
46
  let sn = f32(sin_cache[absPos * HALF + d]);
@@ -52,6 +59,6 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
52
 
53
  let base4 = (b * params.qSeq + s) * Q_HIDDEN_V4 + h * HEAD_DIM_V4;
54
  for (var c = 0u; c < HEAD_DIM_V4; c = c + 1u) {
55
- qout[base4 + c] = vec4<f32>(q[c * 4u], q[c * 4u + 1u], q[c * 4u + 2u], q[c * 4u + 3u]);
56
  }
57
  }
 
8
  // exactly (the present-cache K is already norm/rotary-applied, so only Q is touched),
9
  // so the flash result is bit-identical to the cooperative/threaded path.
10
  //
11
+ // f16 queries are widened before cos/sin and norm/rotary arithmetic, and the
12
+ // rotation itself always runs in f32 — RoPE precision matters at large
13
+ // positions. The store narrows only for a consumer whose operand tiles are
14
+ // f16 anyway (the subgroup-matrix route), where a wider intermediate would be
15
+ // narrowed at staging regardless; every other consumer keeps the f32 output.
16
  const HEAD_DIM: u32 = {{ headDim }}u;
17
  const HEAD_DIM_V4: u32 = {{ headDim }}u / 4u;
18
  const Q_HEADS: u32 = {{ qHeads }}u;
19
  const Q_HIDDEN: u32 = {{ qHidden }}u;
20
  const Q_HIDDEN_V4: u32 = {{ qHidden }}u / 4u;
21
  const WG: u32 = {{ copyWorkgroupSize }}u;
22
+ {% set HAS_QNORM = hasQNorm is not defined or hasQNorm %}
23
  const HALF: u32 = {{ half }}u;
24
+ {% if HAS_QNORM %}
25
  const QK_EPS: f32 = {{ qkEps }};
26
+ {% endif %}
27
 
28
  @compute @workgroup_size(WG)
29
  fn main(@builtin(global_invocation_id) gid: vec3<u32>,
 
42
 
43
  var q: array<f32, HEAD_DIM>;
44
  for (var d = 0u; d < HEAD_DIM; d = d + 1u) { q[d] = f32(query[base + d]); }
45
+ {% if HAS_QNORM %}
46
  var ss = 0.0;
47
  for (var d = 0u; d < HEAD_DIM; d = d + 1u) { ss = ss + q[d] * q[d]; }
48
  let invRms = inverseSqrt(ss / f32(HEAD_DIM) + QK_EPS);
49
  for (var d = 0u; d < HEAD_DIM; d = d + 1u) { q[d] = q[d] * invRms * f32(q_norm_weight[d]); }
50
+ {% endif %}
51
  for (var d = 0u; d < HALF; d = d + 1u) {
52
  let cs = f32(cos_cache[absPos * HALF + d]);
53
  let sn = f32(sin_cache[absPos * HALF + d]);
 
59
 
60
  let base4 = (b * params.qSeq + s) * Q_HIDDEN_V4 + h * HEAD_DIM_V4;
61
  for (var c = 0u; c < HEAD_DIM_V4; c = c + 1u) {
62
+ qout[base4 + c] = {{ "vec4<f16>(" if (qPrepF16 is defined and qPrepF16) else "" }}vec4<f32>(q[c * 4u], q[c * 4u + 1u], q[c * 4u + 2u], q[c * 4u + 3u]){{ ")" if (qPrepF16 is defined and qPrepF16) else "" }};
63
  }
64
  }
build/webgpu/manifest.json CHANGED
@@ -210,13 +210,23 @@
210
  "CLUSTER_TILE_K_F16": 8,
211
  "NO_SG_TILE_K_MAX": 16,
212
  "COOPERATIVE_WORKGROUP_SIZE": 32,
213
- "CLUSTER_MAX_SLICE": 8
 
 
 
 
 
 
 
214
  },
215
  "derive": {
216
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
217
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
218
  "subgroupsWave32": "device.features.has(\"subgroups\") and wave32Adapter",
219
  "narrowSubgroupRange": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize < device.adapterInfo.subgroupMaxSize and device.adapterInfo.subgroupMaxSize <= 16",
 
 
 
220
  "windowCacheRequested": "attrs.sliding_window_cache == 1",
221
  "headDim": "dim(shapes.query, 2) / attrs.num_heads if (ranks.query == 3 and attrs.num_heads > 0) else 0",
222
  "copyWorkgroupSize": "min(tunables.COPY_WORKGROUP_SIZE, min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX))",
@@ -310,7 +320,41 @@
310
  "cachedNoSgTileBytes": "gqaHeadDim * (8 if tensorDtypes.queryT == \"float32\" else 4) + cachedNoSgReductionBytesPerKey",
311
  "cachedNoSgTileK": "min(tunables.NO_SG_TILE_K_MAX, max(1, floor(device.limits.maxComputeWorkgroupStorageSize / cachedNoSgTileBytes)))",
312
  "cachedNoSgWorkgroupBytes": "cachedNoSgTileK * cachedNoSgTileBytes",
313
- "cachedNoSgClusterStorageOk": "cachedNoSgWorkgroupBytes <= device.limits.maxComputeWorkgroupStorageSize"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  },
315
  "bindingSets": {
316
  "splitAttention": [
@@ -1895,9 +1939,1306 @@
1895
  ]
1896
  }
1897
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1898
  ]
1899
  },
1900
  "variants": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1901
  {
1902
  "id": "new_kv_share_append_split",
1903
  "description": "Retains the existing cache and appends new key/value rows in separate passes before portable attention. It avoids rebuilding unchanged cache positions when the past and present allocations share capacity.",
 
210
  "CLUSTER_TILE_K_F16": 8,
211
  "NO_SG_TILE_K_MAX": 16,
212
  "COOPERATIVE_WORKGROUP_SIZE": 32,
213
+ "CLUSTER_MAX_SLICE": 8,
214
+ "MATERIALIZED_SGMAT_QUERY_TILE": 64,
215
+ "MATERIALIZED_SGMAT_KEY_TILE": 64,
216
+ "MATERIALIZED_SGMAT_INNER_TILE": 32,
217
+ "MATERIALIZED_SOFTMAX_WORKGROUP_SIZE": 256,
218
+ "MATERIALIZED_SGMAT_MIN_SEQ": 256,
219
+ "MATERIALIZED_SGMAT_PAST_MIN_SEQ": 256,
220
+ "MATERIALIZED_SGMAT_WINDOW_MIN_QSEQ": 128
221
  },
222
  "derive": {
223
  "deviceWorkgroupCap": "min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX)",
224
  "wave32Adapter": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize == 32 and device.adapterInfo.subgroupMaxSize == 32",
225
  "subgroupsWave32": "device.features.has(\"subgroups\") and wave32Adapter",
226
  "narrowSubgroupRange": "has(device.adapterInfo, \"subgroupMinSize\") and has(device.adapterInfo, \"subgroupMaxSize\") and device.adapterInfo.subgroupMinSize < device.adapterInfo.subgroupMaxSize and device.adapterInfo.subgroupMaxSize <= 16",
227
+ "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",
228
+ "pinSubgroupSize32": "canPinSubgroupSize32 and not wave32Adapter",
229
+ "wave32Effective": "wave32Adapter or pinSubgroupSize32",
230
  "windowCacheRequested": "attrs.sliding_window_cache == 1",
231
  "headDim": "dim(shapes.query, 2) / attrs.num_heads if (ranks.query == 3 and attrs.num_heads > 0) else 0",
232
  "copyWorkgroupSize": "min(tunables.COPY_WORKGROUP_SIZE, min(device.limits.maxComputeInvocationsPerWorkgroup, device.limits.maxComputeWorkgroupSizeX))",
 
320
  "cachedNoSgTileBytes": "gqaHeadDim * (8 if tensorDtypes.queryT == \"float32\" else 4) + cachedNoSgReductionBytesPerKey",
321
  "cachedNoSgTileK": "min(tunables.NO_SG_TILE_K_MAX, max(1, floor(device.limits.maxComputeWorkgroupStorageSize / cachedNoSgTileBytes)))",
322
  "cachedNoSgWorkgroupBytes": "cachedNoSgTileK * cachedNoSgTileBytes",
323
+ "cachedNoSgClusterStorageOk": "cachedNoSgWorkgroupBytes <= device.limits.maxComputeWorkgroupStorageSize",
324
+ "gqaMatQueryTile": "tunables.MATERIALIZED_SGMAT_QUERY_TILE",
325
+ "gqaMatKeyTile": "tunables.MATERIALIZED_SGMAT_KEY_TILE",
326
+ "gqaMatInnerTile": "tunables.MATERIALIZED_SGMAT_INNER_TILE",
327
+ "gqaMatSubgroupRows": "floor(gqaMatQueryTile / 16)",
328
+ "gqaMatSubgroupCols": "floor(gqaMatKeyTile / 32)",
329
+ "gqaMatWorkgroupSize": "gqaMatSubgroupRows * gqaMatSubgroupCols * 32",
330
+ "gqaMatCompactStorageBytes": "(gqaMatQueryTile + gqaMatKeyTile) * gqaMatInnerTile * 4",
331
+ "gqaMatGeometryOk": "gqaMatQueryTile >= 16 and gqaMatQueryTile % 16 == 0 and gqaMatKeyTile >= 32 and gqaMatKeyTile <= 64 and gqaMatKeyTile % 32 == 0 and gqaMatInnerTile == 32 and gqaMatQueryTile >= 64 and gqaMatKeyTile >= 64",
332
+ "gqaMatResourcesFit": "gqaMatGeometryOk and gqaMatWorkgroupSize <= deviceWorkgroupCap and gqaMatCompactStorageBytes <= device.limits.maxComputeWorkgroupStorageSize",
333
+ "gqaMatScoreBytes": "dim(shapes.query, 0) * attrs.num_heads * dim(shapes.query, 1) * dim(shapes.key, 1) * 4",
334
+ "gqaMatScoreFits": "gqaMatScoreBytes <= device.limits.maxStorageBufferBindingSize and gqaMatScoreBytes <= device.limits.maxBufferSize",
335
+ "gqaMatBuffersFit": "numel(shapes.query) * 4 <= device.limits.maxStorageBufferBindingSize and numel(shapes.key) * 4 <= device.limits.maxStorageBufferBindingSize and numel(shapes.value) * 4 <= device.limits.maxStorageBufferBindingSize",
336
+ "gqaMatDispatchFits": "ceilDiv(dim(shapes.key, 1), gqaMatKeyTile) <= device.limits.maxComputeWorkgroupsPerDimension and ceilDiv(dim(shapes.query, 1), gqaMatQueryTile) <= device.limits.maxComputeWorkgroupsPerDimension and dim(shapes.query, 0) * attrs.num_heads <= device.limits.maxComputeWorkgroupsPerDimension",
337
+ "gqaMatStatSlots": "ceilDiv(dim(shapes.key, 1), gqaMatKeyTile) * gqaMatSubgroupCols",
338
+ "gqaMatRowStatsWg": "min(tunables.MATERIALIZED_SOFTMAX_WORKGROUP_SIZE, deviceWorkgroupCap)",
339
+ "gqaMatRowStatsElements": "dim(shapes.query, 0) * attrs.num_heads * dim(shapes.query, 1) * 2",
340
+ "gqaMatScorePartialElements": "dim(shapes.query, 0) * attrs.num_heads * gqaMatStatSlots * dim(shapes.query, 1) * 2",
341
+ "gqaMatCoreOk": "qkvContractOk and attrs.causal == 0 and tensorDtypes.query == \"float32\" and attrs.local_window_size == -1 and headDim >= 64 and headDim <= 256 and headDim % 16 == 0 and dim(shapes.query, 1) >= tunables.MATERIALIZED_SGMAT_MIN_SEQ and dim(shapes.key, 1) >= tunables.MATERIALIZED_SGMAT_MIN_SEQ and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatScoreFits and gqaMatBuffersFit and gqaMatResourcesFit and gqaMatDispatchFits",
342
+ "gqaMatPastQSeq": "dim(shapes.queryT, 1)",
343
+ "gqaMatPastKvSeq": "dim(shapes.presentKeyT, 2)",
344
+ "gqaMatPastScoreBytes": "dim(shapes.queryT, 0) * attrs.num_heads * gqaMatPastQSeq * gqaMatPastKvSeq * 4",
345
+ "gqaMatPastScoreFits": "gqaMatPastScoreBytes <= device.limits.maxStorageBufferBindingSize and gqaMatPastScoreBytes <= device.limits.maxBufferSize",
346
+ "gqaMatPastBuffersFit": "numel(shapes.queryT) * 4 <= device.limits.maxStorageBufferBindingSize and numel(shapes.presentKeyT) * 4 <= device.limits.maxStorageBufferBindingSize and numel(shapes.presentValueT) * 4 <= device.limits.maxStorageBufferBindingSize",
347
+ "gqaMatPastStatSlots": "ceilDiv(gqaMatPastKvSeq, gqaMatKeyTile) * gqaMatSubgroupCols",
348
+ "gqaMatPastRowStatsElements": "dim(shapes.queryT, 0) * attrs.num_heads * gqaMatPastQSeq * 2",
349
+ "gqaMatPastScorePartialElements": "dim(shapes.queryT, 0) * attrs.num_heads * gqaMatPastStatSlots * gqaMatPastQSeq * 2",
350
+ "gqaMatPastDispatchFits": "ceilDiv(gqaMatPastKvSeq, gqaMatKeyTile) <= device.limits.maxComputeWorkgroupsPerDimension and ceilDiv(gqaMatPastQSeq, gqaMatQueryTile) <= device.limits.maxComputeWorkgroupsPerDimension and dim(shapes.queryT, 0) * attrs.num_heads <= device.limits.maxComputeWorkgroupsPerDimension",
351
+ "gqaMatWindowOk": "windowShiftOk and plainAttentionOptions and tensorDtypes.queryT == \"float32\" and attrs.local_window_size > 0 and dim(shapes.queryT, 1) == dim(shapes.keyT, 1) and gqaHeadDim >= 64 and gqaHeadDim <= 256 and gqaHeadDim % 16 == 0 and dim(shapes.queryT, 1) >= tunables.MATERIALIZED_SGMAT_WINDOW_MIN_QSEQ and windowCapacity >= dim(shapes.queryT, 1) and attrs.local_window_size + dim(shapes.queryT, 1) >= windowCapacity and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatPastScoreFits and gqaMatPastBuffersFit and gqaMatResourcesFit and gqaMatPastDispatchFits",
352
+ "gqaMatPastCoreOk": "sharedKvFloatOk and plainAttentionOptions and tensorDtypes.queryT == \"float32\" and attrs.local_window_size == -1 and gqaHeadDim >= 64 and gqaHeadDim <= 256 and gqaHeadDim % 16 == 0 and gqaMatPastQSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= gqaMatPastQSeq and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatPastScoreFits and gqaMatPastBuffersFit and gqaMatResourcesFit and gqaMatPastDispatchFits",
353
+ "gqaMatPastRotaryOk": "sharedKvFloatOk and standardSoftmax and present.cosCacheT and rotaryRequested and not present.qNormWeightT and not present.attentionBiasT and not present.headSinkT and tensorDtypes.queryT == \"float32\" and attrs.local_window_size == -1 and gqaHeadDim >= 64 and gqaHeadDim <= 256 and gqaHeadDim % 16 == 0 and gqaHeadDim % 8 == 0 and gqaMatPastQSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= gqaMatPastQSeq and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatPastScoreFits and gqaMatPastBuffersFit and gqaMatResourcesFit and gqaMatPastDispatchFits",
354
+ "gqaMatNewPastOk": "newKvPastOk and plainAttentionOptions and tensorDtypes.queryT == \"float32\" and attrs.local_window_size == -1 and gqaHeadDim >= 64 and gqaHeadDim <= 256 and gqaHeadDim % 16 == 0 and gqaMatPastQSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= gqaMatPastQSeq and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatPastScoreFits and gqaMatPastBuffersFit and gqaMatResourcesFit and gqaMatPastDispatchFits",
355
+ "gqaMatPastCoreF16Ok": "sharedKvFloatOk and plainAttentionOptions and tensorDtypes.queryT == \"float16\" and device.features.has(\"shader-f16\") and attrs.local_window_size == -1 and gqaHeadDim >= 64 and gqaHeadDim <= 256 and gqaHeadDim % 16 == 0 and gqaMatPastQSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= gqaMatPastQSeq and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatPastScoreFits and gqaMatPastBuffersFit and gqaMatResourcesFit and gqaMatPastDispatchFits",
356
+ "gqaMatPastRotaryF16Ok": "sharedKvFloatOk and standardSoftmax and present.cosCacheT and rotaryRequested and not present.qNormWeightT and not present.attentionBiasT and not present.headSinkT and tensorDtypes.queryT == \"float16\" and device.features.has(\"shader-f16\") and attrs.local_window_size == -1 and gqaHeadDim >= 64 and gqaHeadDim <= 256 and gqaHeadDim % 16 == 0 and gqaHeadDim % 8 == 0 and gqaMatPastQSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= gqaMatPastQSeq and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatPastScoreFits and gqaMatPastBuffersFit and gqaMatResourcesFit and gqaMatPastDispatchFits",
357
+ "gqaMatPastBiasOk": "sharedKvFloatOk and standardSoftmax and not present.cosCacheT and not rotaryRequested and not present.qNormWeightT and not present.headSinkT and present.attentionBiasT and ranks.attentionBiasT == 4 and tensorDtypes.attentionBiasT == \"float32\" and (dim(shapes.attentionBiasT, 0) == dim(shapes.queryT, 0) or dim(shapes.attentionBiasT, 0) == 1) and (dim(shapes.attentionBiasT, 1) == attrs.num_heads or dim(shapes.attentionBiasT, 1) == 1) and dim(shapes.attentionBiasT, 2) == dim(shapes.queryT, 1) and dim(shapes.attentionBiasT, 3) == dim(shapes.presentKeyT, 2) and tensorDtypes.queryT == \"float32\" and attrs.local_window_size == -1 and gqaHeadDim >= 64 and gqaHeadDim <= 256 and gqaHeadDim % 16 == 0 and gqaMatPastQSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= tunables.MATERIALIZED_SGMAT_PAST_MIN_SEQ and gqaMatPastKvSeq >= gqaMatPastQSeq and device.features.has(\"subgroups\") and wave32Effective and device.features.has(\"chromium-experimental-subgroup-matrix\") and gqaMatPastScoreFits and gqaMatPastBuffersFit and gqaMatResourcesFit and gqaMatPastDispatchFits"
358
  },
359
  "bindingSets": {
360
  "splitAttention": [
 
1939
  ]
1940
  }
1941
  }
1942
+ ],
1943
+ "gqaMatScoreStats": [
1944
+ {
1945
+ "name": "query",
1946
+ "arg": "queryT",
1947
+ "semantic": "query",
1948
+ "buffer": { "type": "read-only-storage" },
1949
+ "elementType": "f32"
1950
+ },
1951
+ {
1952
+ "name": "key",
1953
+ "arg": "keyT",
1954
+ "semantic": "key",
1955
+ "buffer": { "type": "read-only-storage" },
1956
+ "elementType": "f32"
1957
+ },
1958
+ { "name": "scores", "semantic": "materializedScores", "buffer": { "type": "storage" }, "elementType": "f32" },
1959
+ {
1960
+ "name": "scorePartials",
1961
+ "semantic": "materializedScorePartials",
1962
+ "buffer": { "type": "storage" },
1963
+ "elementType": "f32"
1964
+ },
1965
+ {
1966
+ "name": "params",
1967
+ "semantic": "kernel.params",
1968
+ "buffer": { "type": "uniform" },
1969
+ "struct": {
1970
+ "name": "Params",
1971
+ "fields": [
1972
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.query, 1)" },
1973
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.key, 1)" },
1974
+ { "name": "scale", "type": "f32", "value": "attrs.scale if has(attrs, \"scale\") else 0" }
1975
+ ]
1976
+ }
1977
+ }
1978
+ ],
1979
+ "gqaMatRowStatsCombine": [
1980
+ {
1981
+ "name": "scorePartials",
1982
+ "semantic": "materializedScorePartials",
1983
+ "buffer": { "type": "read-only-storage" },
1984
+ "elementType": "f32"
1985
+ },
1986
+ { "name": "rowStats", "semantic": "materializedRowStats", "buffer": { "type": "storage" }, "elementType": "f32" },
1987
+ {
1988
+ "name": "params",
1989
+ "semantic": "kernel.params",
1990
+ "buffer": { "type": "uniform" },
1991
+ "struct": {
1992
+ "name": "Params",
1993
+ "fields": [
1994
+ { "name": "rows", "type": "u32", "value": "dim(shapes.query, 0) * attrs.num_heads * dim(shapes.query, 1)" }
1995
+ ]
1996
+ }
1997
+ }
1998
+ ],
1999
+ "gqaMatApplyFused": [
2000
+ {
2001
+ "name": "scores",
2002
+ "semantic": "materializedScores",
2003
+ "buffer": { "type": "read-only-storage" },
2004
+ "elementType": "f32"
2005
+ },
2006
+ {
2007
+ "name": "value",
2008
+ "arg": "valueT",
2009
+ "semantic": "value",
2010
+ "buffer": { "type": "read-only-storage" },
2011
+ "elementType": "f32"
2012
+ },
2013
+ {
2014
+ "name": "rowStats",
2015
+ "semantic": "materializedRowStats",
2016
+ "buffer": { "type": "read-only-storage" },
2017
+ "elementType": "f32"
2018
+ },
2019
+ {
2020
+ "name": "output",
2021
+ "arg": "outputT",
2022
+ "semantic": "output",
2023
+ "buffer": { "type": "storage" },
2024
+ "elementType": "f32"
2025
+ },
2026
+ {
2027
+ "name": "params",
2028
+ "semantic": "kernel.params",
2029
+ "buffer": { "type": "uniform" },
2030
+ "struct": {
2031
+ "name": "Params",
2032
+ "fields": [
2033
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.query, 1)" },
2034
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.key, 1)" }
2035
+ ]
2036
+ }
2037
+ }
2038
+ ],
2039
+ "gqaMatPastScoreStats": [
2040
+ { "name": "query", "arg": "queryT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2041
+ { "name": "key", "arg": "presentKeyT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2042
+ { "name": "scores", "semantic": "materializedScores", "buffer": { "type": "storage" }, "elementType": "f32" },
2043
+ {
2044
+ "name": "scorePartials",
2045
+ "semantic": "materializedScorePartials",
2046
+ "buffer": { "type": "storage" },
2047
+ "elementType": "f32"
2048
+ },
2049
+ {
2050
+ "name": "params",
2051
+ "semantic": "kernel.params",
2052
+ "buffer": { "type": "uniform" },
2053
+ "struct": {
2054
+ "name": "Params",
2055
+ "fields": [
2056
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2057
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" },
2058
+ { "name": "scale", "type": "f32", "value": "attrs.scale if attrs.scale else 0" }
2059
+ ]
2060
+ }
2061
+ }
2062
+ ],
2063
+ "gqaMatWindowScoreStats": [
2064
+ { "name": "query", "arg": "queryT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2065
+ { "name": "key", "arg": "presentKeyT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2066
+ { "name": "scores", "semantic": "materializedScores", "buffer": { "type": "storage" }, "elementType": "f32" },
2067
+ {
2068
+ "name": "scorePartials",
2069
+ "semantic": "materializedScorePartials",
2070
+ "buffer": { "type": "storage" },
2071
+ "elementType": "f32"
2072
+ },
2073
+ { "name": "seqlens_k", "arg": "seqlensKT", "buffer": { "type": "read-only-storage" }, "elementType": "i32" },
2074
+ {
2075
+ "name": "params",
2076
+ "semantic": "kernel.params",
2077
+ "buffer": { "type": "uniform" },
2078
+ "struct": {
2079
+ "name": "Params",
2080
+ "fields": [
2081
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2082
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" },
2083
+ { "name": "windowSize", "type": "u32", "value": "attrs.local_window_size" },
2084
+ { "name": "scale", "type": "f32", "value": "attrs.scale if attrs.scale else 0" }
2085
+ ]
2086
+ }
2087
+ }
2088
+ ],
2089
+ "gqaMatWindowApplyFused": [
2090
+ {
2091
+ "name": "scores",
2092
+ "semantic": "materializedScores",
2093
+ "buffer": { "type": "read-only-storage" },
2094
+ "elementType": "f32"
2095
+ },
2096
+ { "name": "value", "arg": "presentValueT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2097
+ {
2098
+ "name": "rowStats",
2099
+ "semantic": "materializedRowStats",
2100
+ "buffer": { "type": "read-only-storage" },
2101
+ "elementType": "f32"
2102
+ },
2103
+ { "name": "output", "arg": "outputT", "buffer": { "type": "storage" }, "elementType": "f32" },
2104
+ { "name": "seqlens_k", "arg": "seqlensKT", "buffer": { "type": "read-only-storage" }, "elementType": "i32" },
2105
+ {
2106
+ "name": "params",
2107
+ "semantic": "kernel.params",
2108
+ "buffer": { "type": "uniform" },
2109
+ "struct": {
2110
+ "name": "Params",
2111
+ "fields": [
2112
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2113
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" },
2114
+ { "name": "windowSize", "type": "u32", "value": "attrs.local_window_size" }
2115
+ ]
2116
+ }
2117
+ }
2118
+ ],
2119
+ "gqaMatPastRotaryScoreStats": [
2120
+ { "name": "query", "semantic": "qPrep", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2121
+ { "name": "key", "arg": "presentKeyT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2122
+ { "name": "scores", "semantic": "materializedScores", "buffer": { "type": "storage" }, "elementType": "f32" },
2123
+ {
2124
+ "name": "scorePartials",
2125
+ "semantic": "materializedScorePartials",
2126
+ "buffer": { "type": "storage" },
2127
+ "elementType": "f32"
2128
+ },
2129
+ {
2130
+ "name": "params",
2131
+ "semantic": "kernel.params",
2132
+ "buffer": { "type": "uniform" },
2133
+ "struct": {
2134
+ "name": "Params",
2135
+ "fields": [
2136
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2137
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" },
2138
+ { "name": "scale", "type": "f32", "value": "attrs.scale if attrs.scale else 0" }
2139
+ ]
2140
+ }
2141
+ }
2142
+ ],
2143
+ "rotaryQprep": [
2144
+ { "name": "query", "arg": "queryT", "buffer": { "type": "read-only-storage" }, "elementType": "$inputScalar" },
2145
+ {
2146
+ "name": "cos_cache",
2147
+ "arg": "cosCacheT",
2148
+ "buffer": { "type": "read-only-storage" },
2149
+ "elementType": "$cosScalar"
2150
+ },
2151
+ {
2152
+ "name": "sin_cache",
2153
+ "arg": "sinCacheT",
2154
+ "buffer": { "type": "read-only-storage" },
2155
+ "elementType": "$cosScalar"
2156
+ },
2157
+ { "name": "qout", "semantic": "qPrep", "buffer": { "type": "storage" }, "elementType": "vec4<f32>" },
2158
+ {
2159
+ "name": "params",
2160
+ "semantic": "kernel.params",
2161
+ "buffer": { "type": "uniform" },
2162
+ "struct": {
2163
+ "name": "Params",
2164
+ "fields": [
2165
+ { "name": "batch", "type": "u32", "value": "dim(shapes.queryT, 0)" },
2166
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2167
+ { "name": "totalSeq", "type": "u32", "value": "dim(shapes.pastKeyT, 2)" }
2168
+ ]
2169
+ }
2170
+ }
2171
+ ],
2172
+ "gqaMatPastRowStatsCombine": [
2173
+ {
2174
+ "name": "scorePartials",
2175
+ "semantic": "materializedScorePartials",
2176
+ "buffer": { "type": "read-only-storage" },
2177
+ "elementType": "f32"
2178
+ },
2179
+ { "name": "rowStats", "semantic": "materializedRowStats", "buffer": { "type": "storage" }, "elementType": "f32" },
2180
+ {
2181
+ "name": "params",
2182
+ "semantic": "kernel.params",
2183
+ "buffer": { "type": "uniform" },
2184
+ "struct": {
2185
+ "name": "Params",
2186
+ "fields": [
2187
+ {
2188
+ "name": "rows",
2189
+ "type": "u32",
2190
+ "value": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)"
2191
+ }
2192
+ ]
2193
+ }
2194
+ }
2195
+ ],
2196
+ "gqaMatPastApplyFused": [
2197
+ {
2198
+ "name": "scores",
2199
+ "semantic": "materializedScores",
2200
+ "buffer": { "type": "read-only-storage" },
2201
+ "elementType": "f32"
2202
+ },
2203
+ { "name": "value", "arg": "presentValueT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2204
+ {
2205
+ "name": "rowStats",
2206
+ "semantic": "materializedRowStats",
2207
+ "buffer": { "type": "read-only-storage" },
2208
+ "elementType": "f32"
2209
+ },
2210
+ { "name": "output", "arg": "outputT", "buffer": { "type": "storage" }, "elementType": "f32" },
2211
+ {
2212
+ "name": "params",
2213
+ "semantic": "kernel.params",
2214
+ "buffer": { "type": "uniform" },
2215
+ "struct": {
2216
+ "name": "Params",
2217
+ "fields": [
2218
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2219
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" }
2220
+ ]
2221
+ }
2222
+ }
2223
+ ],
2224
+ "gqaMatPastScoreStatsF16": [
2225
+ { "name": "query", "arg": "queryT", "buffer": { "type": "read-only-storage" }, "elementType": "f16" },
2226
+ { "name": "key", "arg": "presentKeyT", "buffer": { "type": "read-only-storage" }, "elementType": "f16" },
2227
+ { "name": "scores", "semantic": "materializedScores", "buffer": { "type": "storage" }, "elementType": "f32" },
2228
+ {
2229
+ "name": "scorePartials",
2230
+ "semantic": "materializedScorePartials",
2231
+ "buffer": { "type": "storage" },
2232
+ "elementType": "f32"
2233
+ },
2234
+ {
2235
+ "name": "params",
2236
+ "semantic": "kernel.params",
2237
+ "buffer": { "type": "uniform" },
2238
+ "struct": {
2239
+ "name": "Params",
2240
+ "fields": [
2241
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2242
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" },
2243
+ { "name": "scale", "type": "f32", "value": "attrs.scale if attrs.scale else 0" }
2244
+ ]
2245
+ }
2246
+ }
2247
+ ],
2248
+ "gqaMatPastApplyFusedF16": [
2249
+ {
2250
+ "name": "scores",
2251
+ "semantic": "materializedScores",
2252
+ "buffer": { "type": "read-only-storage" },
2253
+ "elementType": "f32"
2254
+ },
2255
+ { "name": "value", "arg": "presentValueT", "buffer": { "type": "read-only-storage" }, "elementType": "f16" },
2256
+ {
2257
+ "name": "rowStats",
2258
+ "semantic": "materializedRowStats",
2259
+ "buffer": { "type": "read-only-storage" },
2260
+ "elementType": "f32"
2261
+ },
2262
+ { "name": "output", "arg": "outputT", "buffer": { "type": "storage" }, "elementType": "f16" },
2263
+ {
2264
+ "name": "params",
2265
+ "semantic": "kernel.params",
2266
+ "buffer": { "type": "uniform" },
2267
+ "struct": {
2268
+ "name": "Params",
2269
+ "fields": [
2270
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2271
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" }
2272
+ ]
2273
+ }
2274
+ }
2275
+ ],
2276
+ "gqaMatPastRotaryScoreStatsF16": [
2277
+ { "name": "query", "semantic": "qPrep", "buffer": { "type": "read-only-storage" }, "elementType": "f16" },
2278
+ { "name": "key", "arg": "presentKeyT", "buffer": { "type": "read-only-storage" }, "elementType": "f16" },
2279
+ { "name": "scores", "semantic": "materializedScores", "buffer": { "type": "storage" }, "elementType": "f32" },
2280
+ {
2281
+ "name": "scorePartials",
2282
+ "semantic": "materializedScorePartials",
2283
+ "buffer": { "type": "storage" },
2284
+ "elementType": "f32"
2285
+ },
2286
+ {
2287
+ "name": "params",
2288
+ "semantic": "kernel.params",
2289
+ "buffer": { "type": "uniform" },
2290
+ "struct": {
2291
+ "name": "Params",
2292
+ "fields": [
2293
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2294
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" },
2295
+ { "name": "scale", "type": "f32", "value": "attrs.scale if attrs.scale else 0" }
2296
+ ]
2297
+ }
2298
+ }
2299
+ ],
2300
+ "rotaryQprepF16": [
2301
+ { "name": "query", "arg": "queryT", "buffer": { "type": "read-only-storage" }, "elementType": "$inputScalar" },
2302
+ {
2303
+ "name": "cos_cache",
2304
+ "arg": "cosCacheT",
2305
+ "buffer": { "type": "read-only-storage" },
2306
+ "elementType": "$cosScalar"
2307
+ },
2308
+ {
2309
+ "name": "sin_cache",
2310
+ "arg": "sinCacheT",
2311
+ "buffer": { "type": "read-only-storage" },
2312
+ "elementType": "$cosScalar"
2313
+ },
2314
+ { "name": "qout", "semantic": "qPrep", "buffer": { "type": "storage" }, "elementType": "vec4<f16>" },
2315
+ {
2316
+ "name": "params",
2317
+ "semantic": "kernel.params",
2318
+ "buffer": { "type": "uniform" },
2319
+ "struct": {
2320
+ "name": "Params",
2321
+ "fields": [
2322
+ { "name": "batch", "type": "u32", "value": "dim(shapes.queryT, 0)" },
2323
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2324
+ { "name": "totalSeq", "type": "u32", "value": "dim(shapes.pastKeyT, 2)" }
2325
+ ]
2326
+ }
2327
+ }
2328
+ ],
2329
+ "gqaMatPastBiasScoreStats": [
2330
+ { "name": "query", "arg": "queryT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2331
+ { "name": "key", "arg": "presentKeyT", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
2332
+ {
2333
+ "name": "attention_bias",
2334
+ "arg": "attentionBiasT",
2335
+ "buffer": { "type": "read-only-storage" },
2336
+ "elementType": "f32"
2337
+ },
2338
+ { "name": "scores", "semantic": "materializedScores", "buffer": { "type": "storage" }, "elementType": "f32" },
2339
+ {
2340
+ "name": "scorePartials",
2341
+ "semantic": "materializedScorePartials",
2342
+ "buffer": { "type": "storage" },
2343
+ "elementType": "f32"
2344
+ },
2345
+ {
2346
+ "name": "params",
2347
+ "semantic": "kernel.params",
2348
+ "buffer": { "type": "uniform" },
2349
+ "struct": {
2350
+ "name": "Params",
2351
+ "fields": [
2352
+ { "name": "qSeq", "type": "u32", "value": "dim(shapes.queryT, 1)" },
2353
+ { "name": "kvSeq", "type": "u32", "value": "dim(shapes.presentKeyT, 2)" },
2354
+ { "name": "scale", "type": "f32", "value": "attrs.scale if attrs.scale else 0" },
2355
+ { "name": "biasBatch", "type": "u32", "value": "dim(shapes.attentionBiasT, 0)" },
2356
+ { "name": "biasHeads", "type": "u32", "value": "dim(shapes.attentionBiasT, 1)" }
2357
+ ]
2358
+ }
2359
+ }
2360
  ]
2361
  },
2362
  "variants": [
2363
+ {
2364
+ "id": "qkv_present_materialized_sgmat_f32",
2365
+ "description": "Materialized float32 subgroup-matrix prefill for the bidirectional no-past qkv route, where `seqlens_k` is metadata-only like the flash routes: the score pass emits per-row softmax statistics, the apply pass folds the softmax, and the present copy is unchanged. It serves square-ish f32 prefill where the score and value GEMMs dominate.",
2366
+ "priority": 23,
2367
+ "requires": {
2368
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
2369
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
2370
+ },
2371
+ "when": ["gqaMatCoreOk"],
2372
+ "constants": {
2373
+ "qNumHeads": "attrs.num_heads",
2374
+ "kvNumHeads": "attrs.kv_num_heads",
2375
+ "headDim": "dim(shapes.query, 2) / attrs.num_heads",
2376
+ "qHidden": "dim(shapes.query, 2)",
2377
+ "kvHidden": "dim(shapes.key, 2)",
2378
+ "hasBias": false,
2379
+ "useSubgroups": true,
2380
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
2381
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
2382
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
2383
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
2384
+ "materializedSgmatDirectScoreStore": false,
2385
+ "materializedSgmatDirectApplyStore": false,
2386
+ "materializedSgmatRuntimeDirectStore": false,
2387
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
2388
+ "statSlots": "gqaMatStatSlots",
2389
+ "statQuerySeq": "dim(shapes.query, 1)",
2390
+ "presentScalar": "qkvCacheScalar",
2391
+ "presentElement": "qkvCacheVec4",
2392
+ "presentVec4": true,
2393
+ "copyWorkgroupSize": "copyWorkgroupSize",
2394
+ "inputElement": "qkvInputVec4",
2395
+ "headDimV4": "(dim(shapes.query, 2) / attrs.num_heads) / 4",
2396
+ "kvHiddenV4": "dim(shapes.key, 2) / 4"
2397
+ },
2398
+ "intermediates": [
2399
+ {
2400
+ "id": "materializedScores",
2401
+ "dtype": "float32",
2402
+ "shape": "[dim(shapes.query, 0) * attrs.num_heads * dim(shapes.query, 1) * dim(shapes.key, 1)]"
2403
+ },
2404
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatRowStatsElements]" },
2405
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatScorePartialElements]" }
2406
+ ],
2407
+ "passes": [
2408
+ {
2409
+ "id": "scores",
2410
+ "name": "GroupQueryAttention.MaterializedScoresSgmat",
2411
+ "source": {
2412
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2413
+ "inputs": { "phase": "\"score\"", "emitRowStats": true, "layout": "\"bsh\"" }
2414
+ },
2415
+ "bindings": "gqaMatScoreStats",
2416
+ "dispatch": {
2417
+ "x": "ceilDiv(dim(shapes.key, 1), gqaMatKeyTile)",
2418
+ "y": "ceilDiv(dim(shapes.query, 1), gqaMatQueryTile)",
2419
+ "z": "dim(shapes.query, 0) * attrs.num_heads"
2420
+ }
2421
+ },
2422
+ {
2423
+ "id": "rowstats",
2424
+ "name": "GroupQueryAttention.MaterializedRowStatsCombine",
2425
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja" },
2426
+ "bindings": "gqaMatRowStatsCombine",
2427
+ "dispatch": {
2428
+ "gridStride": "dim(shapes.query, 0) * attrs.num_heads * dim(shapes.query, 1)",
2429
+ "workgroupSize": "gqaMatRowStatsWg"
2430
+ }
2431
+ },
2432
+ {
2433
+ "id": "apply",
2434
+ "name": "GroupQueryAttention.MaterializedApplySgmat",
2435
+ "source": {
2436
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2437
+ "inputs": { "phase": "\"apply\"", "fusedSoftmax": true, "layout": "\"bsh\"" }
2438
+ },
2439
+ "bindings": "gqaMatApplyFused",
2440
+ "dispatch": {
2441
+ "x": "ceilDiv(headDim, gqaMatKeyTile)",
2442
+ "y": "ceilDiv(dim(shapes.query, 1), gqaMatQueryTile)",
2443
+ "z": "dim(shapes.query, 0) * attrs.num_heads"
2444
+ }
2445
+ },
2446
+ {
2447
+ "id": "present",
2448
+ "name": "GroupQueryAttention.MaterializedPresent",
2449
+ "shader": "gqa-present.wgsl.jinja",
2450
+ "bindings": "qkvPresent",
2451
+ "dispatch": { "threads": "numel(shapes.presentKeyT) / 4", "workgroupSize": "constants.copyWorkgroupSize" }
2452
+ }
2453
+ ]
2454
+ },
2455
+ {
2456
+ "id": "past_kv_materialized_sgmat_f32",
2457
+ "requires": {
2458
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
2459
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
2460
+ },
2461
+ "description": "Materializes the causal score matrix with float32 subgroup-matrix tiles over the shared float cache and applies the softmax-normalized weights with the same tiles. The score pass skips key tiles past each query tile's causal bound and the apply pass stops its reduction there, matching flash's triangular work at matrix-unit throughput.",
2462
+ "priority": 35,
2463
+ "when": ["gqaMatPastCoreOk"],
2464
+ "constants": {
2465
+ "qNumHeads": "attrs.num_heads",
2466
+ "kvNumHeads": "attrs.kv_num_heads",
2467
+ "headDim": "gqaHeadDim",
2468
+ "qHidden": "dim(shapes.queryT, 2)",
2469
+ "hasBias": false,
2470
+ "useSubgroups": true,
2471
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
2472
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
2473
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
2474
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
2475
+ "materializedSgmatDirectScoreStore": false,
2476
+ "materializedSgmatDirectApplyStore": false,
2477
+ "materializedSgmatRuntimeDirectStore": false,
2478
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
2479
+ "statSlots": "gqaMatPastStatSlots",
2480
+ "statQuerySeq": "dim(shapes.queryT, 1)",
2481
+ "mode": "\"copy\"",
2482
+ "packed": "gqaHeadDim",
2483
+ "kvHeads": "attrs.kv_num_heads",
2484
+ "inputScalar": "gqaScalar",
2485
+ "usesF16": false,
2486
+ "copyWorkgroupSize": "copyWorkgroupSize"
2487
+ },
2488
+ "intermediates": [
2489
+ {
2490
+ "id": "materializedScores",
2491
+ "dtype": "float32",
2492
+ "shape": "[dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1) * dim(shapes.presentKeyT, 2)]"
2493
+ },
2494
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatPastRowStatsElements]" },
2495
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatPastScorePartialElements]" }
2496
+ ],
2497
+ "passes": [
2498
+ {
2499
+ "id": "present",
2500
+ "name": "GroupQueryAttention.Present",
2501
+ "shader": "gqa-present.wgsl.jinja",
2502
+ "bindings": "presentCopy",
2503
+ "dispatch": {
2504
+ "threads": "dim(shapes.queryT, 0) * attrs.kv_num_heads * dim(shapes.pastKeyT, 2)",
2505
+ "workgroupSize": "copyWorkgroupSize"
2506
+ },
2507
+ "viewAlias": [{ "input": "src_k", "output": "present_key" }, { "input": "src_v", "output": "present_value" }]
2508
+ },
2509
+ {
2510
+ "id": "scores",
2511
+ "name": "GroupQueryAttention.PastMaterializedScores",
2512
+ "source": {
2513
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2514
+ "inputs": {
2515
+ "phase": "\"score\"",
2516
+ "emitRowStats": true,
2517
+ "layout": "\"bsh\"",
2518
+ "kvLayout": "\"bhsd\"",
2519
+ "causalRightAlign": true
2520
+ }
2521
+ },
2522
+ "bindings": "gqaMatPastScoreStats",
2523
+ "dispatch": {
2524
+ "x": "ceilDiv(dim(shapes.presentKeyT, 2), gqaMatKeyTile)",
2525
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2526
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2527
+ }
2528
+ },
2529
+ {
2530
+ "id": "rowstats",
2531
+ "name": "GroupQueryAttention.PastMaterializedRowStats",
2532
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja", "inputs": {} },
2533
+ "bindings": "gqaMatPastRowStatsCombine",
2534
+ "dispatch": {
2535
+ "gridStride": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
2536
+ "workgroupSize": "gqaMatRowStatsWg"
2537
+ }
2538
+ },
2539
+ {
2540
+ "id": "apply",
2541
+ "name": "GroupQueryAttention.PastMaterializedApply",
2542
+ "source": {
2543
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2544
+ "inputs": {
2545
+ "phase": "\"apply\"",
2546
+ "fusedSoftmax": true,
2547
+ "layout": "\"bsh\"",
2548
+ "kvLayout": "\"bhsd\"",
2549
+ "causalRightAlign": true
2550
+ }
2551
+ },
2552
+ "bindings": "gqaMatPastApplyFused",
2553
+ "dispatch": {
2554
+ "x": "ceilDiv(gqaHeadDim, gqaMatKeyTile)",
2555
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2556
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2557
+ }
2558
+ }
2559
+ ]
2560
+ },
2561
+ {
2562
+ "id": "past_kv_bias_materialized_sgmat_f32",
2563
+ "requires": {
2564
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
2565
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
2566
+ },
2567
+ "description": "Adds the additive attention bias (batch/head-broadcast at runtime) to the materialized causal scores as they are stored, so the emitted row statistics fold it in for free; everything else matches `past_kv_materialized_sgmat_f32`.",
2568
+ "priority": 36,
2569
+ "when": ["gqaMatPastBiasOk"],
2570
+ "constants": {
2571
+ "qNumHeads": "attrs.num_heads",
2572
+ "kvNumHeads": "attrs.kv_num_heads",
2573
+ "headDim": "gqaHeadDim",
2574
+ "qHidden": "dim(shapes.queryT, 2)",
2575
+ "hasBias": false,
2576
+ "useSubgroups": true,
2577
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
2578
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
2579
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
2580
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
2581
+ "materializedSgmatDirectScoreStore": false,
2582
+ "materializedSgmatDirectApplyStore": false,
2583
+ "materializedSgmatRuntimeDirectStore": false,
2584
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
2585
+ "statSlots": "gqaMatPastStatSlots",
2586
+ "statQuerySeq": "dim(shapes.queryT, 1)",
2587
+ "mode": "\"copy\"",
2588
+ "packed": "gqaHeadDim",
2589
+ "kvHeads": "attrs.kv_num_heads",
2590
+ "inputScalar": "gqaScalar",
2591
+ "usesF16": false,
2592
+ "copyWorkgroupSize": "copyWorkgroupSize"
2593
+ },
2594
+ "intermediates": [
2595
+ {
2596
+ "id": "materializedScores",
2597
+ "dtype": "float32",
2598
+ "shape": "[dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1) * dim(shapes.presentKeyT, 2)]"
2599
+ },
2600
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatPastRowStatsElements]" },
2601
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatPastScorePartialElements]" }
2602
+ ],
2603
+ "passes": [
2604
+ {
2605
+ "id": "present",
2606
+ "name": "GroupQueryAttention.Present",
2607
+ "shader": "gqa-present.wgsl.jinja",
2608
+ "bindings": "presentCopy",
2609
+ "dispatch": {
2610
+ "threads": "dim(shapes.queryT, 0) * attrs.kv_num_heads * dim(shapes.pastKeyT, 2)",
2611
+ "workgroupSize": "copyWorkgroupSize"
2612
+ },
2613
+ "viewAlias": [{ "input": "src_k", "output": "present_key" }, { "input": "src_v", "output": "present_value" }]
2614
+ },
2615
+ {
2616
+ "id": "scores",
2617
+ "name": "GroupQueryAttention.PastMaterializedScores",
2618
+ "source": {
2619
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2620
+ "inputs": {
2621
+ "phase": "\"score\"",
2622
+ "emitRowStats": true,
2623
+ "layout": "\"bsh\"",
2624
+ "kvLayout": "\"bhsd\"",
2625
+ "causalRightAlign": true,
2626
+ "scoreBias": true
2627
+ }
2628
+ },
2629
+ "bindings": "gqaMatPastBiasScoreStats",
2630
+ "dispatch": {
2631
+ "x": "ceilDiv(dim(shapes.presentKeyT, 2), gqaMatKeyTile)",
2632
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2633
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2634
+ }
2635
+ },
2636
+ {
2637
+ "id": "rowstats",
2638
+ "name": "GroupQueryAttention.PastMaterializedRowStats",
2639
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja", "inputs": {} },
2640
+ "bindings": "gqaMatPastRowStatsCombine",
2641
+ "dispatch": {
2642
+ "gridStride": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
2643
+ "workgroupSize": "gqaMatRowStatsWg"
2644
+ }
2645
+ },
2646
+ {
2647
+ "id": "apply",
2648
+ "name": "GroupQueryAttention.PastMaterializedApply",
2649
+ "source": {
2650
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2651
+ "inputs": {
2652
+ "phase": "\"apply\"",
2653
+ "fusedSoftmax": true,
2654
+ "layout": "\"bsh\"",
2655
+ "kvLayout": "\"bhsd\"",
2656
+ "causalRightAlign": true
2657
+ }
2658
+ },
2659
+ "bindings": "gqaMatPastApplyFused",
2660
+ "dispatch": {
2661
+ "x": "ceilDiv(gqaHeadDim, gqaMatKeyTile)",
2662
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2663
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2664
+ }
2665
+ }
2666
+ ]
2667
+ },
2668
+ {
2669
+ "id": "past_kv_materialized_sgmat_f16",
2670
+ "requires": {
2671
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
2672
+ "subgroupMatrixConfigs": [{ "componentType": "f16", "M": 8, "N": 8, "K": 8 }]
2673
+ },
2674
+ "description": "Materializes the causal score matrix with `f16` operand tiles feeding `f32`-accumulating subgroup matrices over the shared cache and applies the softmax-normalized weights the same way; scores and row statistics stay `f32`. The score pass skips key tiles past each query tile's causal bound and the apply pass stops its reduction there.",
2675
+ "priority": 35,
2676
+ "when": ["gqaMatPastCoreF16Ok"],
2677
+ "constants": {
2678
+ "qNumHeads": "attrs.num_heads",
2679
+ "kvNumHeads": "attrs.kv_num_heads",
2680
+ "headDim": "gqaHeadDim",
2681
+ "qHidden": "dim(shapes.queryT, 2)",
2682
+ "hasBias": false,
2683
+ "useSubgroups": true,
2684
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
2685
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
2686
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
2687
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
2688
+ "materializedSgmatDirectScoreStore": false,
2689
+ "materializedSgmatDirectApplyStore": false,
2690
+ "materializedSgmatRuntimeDirectStore": false,
2691
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
2692
+ "statSlots": "gqaMatPastStatSlots",
2693
+ "statQuerySeq": "dim(shapes.queryT, 1)",
2694
+ "mode": "\"copy\"",
2695
+ "packed": "gqaHeadDim",
2696
+ "kvHeads": "attrs.kv_num_heads",
2697
+ "inputScalar": "gqaScalar",
2698
+ "usesF16": true,
2699
+ "copyWorkgroupSize": "copyWorkgroupSize",
2700
+ "operandF16": true
2701
+ },
2702
+ "intermediates": [
2703
+ {
2704
+ "id": "materializedScores",
2705
+ "dtype": "float32",
2706
+ "shape": "[dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1) * dim(shapes.presentKeyT, 2)]"
2707
+ },
2708
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatPastRowStatsElements]" },
2709
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatPastScorePartialElements]" }
2710
+ ],
2711
+ "passes": [
2712
+ {
2713
+ "id": "present",
2714
+ "name": "GroupQueryAttention.Present",
2715
+ "shader": "gqa-present.wgsl.jinja",
2716
+ "bindings": "presentCopy",
2717
+ "dispatch": {
2718
+ "threads": "dim(shapes.queryT, 0) * attrs.kv_num_heads * dim(shapes.pastKeyT, 2)",
2719
+ "workgroupSize": "copyWorkgroupSize"
2720
+ },
2721
+ "viewAlias": [{ "input": "src_k", "output": "present_key" }, { "input": "src_v", "output": "present_value" }]
2722
+ },
2723
+ {
2724
+ "id": "scores",
2725
+ "name": "GroupQueryAttention.PastMaterializedScores",
2726
+ "source": {
2727
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2728
+ "inputs": {
2729
+ "phase": "\"score\"",
2730
+ "emitRowStats": true,
2731
+ "layout": "\"bsh\"",
2732
+ "kvLayout": "\"bhsd\"",
2733
+ "causalRightAlign": true
2734
+ }
2735
+ },
2736
+ "bindings": "gqaMatPastScoreStatsF16",
2737
+ "dispatch": {
2738
+ "x": "ceilDiv(dim(shapes.presentKeyT, 2), gqaMatKeyTile)",
2739
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2740
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2741
+ }
2742
+ },
2743
+ {
2744
+ "id": "rowstats",
2745
+ "name": "GroupQueryAttention.PastMaterializedRowStats",
2746
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja", "inputs": {} },
2747
+ "bindings": "gqaMatPastRowStatsCombine",
2748
+ "dispatch": {
2749
+ "gridStride": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
2750
+ "workgroupSize": "gqaMatRowStatsWg"
2751
+ }
2752
+ },
2753
+ {
2754
+ "id": "apply",
2755
+ "name": "GroupQueryAttention.PastMaterializedApply",
2756
+ "source": {
2757
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2758
+ "inputs": {
2759
+ "phase": "\"apply\"",
2760
+ "fusedSoftmax": true,
2761
+ "layout": "\"bsh\"",
2762
+ "kvLayout": "\"bhsd\"",
2763
+ "causalRightAlign": true
2764
+ }
2765
+ },
2766
+ "bindings": "gqaMatPastApplyFusedF16",
2767
+ "dispatch": {
2768
+ "x": "ceilDiv(gqaHeadDim, gqaMatKeyTile)",
2769
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2770
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2771
+ }
2772
+ }
2773
+ ]
2774
+ },
2775
+ {
2776
+ "id": "past_kv_rotary_materialized_sgmat_f32",
2777
+ "requires": {
2778
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
2779
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
2780
+ },
2781
+ "description": "Rotary twin of the causal materialized route: the shared cache already holds rotary-transformed keys, so one preparation pass rotates the query block at its absolute positions and the tile-skipping score/apply passes run unchanged on the prepared buffer.",
2782
+ "priority": 37,
2783
+ "when": ["gqaMatPastRotaryOk"],
2784
+ "constants": {
2785
+ "qNumHeads": "attrs.num_heads",
2786
+ "kvNumHeads": "attrs.kv_num_heads",
2787
+ "headDim": "gqaHeadDim",
2788
+ "qHidden": "dim(shapes.queryT, 2)",
2789
+ "hasBias": false,
2790
+ "useSubgroups": true,
2791
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
2792
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
2793
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
2794
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
2795
+ "materializedSgmatDirectScoreStore": false,
2796
+ "materializedSgmatDirectApplyStore": false,
2797
+ "materializedSgmatRuntimeDirectStore": false,
2798
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
2799
+ "statSlots": "gqaMatPastStatSlots",
2800
+ "statQuerySeq": "dim(shapes.queryT, 1)",
2801
+ "mode": "\"copy\"",
2802
+ "packed": "gqaHeadDim",
2803
+ "kvHeads": "attrs.kv_num_heads",
2804
+ "inputScalar": "gqaScalar",
2805
+ "usesF16": false,
2806
+ "copyWorkgroupSize": "copyWorkgroupSize",
2807
+ "half": "gqaHeadDim / 2",
2808
+ "qHeads": "attrs.num_heads",
2809
+ "cosScalar": "\"f16\" if tensorDtypes.cosCacheT == \"float16\" else \"f32\"",
2810
+ "hasQNorm": false
2811
+ },
2812
+ "intermediates": [
2813
+ {
2814
+ "id": "qPrep",
2815
+ "dtype": "float32",
2816
+ "shape": "[dim(shapes.queryT, 0), dim(shapes.queryT, 1), dim(shapes.queryT, 2)]"
2817
+ },
2818
+ {
2819
+ "id": "materializedScores",
2820
+ "dtype": "float32",
2821
+ "shape": "[dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1) * dim(shapes.presentKeyT, 2)]"
2822
+ },
2823
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatPastRowStatsElements]" },
2824
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatPastScorePartialElements]" }
2825
+ ],
2826
+ "passes": [
2827
+ {
2828
+ "id": "present",
2829
+ "name": "GroupQueryAttention.Present",
2830
+ "shader": "gqa-present.wgsl.jinja",
2831
+ "bindings": "presentCopy",
2832
+ "dispatch": {
2833
+ "threads": "dim(shapes.queryT, 0) * attrs.kv_num_heads * dim(shapes.pastKeyT, 2)",
2834
+ "workgroupSize": "copyWorkgroupSize"
2835
+ },
2836
+ "viewAlias": [{ "input": "src_k", "output": "present_key" }, { "input": "src_v", "output": "present_value" }]
2837
+ },
2838
+ {
2839
+ "id": "qprep",
2840
+ "name": "GroupQueryAttention.RotaryQPrep",
2841
+ "shader": "gqa-qprep.wgsl.jinja",
2842
+ "bindings": "rotaryQprep",
2843
+ "dispatch": {
2844
+ "threads": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
2845
+ "workgroupSize": "copyWorkgroupSize"
2846
+ }
2847
+ },
2848
+ {
2849
+ "id": "scores",
2850
+ "name": "GroupQueryAttention.PastRotaryMaterializedScores",
2851
+ "source": {
2852
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2853
+ "inputs": {
2854
+ "phase": "\"score\"",
2855
+ "emitRowStats": true,
2856
+ "layout": "\"bsh\"",
2857
+ "kvLayout": "\"bhsd\"",
2858
+ "causalRightAlign": true
2859
+ }
2860
+ },
2861
+ "bindings": "gqaMatPastRotaryScoreStats",
2862
+ "dispatch": {
2863
+ "x": "ceilDiv(dim(shapes.presentKeyT, 2), gqaMatKeyTile)",
2864
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2865
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2866
+ }
2867
+ },
2868
+ {
2869
+ "id": "rowstats",
2870
+ "name": "GroupQueryAttention.PastRotaryMaterializedRowStats",
2871
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja", "inputs": {} },
2872
+ "bindings": "gqaMatPastRowStatsCombine",
2873
+ "dispatch": {
2874
+ "gridStride": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
2875
+ "workgroupSize": "gqaMatRowStatsWg"
2876
+ }
2877
+ },
2878
+ {
2879
+ "id": "apply",
2880
+ "name": "GroupQueryAttention.PastRotaryMaterializedApply",
2881
+ "source": {
2882
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2883
+ "inputs": {
2884
+ "phase": "\"apply\"",
2885
+ "fusedSoftmax": true,
2886
+ "layout": "\"bsh\"",
2887
+ "kvLayout": "\"bhsd\"",
2888
+ "causalRightAlign": true
2889
+ }
2890
+ },
2891
+ "bindings": "gqaMatPastApplyFused",
2892
+ "dispatch": {
2893
+ "x": "ceilDiv(gqaHeadDim, gqaMatKeyTile)",
2894
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2895
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2896
+ }
2897
+ }
2898
+ ]
2899
+ },
2900
+ {
2901
+ "id": "past_kv_rotary_materialized_sgmat_f16",
2902
+ "requires": {
2903
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
2904
+ "subgroupMatrixConfigs": [{ "componentType": "f16", "M": 8, "N": 8, "K": 8 }]
2905
+ },
2906
+ "description": "Materializes the causal score matrix with `f16` operand tiles feeding `f32`-accumulating subgroup matrices over the shared cache and applies the softmax-normalized weights the same way; scores and row statistics stay `f32`. The score pass skips key tiles past each query tile's causal bound and the apply pass stops its reduction there.",
2907
+ "priority": 37,
2908
+ "when": ["gqaMatPastRotaryF16Ok"],
2909
+ "constants": {
2910
+ "qNumHeads": "attrs.num_heads",
2911
+ "kvNumHeads": "attrs.kv_num_heads",
2912
+ "headDim": "gqaHeadDim",
2913
+ "qHidden": "dim(shapes.queryT, 2)",
2914
+ "hasBias": false,
2915
+ "useSubgroups": true,
2916
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
2917
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
2918
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
2919
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
2920
+ "materializedSgmatDirectScoreStore": false,
2921
+ "materializedSgmatDirectApplyStore": false,
2922
+ "materializedSgmatRuntimeDirectStore": false,
2923
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
2924
+ "statSlots": "gqaMatPastStatSlots",
2925
+ "statQuerySeq": "dim(shapes.queryT, 1)",
2926
+ "mode": "\"copy\"",
2927
+ "packed": "gqaHeadDim",
2928
+ "kvHeads": "attrs.kv_num_heads",
2929
+ "inputScalar": "gqaScalar",
2930
+ "usesF16": true,
2931
+ "copyWorkgroupSize": "copyWorkgroupSize",
2932
+ "half": "gqaHeadDim / 2",
2933
+ "qHeads": "attrs.num_heads",
2934
+ "cosScalar": "\"f16\" if tensorDtypes.cosCacheT == \"float16\" else \"f32\"",
2935
+ "hasQNorm": false,
2936
+ "operandF16": true
2937
+ },
2938
+ "intermediates": [
2939
+ {
2940
+ "id": "qPrep",
2941
+ "dtype": "float16",
2942
+ "shape": "[dim(shapes.queryT, 0), dim(shapes.queryT, 1), dim(shapes.queryT, 2)]"
2943
+ },
2944
+ {
2945
+ "id": "materializedScores",
2946
+ "dtype": "float32",
2947
+ "shape": "[dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1) * dim(shapes.presentKeyT, 2)]"
2948
+ },
2949
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatPastRowStatsElements]" },
2950
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatPastScorePartialElements]" }
2951
+ ],
2952
+ "passes": [
2953
+ {
2954
+ "id": "present",
2955
+ "name": "GroupQueryAttention.Present",
2956
+ "shader": "gqa-present.wgsl.jinja",
2957
+ "bindings": "presentCopy",
2958
+ "dispatch": {
2959
+ "threads": "dim(shapes.queryT, 0) * attrs.kv_num_heads * dim(shapes.pastKeyT, 2)",
2960
+ "workgroupSize": "copyWorkgroupSize"
2961
+ },
2962
+ "viewAlias": [{ "input": "src_k", "output": "present_key" }, { "input": "src_v", "output": "present_value" }]
2963
+ },
2964
+ {
2965
+ "id": "qprep",
2966
+ "name": "GroupQueryAttention.RotaryQPrep",
2967
+ "shader": "gqa-qprep.wgsl.jinja",
2968
+ "bindings": "rotaryQprepF16",
2969
+ "dispatch": {
2970
+ "threads": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
2971
+ "workgroupSize": "copyWorkgroupSize"
2972
+ },
2973
+ "constants": { "qPrepF16": true }
2974
+ },
2975
+ {
2976
+ "id": "scores",
2977
+ "name": "GroupQueryAttention.PastRotaryMaterializedScores",
2978
+ "source": {
2979
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
2980
+ "inputs": {
2981
+ "phase": "\"score\"",
2982
+ "emitRowStats": true,
2983
+ "layout": "\"bsh\"",
2984
+ "kvLayout": "\"bhsd\"",
2985
+ "causalRightAlign": true
2986
+ }
2987
+ },
2988
+ "bindings": "gqaMatPastRotaryScoreStatsF16",
2989
+ "dispatch": {
2990
+ "x": "ceilDiv(dim(shapes.presentKeyT, 2), gqaMatKeyTile)",
2991
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
2992
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
2993
+ }
2994
+ },
2995
+ {
2996
+ "id": "rowstats",
2997
+ "name": "GroupQueryAttention.PastRotaryMaterializedRowStats",
2998
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja", "inputs": {} },
2999
+ "bindings": "gqaMatPastRowStatsCombine",
3000
+ "dispatch": {
3001
+ "gridStride": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
3002
+ "workgroupSize": "gqaMatRowStatsWg"
3003
+ }
3004
+ },
3005
+ {
3006
+ "id": "apply",
3007
+ "name": "GroupQueryAttention.PastRotaryMaterializedApply",
3008
+ "source": {
3009
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
3010
+ "inputs": {
3011
+ "phase": "\"apply\"",
3012
+ "fusedSoftmax": true,
3013
+ "layout": "\"bsh\"",
3014
+ "kvLayout": "\"bhsd\"",
3015
+ "causalRightAlign": true
3016
+ }
3017
+ },
3018
+ "bindings": "gqaMatPastApplyFusedF16",
3019
+ "dispatch": {
3020
+ "x": "ceilDiv(gqaHeadDim, gqaMatKeyTile)",
3021
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
3022
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
3023
+ }
3024
+ }
3025
+ ]
3026
+ },
3027
+ {
3028
+ "id": "new_kv_past_materialized_sgmat_f32",
3029
+ "requires": {
3030
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
3031
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
3032
+ },
3033
+ "description": "Append-route twin of the causal materialized route for chunked prefill: one merge pass concatenates the past cache with the new key/value rows, and the tile-skipping score/apply passes run over the merged cache with the same right-aligned causal bound.",
3034
+ "priority": 35,
3035
+ "when": ["gqaMatNewPastOk"],
3036
+ "constants": {
3037
+ "qNumHeads": "attrs.num_heads",
3038
+ "kvNumHeads": "attrs.kv_num_heads",
3039
+ "headDim": "gqaHeadDim",
3040
+ "qHidden": "dim(shapes.queryT, 2)",
3041
+ "hasBias": false,
3042
+ "useSubgroups": true,
3043
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
3044
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
3045
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
3046
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
3047
+ "materializedSgmatDirectScoreStore": false,
3048
+ "materializedSgmatDirectApplyStore": false,
3049
+ "materializedSgmatRuntimeDirectStore": false,
3050
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
3051
+ "statSlots": "gqaMatPastStatSlots",
3052
+ "statQuerySeq": "dim(shapes.queryT, 1)",
3053
+ "mode": "\"merge\"",
3054
+ "packed": "gqaHeadDim",
3055
+ "kvHeads": "attrs.kv_num_heads",
3056
+ "inputScalar": "gqaScalar",
3057
+ "usesF16": false,
3058
+ "copyWorkgroupSize": "copyWorkgroupSize"
3059
+ },
3060
+ "intermediates": [
3061
+ {
3062
+ "id": "materializedScores",
3063
+ "dtype": "float32",
3064
+ "shape": "[dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1) * dim(shapes.presentKeyT, 2)]"
3065
+ },
3066
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatPastRowStatsElements]" },
3067
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatPastScorePartialElements]" }
3068
+ ],
3069
+ "passes": [
3070
+ {
3071
+ "id": "present",
3072
+ "name": "GroupQueryAttention.Merge",
3073
+ "shader": "gqa-present.wgsl.jinja",
3074
+ "bindings": "presentMerge",
3075
+ "dispatch": {
3076
+ "threads": "dim(shapes.queryT, 0) * attrs.kv_num_heads * dim(shapes.presentKeyT, 2)",
3077
+ "workgroupSize": "copyWorkgroupSize"
3078
+ }
3079
+ },
3080
+ {
3081
+ "id": "scores",
3082
+ "name": "GroupQueryAttention.NewPastMaterializedScores",
3083
+ "source": {
3084
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
3085
+ "inputs": {
3086
+ "phase": "\"score\"",
3087
+ "emitRowStats": true,
3088
+ "layout": "\"bsh\"",
3089
+ "kvLayout": "\"bhsd\"",
3090
+ "causalRightAlign": true
3091
+ }
3092
+ },
3093
+ "bindings": "gqaMatPastScoreStats",
3094
+ "dispatch": {
3095
+ "x": "ceilDiv(dim(shapes.presentKeyT, 2), gqaMatKeyTile)",
3096
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
3097
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
3098
+ }
3099
+ },
3100
+ {
3101
+ "id": "rowstats",
3102
+ "name": "GroupQueryAttention.NewPastMaterializedRowStats",
3103
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja", "inputs": {} },
3104
+ "bindings": "gqaMatPastRowStatsCombine",
3105
+ "dispatch": {
3106
+ "gridStride": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
3107
+ "workgroupSize": "gqaMatRowStatsWg"
3108
+ }
3109
+ },
3110
+ {
3111
+ "id": "apply",
3112
+ "name": "GroupQueryAttention.NewPastMaterializedApply",
3113
+ "source": {
3114
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
3115
+ "inputs": {
3116
+ "phase": "\"apply\"",
3117
+ "fusedSoftmax": true,
3118
+ "layout": "\"bsh\"",
3119
+ "kvLayout": "\"bhsd\"",
3120
+ "causalRightAlign": true
3121
+ }
3122
+ },
3123
+ "bindings": "gqaMatPastApplyFused",
3124
+ "dispatch": {
3125
+ "x": "ceilDiv(gqaHeadDim, gqaMatKeyTile)",
3126
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
3127
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
3128
+ }
3129
+ }
3130
+ ]
3131
+ },
3132
+ {
3133
+ "id": "window_shift_materialized_sgmat_f32",
3134
+ "requires": {
3135
+ "features": ["subgroups", "chromium-experimental-subgroup-matrix"],
3136
+ "subgroupMatrixConfigs": [{ "componentType": "f32", "resultComponentType": "f32", "M": 8, "N": 8, "K": 8 }]
3137
+ },
3138
+ "description": "Windowed-cache twin of the causal materialized route for chunked prefill: the shift pass compacts surviving cache rows and appends the chunk, the score pass masks each row's sliding-window floor alongside the causal bound so the row statistics fold both out, and the apply pass starts each reduction at the first key tile the window can reach.",
3139
+ "priority": 36,
3140
+ "when": ["gqaMatWindowOk"],
3141
+ "constants": {
3142
+ "qNumHeads": "attrs.num_heads",
3143
+ "kvNumHeads": "attrs.kv_num_heads",
3144
+ "headDim": "gqaHeadDim",
3145
+ "qHidden": "dim(shapes.queryT, 2)",
3146
+ "hasBias": false,
3147
+ "useSubgroups": true,
3148
+ "attentionScaleExpression": "\"select(inverseSqrt(f32(HEAD_DIM)), params.scale, params.scale != 0.0)\"",
3149
+ "materializedSgmatQueryTile": "gqaMatQueryTile",
3150
+ "materializedSgmatKeyTile": "gqaMatKeyTile",
3151
+ "materializedSgmatInnerTile": "gqaMatInnerTile",
3152
+ "materializedSgmatDirectScoreStore": false,
3153
+ "materializedSgmatDirectApplyStore": false,
3154
+ "materializedSgmatRuntimeDirectStore": false,
3155
+ "materializedRowStatsWg": "gqaMatRowStatsWg",
3156
+ "statSlots": "gqaMatPastStatSlots",
3157
+ "statQuerySeq": "dim(shapes.queryT, 1)",
3158
+ "mode": "\"window_shift\"",
3159
+ "packed": "gqaHeadDim",
3160
+ "kvHeads": "attrs.kv_num_heads",
3161
+ "inputScalar": "gqaScalar",
3162
+ "zeroScalar": "gqaScalar",
3163
+ "usesF16": false,
3164
+ "copyWorkgroupSize": "copyWorkgroupSize"
3165
+ },
3166
+ "intermediates": [
3167
+ {
3168
+ "id": "materializedScores",
3169
+ "dtype": "float32",
3170
+ "shape": "[dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1) * dim(shapes.presentKeyT, 2)]"
3171
+ },
3172
+ { "id": "materializedRowStats", "dtype": "float32", "shape": "[gqaMatPastRowStatsElements]" },
3173
+ { "id": "materializedScorePartials", "dtype": "float32", "shape": "[gqaMatPastScorePartialElements]" }
3174
+ ],
3175
+ "passes": [
3176
+ {
3177
+ "id": "present",
3178
+ "name": "GroupQueryAttention.WindowShift",
3179
+ "shader": "gqa-present.wgsl.jinja",
3180
+ "bindings": "presentMergeShare",
3181
+ "dispatch": {
3182
+ "threads": "dim(shapes.queryT, 0) * attrs.kv_num_heads * dim(shapes.presentKeyT, 2)",
3183
+ "workgroupSize": "copyWorkgroupSize"
3184
+ }
3185
+ },
3186
+ {
3187
+ "id": "scores",
3188
+ "name": "GroupQueryAttention.WindowMaterializedScores",
3189
+ "source": {
3190
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
3191
+ "inputs": {
3192
+ "phase": "\"score\"",
3193
+ "emitRowStats": true,
3194
+ "layout": "\"bsh\"",
3195
+ "kvLayout": "\"bhsd\"",
3196
+ "causalRightAlign": true,
3197
+ "scoreWindow": true,
3198
+ "useSeqlens": true
3199
+ }
3200
+ },
3201
+ "bindings": "gqaMatWindowScoreStats",
3202
+ "dispatch": {
3203
+ "x": "ceilDiv(dim(shapes.presentKeyT, 2), gqaMatKeyTile)",
3204
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
3205
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
3206
+ }
3207
+ },
3208
+ {
3209
+ "id": "rowstats",
3210
+ "name": "GroupQueryAttention.WindowMaterializedRowStats",
3211
+ "source": { "shader": "attn-materialized-rowstats-combine-f32.wgsl.jinja", "inputs": {} },
3212
+ "bindings": "gqaMatPastRowStatsCombine",
3213
+ "dispatch": {
3214
+ "gridStride": "dim(shapes.queryT, 0) * attrs.num_heads * dim(shapes.queryT, 1)",
3215
+ "workgroupSize": "gqaMatRowStatsWg"
3216
+ }
3217
+ },
3218
+ {
3219
+ "id": "apply",
3220
+ "name": "GroupQueryAttention.WindowMaterializedApply",
3221
+ "source": {
3222
+ "shader": "attn-materialized-sgmat-f32.wgsl.jinja",
3223
+ "inputs": {
3224
+ "phase": "\"apply\"",
3225
+ "fusedSoftmax": true,
3226
+ "layout": "\"bsh\"",
3227
+ "kvLayout": "\"bhsd\"",
3228
+ "causalRightAlign": true,
3229
+ "scoreWindow": true,
3230
+ "useSeqlens": true
3231
+ }
3232
+ },
3233
+ "bindings": "gqaMatWindowApplyFused",
3234
+ "dispatch": {
3235
+ "x": "ceilDiv(gqaHeadDim, gqaMatKeyTile)",
3236
+ "y": "ceilDiv(dim(shapes.queryT, 1), gqaMatQueryTile)",
3237
+ "z": "dim(shapes.queryT, 0) * attrs.num_heads"
3238
+ }
3239
+ }
3240
+ ]
3241
+ },
3242
  {
3243
  "id": "new_kv_share_append_split",
3244
  "description": "Retains the existing cache and appends new key/value rows in separate passes before portable attention. It avoids rebuilding unchanged cache positions when the past and present allocations share capacity.",
build/webgpu/metadata.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "com.microsoft.GroupQueryAttention",
3
- "id": "_com_microsoft_groupqueryattention_webgpu_75fbc9b",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
@@ -13,15 +13,17 @@
13
  "attn-flash-online.wgsl.jinja": "MStTMF1/l5L9Rzx1+rPz5T+70/46n/vO/+hZQ0lbFws=",
14
  "attn-flash-prefill-cluster.wgsl.jinja": "yk1WymJIbxCy0slbiho5DS223uZH9PQ15629yvQ9RFc=",
15
  "attn-flash-q32-broadcast.wgsl.jinja": "evSdaiYgvrikYZ8hpb5m4gxf+rIryKkHOTiKgyF7ioI=",
 
 
16
  "attn-online-scalar.wgsl.jinja": "X4yGNCYu9+uJX6Q3EKkxV6v28CPlIYWme0Nmf8F16L8=",
17
- "bench.json": "rGPC3cQInjJZjL+u+MzVXCOGu4L+G0v+/0UGDI9DEuQ=",
18
  "gqa-attention.wgsl.jinja": "0EbwmdstNZMuskS170zYEo8whbQOFtupXLnNP2PaFL0=",
19
  "gqa-present.wgsl.jinja": "J9g6oAeY2BtbmWBMlmbIXbxzo4sKHdxYqcE4EAd7BYM=",
20
- "gqa-qprep.wgsl.jinja": "SvA19/AQoN2FXduwBtMI5QzBeeth2y9g8EnaIkV3V/0=",
21
- "manifest.json": "qODDtl2Snem6J/K6QJwIL5i6CnGQ898t0a/EEka4JnY=",
22
- "test.json": "iZAuQQINr2i9Q2hwXzNhKO+UT+ByfRwPsNc+paVhR20="
23
  }
24
  },
25
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
26
  "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.GroupQueryAttention" }
27
  }
 
1
  {
2
  "name": "com.microsoft.GroupQueryAttention",
3
+ "id": "_com_microsoft_groupqueryattention_webgpu_34ecc1b",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
 
13
  "attn-flash-online.wgsl.jinja": "MStTMF1/l5L9Rzx1+rPz5T+70/46n/vO/+hZQ0lbFws=",
14
  "attn-flash-prefill-cluster.wgsl.jinja": "yk1WymJIbxCy0slbiho5DS223uZH9PQ15629yvQ9RFc=",
15
  "attn-flash-q32-broadcast.wgsl.jinja": "evSdaiYgvrikYZ8hpb5m4gxf+rIryKkHOTiKgyF7ioI=",
16
+ "attn-materialized-rowstats-combine-f32.wgsl.jinja": "TtTBeeeP/ufhzeNHoeLWGLxKTfFT9fiEAstnXB79j48=",
17
+ "attn-materialized-sgmat-f32.wgsl.jinja": "iQeacl5PUCl75lx78qlSBaQ/syBDNxgjZg4UPGki9U0=",
18
  "attn-online-scalar.wgsl.jinja": "X4yGNCYu9+uJX6Q3EKkxV6v28CPlIYWme0Nmf8F16L8=",
19
+ "bench.json": "b4ZLhmCqTm6AgiNfIiRHYVeNp4q1mx12GMcDAextGqI=",
20
  "gqa-attention.wgsl.jinja": "0EbwmdstNZMuskS170zYEo8whbQOFtupXLnNP2PaFL0=",
21
  "gqa-present.wgsl.jinja": "J9g6oAeY2BtbmWBMlmbIXbxzo4sKHdxYqcE4EAd7BYM=",
22
+ "gqa-qprep.wgsl.jinja": "RCBDy7LA/yy4ayRAsnyq+7/IaXjsDGmPWidj51c+t8k=",
23
+ "manifest.json": "oxSz/yE5vrd9vU5lHkrRN2kG8sdBzWQmGP9/jjRoqNU=",
24
+ "test.json": "zfjYb8H4THOE5qN8K90UPdGq0lL/B2LWarbvokAz8Cg="
25
  }
26
  },
27
+ "provenance": { "kernel": { "sha": "c928d21e6cc1310861cba3bafb75f5f679ecf5f3", "dirty": false } },
28
  "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/com.microsoft.GroupQueryAttention" }
29
  }
build/webgpu/test.json CHANGED
@@ -4271,6 +4271,205 @@
4271
  "notes": "Chunked flash prefill into a windowed cache: the present pass compacts entries, the attention bound comes from seqlens_k (kvActive), and the batch stride remains the cache capacity. Batch 0 is unfilled at T=40 while batch 1 evicts at T=80, requiring kvActive to be tracked per batch."
4272
  }
4273
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4274
  {
4275
  "name": "share_append_flash_chunk_slack_b1q32cap64_h2kv1d64",
4276
  "attrs": { "num_heads": 2, "kv_num_heads": 1, "local_window_size": 16 },
@@ -4655,6 +4854,526 @@
4655
  "presentKeyT": { "dtype": "uint8", "shape": [1, 1, 4, 4], "tolerance": 0 },
4656
  "presentValueT": { "dtype": "uint8", "shape": [1, 1, 4, 4], "tolerance": 0 }
4657
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4658
  }
4659
  ]
4660
  }
 
4271
  "notes": "Chunked flash prefill into a windowed cache: the present pass compacts entries, the attention bound comes from seqlens_k (kvActive), and the batch stride remains the cache capacity. Batch 0 is unfilled at T=40 while batch 1 evicts at T=80, requiring kvActive to be tracked per batch."
4272
  }
4273
  },
4274
+ {
4275
+ "name": "window_cache_sgmat_chunk_evicts_b1q128cap256_h2kv1d128_w256",
4276
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "sliding_window_cache": 1, "local_window_size": 256 },
4277
+ "inputs": {
4278
+ "queryT": {
4279
+ "dtype": "float32",
4280
+ "shape": [1, 128, 256],
4281
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27, "scale": 0.5 }
4282
+ },
4283
+ "keyT": {
4284
+ "dtype": "float32",
4285
+ "shape": [1, 128, 128],
4286
+ "data": { "kind": "linspace", "start": -1.0, "end": 1.0 }
4287
+ },
4288
+ "valueT": {
4289
+ "dtype": "float32",
4290
+ "shape": [1, 128, 128],
4291
+ "data": { "kind": "linspace", "start": 0.0, "end": 2.0 }
4292
+ },
4293
+ "pastKeyT": {
4294
+ "dtype": "float32",
4295
+ "shape": [1, 1, 256, 128],
4296
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.31, "scale": 0.5 }
4297
+ },
4298
+ "pastValueT": {
4299
+ "dtype": "float32",
4300
+ "shape": [1, 1, 256, 128],
4301
+ "data": { "kind": "linspace", "start": 0.0, "end": 4.0 }
4302
+ },
4303
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [383] } },
4304
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [384] } }
4305
+ },
4306
+ "outputs": {
4307
+ "outputT": { "dtype": "float32", "shape": [1, 128, 256], "tolerance": 0.005 },
4308
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 256, 128], "tolerance": 0.0001 },
4309
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 256, 128], "tolerance": 0.0001 }
4310
+ },
4311
+ "provenance": {
4312
+ "source": "onnxruntime/contrib_ops/cpu/bert/group_query_attention.cc",
4313
+ "test": "GQA sliding_window_cache chunk prefill on the materialized subgroup-matrix route",
4314
+ "notes": "T=384 > C=256 evicts 128 rows; w=256=C keeps the floor inactive, so this pins the shift + right-aligned causal path of the materialized window route at d=128 (two apply column tiles)."
4315
+ }
4316
+ },
4317
+ {
4318
+ "name": "window_cache_sgmat_chunk_floor_b1q128cap384_h2kv1d64_w256",
4319
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "sliding_window_cache": 1, "local_window_size": 256 },
4320
+ "inputs": {
4321
+ "queryT": {
4322
+ "dtype": "float32",
4323
+ "shape": [1, 128, 128],
4324
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27, "scale": 0.5 }
4325
+ },
4326
+ "keyT": { "dtype": "float32", "shape": [1, 128, 64], "data": { "kind": "linspace", "start": -1.0, "end": 1.0 } },
4327
+ "valueT": {
4328
+ "dtype": "float32",
4329
+ "shape": [1, 128, 64],
4330
+ "data": { "kind": "linspace", "start": 0.0, "end": 2.0 }
4331
+ },
4332
+ "pastKeyT": {
4333
+ "dtype": "float32",
4334
+ "shape": [1, 1, 384, 64],
4335
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.31, "scale": 0.5 }
4336
+ },
4337
+ "pastValueT": {
4338
+ "dtype": "float32",
4339
+ "shape": [1, 1, 384, 64],
4340
+ "data": { "kind": "linspace", "start": 0.0, "end": 4.0 }
4341
+ },
4342
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
4343
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } }
4344
+ },
4345
+ "outputs": {
4346
+ "outputT": { "dtype": "float32", "shape": [1, 128, 128], "tolerance": 0.005 },
4347
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 384, 64], "tolerance": 0.0001 },
4348
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 384, 64], "tolerance": 0.0001 }
4349
+ },
4350
+ "provenance": {
4351
+ "source": "onnxruntime/contrib_ops/cpu/bert/group_query_attention.cc",
4352
+ "test": "GQA sliding_window_cache chunk prefill on the materialized subgroup-matrix route",
4353
+ "notes": "w+q == C exactly, so every row's window floor is row+1: the second query tile's first key tile is dead (uniform window skip) and the apply pass starts its reduction one tile in. The two-sided band mask is fully exercised."
4354
+ }
4355
+ },
4356
+ {
4357
+ "name": "window_cache_sgmat_chunk_unfilled_b1q128cap256_h2kv1d64_w256",
4358
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "sliding_window_cache": 1, "local_window_size": 256 },
4359
+ "inputs": {
4360
+ "queryT": {
4361
+ "dtype": "float32",
4362
+ "shape": [1, 128, 128],
4363
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27, "scale": 0.5 }
4364
+ },
4365
+ "keyT": { "dtype": "float32", "shape": [1, 128, 64], "data": { "kind": "linspace", "start": -1.0, "end": 1.0 } },
4366
+ "valueT": {
4367
+ "dtype": "float32",
4368
+ "shape": [1, 128, 64],
4369
+ "data": { "kind": "linspace", "start": 0.0, "end": 2.0 }
4370
+ },
4371
+ "pastKeyT": {
4372
+ "dtype": "float32",
4373
+ "shape": [1, 1, 256, 64],
4374
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.31, "scale": 0.5 }
4375
+ },
4376
+ "pastValueT": {
4377
+ "dtype": "float32",
4378
+ "shape": [1, 1, 256, 64],
4379
+ "data": { "kind": "linspace", "start": 0.0, "end": 4.0 }
4380
+ },
4381
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [191] } },
4382
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [192] } }
4383
+ },
4384
+ "outputs": {
4385
+ "outputT": { "dtype": "float32", "shape": [1, 128, 128], "tolerance": 0.005 },
4386
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 256, 64], "tolerance": 0.0001 },
4387
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 256, 64], "tolerance": 0.0001 }
4388
+ },
4389
+ "provenance": {
4390
+ "source": "onnxruntime/contrib_ops/cpu/bert/group_query_attention.cc",
4391
+ "test": "GQA sliding_window_cache chunk prefill on the materialized subgroup-matrix route",
4392
+ "notes": "Partially filled cache: kvActive=192 < C=256, so the live length comes from seqlens_k and the key tiles beyond it publish the stats identity instead of reading cleared rows."
4393
+ }
4394
+ },
4395
+ {
4396
+ "name": "window_cache_sgmat_chunk_mixed_batch_b2q128cap256_h2kv1d64_w192",
4397
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "sliding_window_cache": 1, "local_window_size": 192 },
4398
+ "inputs": {
4399
+ "queryT": {
4400
+ "dtype": "float32",
4401
+ "shape": [2, 128, 128],
4402
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27, "scale": 0.5 }
4403
+ },
4404
+ "keyT": { "dtype": "float32", "shape": [2, 128, 64], "data": { "kind": "linspace", "start": -1.0, "end": 1.0 } },
4405
+ "valueT": {
4406
+ "dtype": "float32",
4407
+ "shape": [2, 128, 64],
4408
+ "data": { "kind": "linspace", "start": 0.0, "end": 2.0 }
4409
+ },
4410
+ "pastKeyT": {
4411
+ "dtype": "float32",
4412
+ "shape": [2, 1, 256, 64],
4413
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.31, "scale": 0.5 }
4414
+ },
4415
+ "pastValueT": {
4416
+ "dtype": "float32",
4417
+ "shape": [2, 1, 256, 64],
4418
+ "data": { "kind": "linspace", "start": 0.0, "end": 4.0 }
4419
+ },
4420
+ "seqlensKT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [319, 191] } },
4421
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [320] } }
4422
+ },
4423
+ "outputs": {
4424
+ "outputT": { "dtype": "float32", "shape": [2, 128, 128], "tolerance": 0.005 },
4425
+ "presentKeyT": { "dtype": "float32", "shape": [2, 1, 256, 64], "tolerance": 0.0001 },
4426
+ "presentValueT": { "dtype": "float32", "shape": [2, 1, 256, 64], "tolerance": 0.0001 }
4427
+ },
4428
+ "provenance": {
4429
+ "source": "onnxruntime/contrib_ops/cpu/bert/group_query_attention.cc",
4430
+ "test": "GQA sliding_window_cache chunk prefill on the materialized subgroup-matrix route",
4431
+ "notes": "Per-batch live lengths: batch 0 evicts (kvActive=256) with an active window floor, batch 1 is unfilled (kvActive=192) with the floor inactive — both from the same dispatch."
4432
+ }
4433
+ },
4434
+ {
4435
+ "name": "window_cache_sgmat_chunk_qtail_b1q160cap320_h2kv1d64_w160",
4436
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "sliding_window_cache": 1, "local_window_size": 160 },
4437
+ "inputs": {
4438
+ "queryT": {
4439
+ "dtype": "float32",
4440
+ "shape": [1, 160, 128],
4441
+ "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27, "scale": 0.5 }
4442
+ },
4443
+ "keyT": { "dtype": "float32", "shape": [1, 160, 64], "data": { "kind": "linspace", "start": -1.0, "end": 1.0 } },
4444
+ "valueT": {
4445
+ "dtype": "float32",
4446
+ "shape": [1, 160, 64],
4447
+ "data": { "kind": "linspace", "start": 0.0, "end": 2.0 }
4448
+ },
4449
+ "pastKeyT": {
4450
+ "dtype": "float32",
4451
+ "shape": [1, 1, 320, 64],
4452
+ "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.31, "scale": 0.5 }
4453
+ },
4454
+ "pastValueT": {
4455
+ "dtype": "float32",
4456
+ "shape": [1, 1, 320, 64],
4457
+ "data": { "kind": "linspace", "start": 0.0, "end": 4.0 }
4458
+ },
4459
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [399] } },
4460
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [400] } }
4461
+ },
4462
+ "outputs": {
4463
+ "outputT": { "dtype": "float32", "shape": [1, 160, 128], "tolerance": 0.005 },
4464
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 320, 64], "tolerance": 0.0001 },
4465
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 320, 64], "tolerance": 0.0001 }
4466
+ },
4467
+ "provenance": {
4468
+ "source": "onnxruntime/contrib_ops/cpu/bert/group_query_attention.cc",
4469
+ "test": "GQA sliding_window_cache chunk prefill on the materialized subgroup-matrix route",
4470
+ "notes": "q=160 is not a query-tile multiple and w+q == C makes every floor active, so the guarded tail rows and the tile-aligned reduction start are exercised together."
4471
+ }
4472
+ },
4473
  {
4474
  "name": "share_append_flash_chunk_slack_b1q32cap64_h2kv1d64",
4475
  "attrs": { "num_heads": 2, "kv_num_heads": 1, "local_window_size": 16 },
 
4854
  "presentKeyT": { "dtype": "uint8", "shape": [1, 1, 4, 4], "tolerance": 0 },
4855
  "presentValueT": { "dtype": "uint8", "shape": [1, 1, 4, 4], "tolerance": 0 }
4856
  }
4857
+ },
4858
+ {
4859
+ "name": "qkv_materialized_sgmat_f32_q512_kv512_h2kv1_d64",
4860
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "causal": 0 },
4861
+ "inputs": {
4862
+ "queryT": {
4863
+ "dtype": "float32",
4864
+ "shape": [1, 512, 128],
4865
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
4866
+ },
4867
+ "keyT": {
4868
+ "dtype": "float32",
4869
+ "shape": [1, 512, 64],
4870
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
4871
+ },
4872
+ "valueT": {
4873
+ "dtype": "float32",
4874
+ "shape": [1, 512, 64],
4875
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
4876
+ },
4877
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
4878
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } }
4879
+ },
4880
+ "outputs": {
4881
+ "outputT": { "dtype": "float32", "shape": [1, 512, 128], "tolerance": 0.00002 },
4882
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 },
4883
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 }
4884
+ }
4885
+ },
4886
+ {
4887
+ "name": "qkv_materialized_sgmat_f32_padded_seqlens_q512_kv512_h2kv1_d64",
4888
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "causal": 0 },
4889
+ "inputs": {
4890
+ "queryT": {
4891
+ "dtype": "float32",
4892
+ "shape": [1, 512, 128],
4893
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
4894
+ },
4895
+ "keyT": {
4896
+ "dtype": "float32",
4897
+ "shape": [1, 512, 64],
4898
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
4899
+ },
4900
+ "valueT": {
4901
+ "dtype": "float32",
4902
+ "shape": [1, 512, 64],
4903
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
4904
+ },
4905
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [299] } },
4906
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } }
4907
+ },
4908
+ "outputs": {
4909
+ "outputT": { "dtype": "float32", "shape": [1, 512, 128], "tolerance": 0.00002 },
4910
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 },
4911
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 }
4912
+ }
4913
+ },
4914
+ {
4915
+ "name": "qkv_materialized_sgmat_f32_rightalign_q512_kv576_h2kv1_d128",
4916
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "causal": 0 },
4917
+ "inputs": {
4918
+ "queryT": {
4919
+ "dtype": "float32",
4920
+ "shape": [1, 512, 256],
4921
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
4922
+ },
4923
+ "keyT": {
4924
+ "dtype": "float32",
4925
+ "shape": [1, 576, 128],
4926
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
4927
+ },
4928
+ "valueT": {
4929
+ "dtype": "float32",
4930
+ "shape": [1, 576, 128],
4931
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
4932
+ },
4933
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [575] } },
4934
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [576] } }
4935
+ },
4936
+ "outputs": {
4937
+ "outputT": { "dtype": "float32", "shape": [1, 512, 256], "tolerance": 0.00002 },
4938
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 576, 128], "tolerance": 0.000001 },
4939
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 576, 128], "tolerance": 0.000001 }
4940
+ }
4941
+ },
4942
+ {
4943
+ "name": "past_kv_materialized_sgmat_f32_q512_p512_h2kv1_d64",
4944
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
4945
+ "inputs": {
4946
+ "queryT": {
4947
+ "dtype": "float32",
4948
+ "shape": [1, 512, 128],
4949
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
4950
+ },
4951
+ "keyT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
4952
+ "valueT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
4953
+ "pastKeyT": {
4954
+ "dtype": "float32",
4955
+ "shape": [1, 1, 512, 64],
4956
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
4957
+ },
4958
+ "pastValueT": {
4959
+ "dtype": "float32",
4960
+ "shape": [1, 1, 512, 64],
4961
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
4962
+ },
4963
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
4964
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } }
4965
+ },
4966
+ "outputs": {
4967
+ "outputT": { "dtype": "float32", "shape": [1, 512, 128], "tolerance": 0.00002 },
4968
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 },
4969
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 }
4970
+ }
4971
+ },
4972
+ {
4973
+ "name": "past_kv_materialized_sgmat_f32_qtail_q520_p576_h2kv1_d64",
4974
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
4975
+ "inputs": {
4976
+ "queryT": {
4977
+ "dtype": "float32",
4978
+ "shape": [1, 520, 128],
4979
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
4980
+ },
4981
+ "keyT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
4982
+ "valueT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
4983
+ "pastKeyT": {
4984
+ "dtype": "float32",
4985
+ "shape": [1, 1, 576, 64],
4986
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
4987
+ },
4988
+ "pastValueT": {
4989
+ "dtype": "float32",
4990
+ "shape": [1, 1, 576, 64],
4991
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
4992
+ },
4993
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [575] } },
4994
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [576] } }
4995
+ },
4996
+ "outputs": {
4997
+ "outputT": { "dtype": "float32", "shape": [1, 520, 128], "tolerance": 0.00002 },
4998
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 576, 64], "tolerance": 0.000001 },
4999
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 576, 64], "tolerance": 0.000001 }
5000
+ }
5001
+ },
5002
+ {
5003
+ "name": "past_kv_materialized_sgmat_f32_group_q512_p512_h4kv2_d128",
5004
+ "attrs": { "num_heads": 4, "kv_num_heads": 2 },
5005
+ "inputs": {
5006
+ "queryT": {
5007
+ "dtype": "float32",
5008
+ "shape": [1, 512, 512],
5009
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5010
+ },
5011
+ "keyT": { "dtype": "float32", "shape": [1, 0, 256], "data": { "kind": "values", "values": [] } },
5012
+ "valueT": { "dtype": "float32", "shape": [1, 0, 256], "data": { "kind": "values", "values": [] } },
5013
+ "pastKeyT": {
5014
+ "dtype": "float32",
5015
+ "shape": [1, 2, 512, 128],
5016
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5017
+ },
5018
+ "pastValueT": {
5019
+ "dtype": "float32",
5020
+ "shape": [1, 2, 512, 128],
5021
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5022
+ },
5023
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
5024
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } }
5025
+ },
5026
+ "outputs": {
5027
+ "outputT": { "dtype": "float32", "shape": [1, 512, 512], "tolerance": 0.00002 },
5028
+ "presentKeyT": { "dtype": "float32", "shape": [1, 2, 512, 128], "tolerance": 0.000001 },
5029
+ "presentValueT": { "dtype": "float32", "shape": [1, 2, 512, 128], "tolerance": 0.000001 }
5030
+ }
5031
+ },
5032
+ {
5033
+ "name": "past_kv_materialized_sgmat_f32_floor_q256_p256_h2kv1_d64",
5034
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
5035
+ "inputs": {
5036
+ "queryT": {
5037
+ "dtype": "float32",
5038
+ "shape": [1, 256, 128],
5039
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5040
+ },
5041
+ "keyT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5042
+ "valueT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5043
+ "pastKeyT": {
5044
+ "dtype": "float32",
5045
+ "shape": [1, 1, 256, 64],
5046
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5047
+ },
5048
+ "pastValueT": {
5049
+ "dtype": "float32",
5050
+ "shape": [1, 1, 256, 64],
5051
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5052
+ },
5053
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [255] } },
5054
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [256] } }
5055
+ },
5056
+ "outputs": {
5057
+ "outputT": { "dtype": "float32", "shape": [1, 256, 128], "tolerance": 0.00002 },
5058
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 256, 64], "tolerance": 0.000001 },
5059
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 256, 64], "tolerance": 0.000001 }
5060
+ }
5061
+ },
5062
+ {
5063
+ "name": "past_kv_rotary_materialized_sgmat_f32_q512_p512_h2kv1_d64",
5064
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "do_rotary": 1 },
5065
+ "inputs": {
5066
+ "queryT": {
5067
+ "dtype": "float32",
5068
+ "shape": [1, 512, 128],
5069
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5070
+ },
5071
+ "keyT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5072
+ "valueT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5073
+ "pastKeyT": {
5074
+ "dtype": "float32",
5075
+ "shape": [1, 1, 512, 64],
5076
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5077
+ },
5078
+ "pastValueT": {
5079
+ "dtype": "float32",
5080
+ "shape": [1, 1, 512, 64],
5081
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5082
+ },
5083
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
5084
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } },
5085
+ "cosCacheT": {
5086
+ "dtype": "float32",
5087
+ "shape": [512, 32],
5088
+ "data": { "kind": "rotaryCos", "thetaStart": 0.07, "thetaStep": 0.013 }
5089
+ },
5090
+ "sinCacheT": {
5091
+ "dtype": "float32",
5092
+ "shape": [512, 32],
5093
+ "data": { "kind": "rotarySin", "thetaStart": 0.07, "thetaStep": 0.013 }
5094
+ }
5095
+ },
5096
+ "outputs": {
5097
+ "outputT": { "dtype": "float32", "shape": [1, 512, 128], "tolerance": 0.00002 },
5098
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 },
5099
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 }
5100
+ }
5101
+ },
5102
+ {
5103
+ "name": "past_kv_rotary_materialized_sgmat_f32_offset_q512_p576_h2kv1_d64",
5104
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "do_rotary": 1 },
5105
+ "inputs": {
5106
+ "queryT": {
5107
+ "dtype": "float32",
5108
+ "shape": [1, 512, 128],
5109
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5110
+ },
5111
+ "keyT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5112
+ "valueT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5113
+ "pastKeyT": {
5114
+ "dtype": "float32",
5115
+ "shape": [1, 1, 576, 64],
5116
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5117
+ },
5118
+ "pastValueT": {
5119
+ "dtype": "float32",
5120
+ "shape": [1, 1, 576, 64],
5121
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5122
+ },
5123
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [575] } },
5124
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [576] } },
5125
+ "cosCacheT": {
5126
+ "dtype": "float32",
5127
+ "shape": [576, 32],
5128
+ "data": { "kind": "rotaryCos", "thetaStart": 0.07, "thetaStep": 0.013 }
5129
+ },
5130
+ "sinCacheT": {
5131
+ "dtype": "float32",
5132
+ "shape": [576, 32],
5133
+ "data": { "kind": "rotarySin", "thetaStart": 0.07, "thetaStep": 0.013 }
5134
+ }
5135
+ },
5136
+ "outputs": {
5137
+ "outputT": { "dtype": "float32", "shape": [1, 512, 128], "tolerance": 0.00002 },
5138
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 576, 64], "tolerance": 0.000001 },
5139
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 576, 64], "tolerance": 0.000001 }
5140
+ }
5141
+ },
5142
+ {
5143
+ "name": "new_kv_past_materialized_sgmat_f32_chunk_q512_p512_h2kv1_d64",
5144
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
5145
+ "inputs": {
5146
+ "queryT": {
5147
+ "dtype": "float32",
5148
+ "shape": [1, 512, 128],
5149
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5150
+ },
5151
+ "keyT": {
5152
+ "dtype": "float32",
5153
+ "shape": [1, 512, 64],
5154
+ "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.037 }
5155
+ },
5156
+ "valueT": {
5157
+ "dtype": "float32",
5158
+ "shape": [1, 512, 64],
5159
+ "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.013 }
5160
+ },
5161
+ "pastKeyT": {
5162
+ "dtype": "float32",
5163
+ "shape": [1, 1, 512, 64],
5164
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5165
+ },
5166
+ "pastValueT": {
5167
+ "dtype": "float32",
5168
+ "shape": [1, 1, 512, 64],
5169
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5170
+ },
5171
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [1023] } },
5172
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [1024] } }
5173
+ },
5174
+ "outputs": {
5175
+ "outputT": { "dtype": "float32", "shape": [1, 512, 128], "tolerance": 0.00002 },
5176
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 1024, 64], "tolerance": 0.000001 },
5177
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 1024, 64], "tolerance": 0.000001 }
5178
+ }
5179
+ },
5180
+ {
5181
+ "name": "qkv_materialized_sgmat_f32_floor_q256_kv256_h2kv1_d64",
5182
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "causal": 0 },
5183
+ "inputs": {
5184
+ "queryT": {
5185
+ "dtype": "float32",
5186
+ "shape": [1, 256, 128],
5187
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5188
+ },
5189
+ "keyT": {
5190
+ "dtype": "float32",
5191
+ "shape": [1, 256, 64],
5192
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5193
+ },
5194
+ "valueT": {
5195
+ "dtype": "float32",
5196
+ "shape": [1, 256, 64],
5197
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5198
+ },
5199
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [255] } },
5200
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [256] } }
5201
+ },
5202
+ "outputs": {
5203
+ "outputT": { "dtype": "float32", "shape": [1, 256, 128], "tolerance": 0.00002 },
5204
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 256, 64], "tolerance": 0.000001 },
5205
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 256, 64], "tolerance": 0.000001 }
5206
+ }
5207
+ },
5208
+ {
5209
+ "name": "past_kv_materialized_sgmat_f16_q512_p512_h2kv1_d64",
5210
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
5211
+ "inputs": {
5212
+ "queryT": {
5213
+ "dtype": "float16",
5214
+ "shape": [1, 512, 128],
5215
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5216
+ },
5217
+ "keyT": { "dtype": "float16", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5218
+ "valueT": { "dtype": "float16", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5219
+ "pastKeyT": {
5220
+ "dtype": "float16",
5221
+ "shape": [1, 1, 512, 64],
5222
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5223
+ },
5224
+ "pastValueT": {
5225
+ "dtype": "float16",
5226
+ "shape": [1, 1, 512, 64],
5227
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5228
+ },
5229
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
5230
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } }
5231
+ },
5232
+ "outputs": {
5233
+ "outputT": { "dtype": "float16", "shape": [1, 512, 128], "tolerance": 0.03 },
5234
+ "presentKeyT": { "dtype": "float16", "shape": [1, 1, 512, 64], "tolerance": 0.001 },
5235
+ "presentValueT": { "dtype": "float16", "shape": [1, 1, 512, 64], "tolerance": 0.001 }
5236
+ }
5237
+ },
5238
+ {
5239
+ "name": "past_kv_materialized_sgmat_f16_floor_q256_p256_h2kv1_d64",
5240
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
5241
+ "inputs": {
5242
+ "queryT": {
5243
+ "dtype": "float16",
5244
+ "shape": [1, 256, 128],
5245
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5246
+ },
5247
+ "keyT": { "dtype": "float16", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5248
+ "valueT": { "dtype": "float16", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5249
+ "pastKeyT": {
5250
+ "dtype": "float16",
5251
+ "shape": [1, 1, 256, 64],
5252
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5253
+ },
5254
+ "pastValueT": {
5255
+ "dtype": "float16",
5256
+ "shape": [1, 1, 256, 64],
5257
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5258
+ },
5259
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [255] } },
5260
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [256] } }
5261
+ },
5262
+ "outputs": {
5263
+ "outputT": { "dtype": "float16", "shape": [1, 256, 128], "tolerance": 0.03 },
5264
+ "presentKeyT": { "dtype": "float16", "shape": [1, 1, 256, 64], "tolerance": 0.001 },
5265
+ "presentValueT": { "dtype": "float16", "shape": [1, 1, 256, 64], "tolerance": 0.001 }
5266
+ }
5267
+ },
5268
+ {
5269
+ "name": "past_kv_rotary_materialized_sgmat_f16_q512_p512_h2kv1_d64",
5270
+ "attrs": { "num_heads": 2, "kv_num_heads": 1, "do_rotary": 1 },
5271
+ "inputs": {
5272
+ "queryT": {
5273
+ "dtype": "float16",
5274
+ "shape": [1, 512, 128],
5275
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5276
+ },
5277
+ "keyT": { "dtype": "float16", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5278
+ "valueT": { "dtype": "float16", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5279
+ "pastKeyT": {
5280
+ "dtype": "float16",
5281
+ "shape": [1, 1, 512, 64],
5282
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5283
+ },
5284
+ "pastValueT": {
5285
+ "dtype": "float16",
5286
+ "shape": [1, 1, 512, 64],
5287
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5288
+ },
5289
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
5290
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } },
5291
+ "cosCacheT": {
5292
+ "dtype": "float16",
5293
+ "shape": [512, 32],
5294
+ "data": { "kind": "rotaryCos", "thetaStart": 0.07, "thetaStep": 0.013 }
5295
+ },
5296
+ "sinCacheT": {
5297
+ "dtype": "float16",
5298
+ "shape": [512, 32],
5299
+ "data": { "kind": "rotarySin", "thetaStart": 0.07, "thetaStep": 0.013 }
5300
+ }
5301
+ },
5302
+ "outputs": {
5303
+ "outputT": { "dtype": "float16", "shape": [1, 512, 128], "tolerance": 0.03 },
5304
+ "presentKeyT": { "dtype": "float16", "shape": [1, 1, 512, 64], "tolerance": 0.001 },
5305
+ "presentValueT": { "dtype": "float16", "shape": [1, 1, 512, 64], "tolerance": 0.001 }
5306
+ }
5307
+ },
5308
+ {
5309
+ "name": "past_kv_bias_materialized_sgmat_f32_q512_p512_h2kv1_d64",
5310
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
5311
+ "inputs": {
5312
+ "queryT": {
5313
+ "dtype": "float32",
5314
+ "shape": [1, 512, 128],
5315
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5316
+ },
5317
+ "keyT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5318
+ "valueT": { "dtype": "float32", "shape": [1, 0, 64], "data": { "kind": "values", "values": [] } },
5319
+ "pastKeyT": {
5320
+ "dtype": "float32",
5321
+ "shape": [1, 1, 512, 64],
5322
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5323
+ },
5324
+ "pastValueT": {
5325
+ "dtype": "float32",
5326
+ "shape": [1, 1, 512, 64],
5327
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5328
+ },
5329
+ "seqlensKT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [511] } },
5330
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [512] } },
5331
+ "attentionBiasT": {
5332
+ "dtype": "float32",
5333
+ "shape": [1, 2, 512, 512],
5334
+ "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.13, "scale": 0.5 }
5335
+ }
5336
+ },
5337
+ "outputs": {
5338
+ "outputT": { "dtype": "float32", "shape": [1, 512, 128], "tolerance": 0.00002 },
5339
+ "presentKeyT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 },
5340
+ "presentValueT": { "dtype": "float32", "shape": [1, 1, 512, 64], "tolerance": 0.000001 }
5341
+ }
5342
+ },
5343
+ {
5344
+ "name": "past_kv_bias_materialized_sgmat_f32_broadcast_b2_q256_p256_h2kv1_d64",
5345
+ "attrs": { "num_heads": 2, "kv_num_heads": 1 },
5346
+ "inputs": {
5347
+ "queryT": {
5348
+ "dtype": "float32",
5349
+ "shape": [2, 256, 128],
5350
+ "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 }
5351
+ },
5352
+ "keyT": { "dtype": "float32", "shape": [2, 0, 64], "data": { "kind": "values", "values": [] } },
5353
+ "valueT": { "dtype": "float32", "shape": [2, 0, 64], "data": { "kind": "values", "values": [] } },
5354
+ "pastKeyT": {
5355
+ "dtype": "float32",
5356
+ "shape": [2, 1, 256, 64],
5357
+ "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 }
5358
+ },
5359
+ "pastValueT": {
5360
+ "dtype": "float32",
5361
+ "shape": [2, 1, 256, 64],
5362
+ "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 }
5363
+ },
5364
+ "seqlensKT": { "dtype": "int32", "shape": [2], "data": { "kind": "values", "values": [255, 255] } },
5365
+ "totalSequenceLengthT": { "dtype": "int32", "shape": [1], "data": { "kind": "values", "values": [256] } },
5366
+ "attentionBiasT": {
5367
+ "dtype": "float32",
5368
+ "shape": [2, 1, 256, 256],
5369
+ "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.17, "scale": 0.5 }
5370
+ }
5371
+ },
5372
+ "outputs": {
5373
+ "outputT": { "dtype": "float32", "shape": [2, 256, 128], "tolerance": 0.00002 },
5374
+ "presentKeyT": { "dtype": "float32", "shape": [2, 1, 256, 64], "tolerance": 0.000001 },
5375
+ "presentValueT": { "dtype": "float32", "shape": [2, 1, 256, 64], "tolerance": 0.000001 }
5376
+ }
5377
  }
5378
  ]
5379
  }