Quazim0t0 commited on
Commit
30eb3fa
·
verified ·
1 Parent(s): 9ae1216

web: QKV dual-GEMM fusion (bit-identical, CUTLASS ex. 45)

Browse files
Files changed (1) hide show
  1. web/public/transformer.js +32 -3
web/public/transformer.js CHANGED
@@ -116,6 +116,35 @@
116
  // ctx.audit re-checks random cells of this LIVE GEMM against the units
117
  return V.vgemmBlock(Xf, Wf, { m, k, n, batch: 1, relu: !!relu }, ctx.L, ctx.bgemm, ctx.audit);
118
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  // ---- layernorm (no affine) -------------------------------------------------
121
  function lnFwd(x, rows, C) {
@@ -227,9 +256,9 @@
227
  for (let l = 0; l < layers; l++) {
228
  const bl = m.blocks[l], cb = { xin: x };
229
  const l1 = lnFwd(x, BT, C); cb.ln1 = l1;
230
- const q = await vmm(l1.y, bl.Wq, BT, C, C, ctx);
231
- const k = await vmm(l1.y, bl.Wk, BT, C, C, ctx);
232
- const v = await vmm(l1.y, bl.Wv, BT, C, C, ctx);
233
  cb.q = q; cb.k = k; cb.v = v;
234
  const scale = 1 / Math.sqrt(hd);
235
  // gather-FUSED attention (CUTLASS ex. 36/52): the kernels read q/k/v in
 
116
  // ctx.audit re-checks random cells of this LIVE GEMM against the units
117
  return V.vgemmBlock(Xf, Wf, { m, k, n, batch: 1, relu: !!relu }, ctx.L, ctx.bgemm, ctx.audit);
118
  }
119
+ // CUTLASS ex. 45 (dual GEMM): sibling GEMMs that share the same LEFT operand
120
+ // run as ONE batched dispatch, and the shared operand is quantized ONCE
121
+ // instead of once per sibling. Used for the q/k/v projections — same X
122
+ // (ln1.y), three weights, identical shapes. Bit-identical to three separate
123
+ // vmm calls: quantizeRows is deterministic (same input -> same int8+scales),
124
+ // the tiled copies index exactly like separate batch elements, and block
125
+ // scales are per-row/per-column PER BATCH ELEMENT, so concatenation changes
126
+ // no scale and no product. The batched kernel is the same exact-gated bgemm
127
+ // that training already runs, and the live-shape audit still samples it.
128
+ async function vmmShared3(Xf, Wa, Wb, Wc, m, k, n, ctx) {
129
+ const x = V.quantizeRows(Xf, m, k);
130
+ const xq = new Int8Array(3 * m * k), xs = new Float32Array(3 * m);
131
+ for (let i = 0; i < 3; i++) { xq.set(x.q, i * m * k); xs.set(x.s, i * m); }
132
+ const wq = new Int8Array(3 * k * n), ws = new Float32Array(3 * n);
133
+ [Wa, Wb, Wc].forEach((W, i) => { const w = V.quantizeCols(W, k, n); wq.set(w.q, i * k * n); ws.set(w.s, i * n); });
134
+ const d = { m, k, n, batch: 3 };
135
+ let out;
136
+ if (ctx.bgemm) {
137
+ out = await ctx.bgemm(xq, wq, xs, ws, d);
138
+ if (ctx.audit && ctx.audit.due()) {
139
+ const bad = V.auditTile(xq, wq, xs, ws, d, out, ctx.L, ctx.audit.cells);
140
+ if (bad) ctx.audit.fail(bad);
141
+ }
142
+ } else {
143
+ out = V.bgemmJS(xq, wq, xs, ws, d, ctx.L);
144
+ }
145
+ const MN = m * n;
146
+ return [out.subarray(0, MN), out.subarray(MN, 2 * MN), out.subarray(2 * MN, 3 * MN)];
147
+ }
148
 
149
  // ---- layernorm (no affine) -------------------------------------------------
150
  function lnFwd(x, rows, C) {
 
256
  for (let l = 0; l < layers; l++) {
257
  const bl = m.blocks[l], cb = { xin: x };
258
  const l1 = lnFwd(x, BT, C); cb.ln1 = l1;
259
+ // q/k/v share the same left operand one batched dispatch, one quantize
260
+ // of ln1.y instead of three (CUTLASS ex. 45; see vmmShared3)
261
+ const [q, k, v] = await vmmShared3(l1.y, bl.Wq, bl.Wk, bl.Wv, BT, C, C, ctx);
262
  cb.q = q; cb.k = k; cb.v = v;
263
  const scale = 1 / Math.sqrt(hd);
264
  // gather-FUSED attention (CUTLASS ex. 36/52): the kernels read q/k/v in