Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>microbench4 v2 — MRT vs multi-dispatch, verified (minichat/slim style)</title>
- <style>
- body{background:#0e0f13;color:#d8e0e8;font:13px ui-monospace,Consolas,monospace;padding:16px;margin:0}
- h2{color:#7dd3fc;margin:0 0 8px}
- h3{color:#93c5fd;margin:16px 0 6px;font-size:13px}
- #status{color:#fbbf24;margin:8px 0;white-space:pre-wrap;font-size:12px}
- #bootlog{background:#0a0c10;border:1px solid #1e293b;border-radius:4px;padding:8px 10px;margin:8px 0;
- font-size:11px;line-height:1.5;white-space:pre-wrap;color:#94a3b8;max-height:260px;overflow-y:auto}
- #bootlog .ok{color:#4ade80}
- #bootlog .bad{color:#f87171}
- #bootlog .warn{color:#fbbf24}
- #bootlog .dim{color:#475569}
- .pill{padding:2px 8px;border-radius:10px;font-size:11px;background:#334155;margin-right:6px;display:inline-block}
- .pill.ok{background:#052e16;color:#4ade80}
- .pill.warn{background:#3b2b0a;color:#fbbf24}
- .pill.bad{background:#3b0a0a;color:#f87171}
- .checks{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin:10px 0}
- .checks label{background:#1e293b;padding:6px 10px;border-radius:4px;cursor:pointer;display:flex;gap:8px;align-items:center;font-size:12px}
- .checks label:hover{background:#334155}
- button{background:#1e293b;color:#7dd3fc;border:1px solid #334155;padding:6px 12px;border-radius:4px;cursor:pointer;font:inherit;font-size:12px}
- button:hover:not(:disabled){background:#334155}
- button:disabled{color:#475569;cursor:not-allowed}
- button.primary{background:#0c1a24;border-color:#7dd3fc;color:#7dd3fc}
- table{border-collapse:collapse;width:100%;margin-top:6px;font-size:11px}
- th,td{padding:5px 8px;text-align:left;border-bottom:1px solid #1e293b}
- th{color:#94a3b8;font-weight:600;font-size:11px}
- .num{text-align:right;font-variant-numeric:tabular-nums}
- tr.pass td.verdict{color:#4ade80;font-weight:600}
- tr.warn td.verdict{color:#fbbf24;font-weight:600}
- tr.fail td.verdict{color:#f87171;font-weight:600}
- tr.pending td{color:#475569}
- .alert{padding:10px;border-radius:4px;margin:10px 0;font-size:12px}
- .alert.bad{background:#3b0a0a;color:#f87171;border:1px solid #7f1d1d}
- .alert.warn{background:#3b2b0a;color:#fbbf24;border:1px solid #78350f}
- .alert.ok{background:#052e16;color:#4ade80;border:1px solid #14532d}
- canvas{display:none}
- </style>
- </head>
- <body>
- <h2>microbench4 v2 — MRT vs multi-dispatch for minichat QKV (verified)</h2>
- <div>
- <span class="pill" id="pillGpu">GPU: ?</span>
- <span class="pill" id="pillIsolation">Isolation: ?</span>
- <span class="pill" id="pillTimer">Timer: ?</span>
- <span class="pill" id="pillMRT">MRT: ?</span>
- <span class="pill" id="pillMode">mode: ?</span>
- </div>
- <div id="status">Booting…</div>
- <div id="warn"></div>
- <div id="bootlog" class="dim">boot log:</div>
- <h3>Test selection</h3>
- <div class="checks">
- <label><input type="checkbox" id="chkF" checked>F · Dispatch floor (noop ×1..32)</label>
- <label><input type="checkbox" id="chkS" checked>S · MRT vs 3-separate (trivial write)</label>
- <label><input type="checkbox" id="chkQ" checked>Q · Current 3-dispatch QKV path (B=1)</label>
- <label><input type="checkbox" id="chkQM" checked>QM · MRT 2-dispatch QKV path (B=1)</label>
- <label><input type="checkbox" id="chkP" checked>P · Prefill B=8 (current vs MRT)</label>
- <label><input type="checkbox" id="chkP100" checked>P100 · Prefill B=100 + spot rows</label>
- <label><input type="checkbox" id="chkV" checked>V · Correctness ladder V1/V2/V3 (f64 mirror)</label>
- </div>
- <div>
- <button id="btnSweep" class="primary">Run selected</button>
- <button id="btnClear">Clear</button>
- <span style="color:#64748b;font-size:11px">?fast in URL = short batches (slow GPUs / SwiftShader).</span>
- </div>
- <h3>Results</h3>
- <table>
- <thead><tr>
- <th>test</th><th>config</th>
- <th class="num">per-rep</th>
- <th class="num">reps</th>
- <th class="num">total ms</th>
- <th class="num">conf</th>
- <th class="num">draws</th>
- </tr></thead>
- <tbody id="resultsBody"></tbody>
- </table>
- <h3>Issue → verdict map</h3>
- <table>
- <thead><tr>
- <th>issue</th><th>test</th>
- <th class="num">metric</th>
- <th>verdict</th>
- <th>action</th>
- </tr></thead>
- <tbody id="issueMapBody"></tbody>
- </table>
- <h3>Interpretation</h3>
- <div id="interpretation">—</div>
- <canvas id="c" width="16" height="16"></canvas>
- <script>
- 'use strict';
- // ============================================================ Config
- const D = 768, KVD = 768; // model dim, kv dim (7B-like: d == kvd, no GQA)
- const OUTQ = D + 2*KVD; // 2304 fused QKV floats
- const P = 4; // split-K partials
- const PXW_OUTQ = OUTQ/4; // 576
- const PXW_Q = D/4; // 192
- const PXW_KV = KVD/4; // 192
- const B_MAX = 100; // prefill batch ceiling (P100 test)
- const POOLH = 128; // pool rows (decode-shaped MAXLEN; B=100 fits)
- const PAGE = 4096; // texture page width (flat index -> (t&4095, t>>12))
- const FAST = /[?&]fast\b/.test(location.search);
- const MEAS = FAST
- ? { warm: 3, estLoops: 1, estReps: 3, target: 1.2, samples: 2, minReps: 5, maxReps: 2000 }
- : { warm: 100, estLoops: 3, estReps: 20, target: 3, samples: 5, minReps: 30, maxReps: 20000 };
- const TOL = { passAbs: 1e-4, warnAbs: 1e-3, distMin: 0.05 }; // f64-mirror gates (f32 budget ~1e-6)
- // numpy ground truth (scripts/mrt_ground_truth.py, f32 pipeline, row 0):
- const GT_ROW0 = { q: -0.181328, k: 0.546511, v: 0.370535 };
- // ============================================================ GL init
- const canvas = document.getElementById('c');
- const gl = canvas.getContext('webgl2', {antialias:false});
- if (!gl) document.getElementById('status').textContent = 'FATAL: no WebGL2';
- else if (!gl.getExtension('EXT_color_buffer_float'))
- document.getElementById('status').textContent = 'FATAL: no EXT_color_buffer_float';
- let TIMER_RES_US = 100;
- (function detectTimer(){
- if (!gl) return;
- const gaps=[]; let prev=performance.now();
- for (let i=0;i<1000;i++){ const t=performance.now(); if(t>prev){gaps.push(t-prev);prev=t;} }
- if (gaps.length>10){ gaps.sort((a,b)=>a-b); TIMER_RES_US=gaps[Math.floor(gaps.length/4)]*1000; }
- const pT=document.getElementById('pillTimer');
- pT.textContent=`Timer: ${TIMER_RES_US.toFixed(0)}µs`;
- pT.className='pill '+(TIMER_RES_US<10?'ok':TIMER_RES_US<50?'warn':'bad');
- const pI=document.getElementById('pillIsolation');
- pI.textContent=`Isolation: ${crossOriginIsolated?'yes':'no'}`;
- pI.className='pill '+(crossOriginIsolated?'ok':'warn');
- if (TIMER_RES_US>20){
- document.getElementById('warn').innerHTML=
- '<div class="alert warn"><b>Timer precision is low ('+TIMER_RES_US.toFixed(0)+'µs).</b> '+
- 'COOP/COEP may not have taken effect — reload the page after the server starts. '+
- 'Bench uses long batches to compensate.</div>';
- }
- })();
- if (gl){
- const dbg = gl.getExtension('WEBGL_debug_renderer_info');
- const gpuName = dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : '?';
- const pg=document.getElementById('pillGpu');
- pg.textContent = 'GPU: ' + gpuName.split('(')[0].trim();
- pg.className='pill ok';
- const mdb = gl.getParameter(gl.MAX_DRAW_BUFFERS);
- const pm=document.getElementById('pillMRT');
- pm.textContent = `MRT: max ${mdb} buffers`;
- pm.className='pill '+(mdb>=3?'ok':'bad');
- const pmo=document.getElementById('pillMode');
- pmo.textContent = FAST ? 'mode: fast' : 'mode: full';
- pmo.className='pill '+(FAST?'warn':'ok');
- }
- // ============================================================ Boot log (UPG style)
- const bootLines = [];
- function bootLog(cls, tag, msg){
- const line = `[${tag}] ${msg}`;
- bootLines.push({cls, line});
- const el = document.getElementById('bootlog');
- el.innerHTML += `\n<span class="${cls}">${line}</span>`;
- console.log((cls==='bad'?'FAIL ':cls==='ok'?'':'') + line);
- }
- // ============================================================ Shader
- function buildShader(mrt){
- const pre = mrt
- ? `layout(location=0) out vec4 out0;
- layout(location=1) out vec4 out1;
- layout(location=2) out vec4 out2;`
- : `out vec4 fragColor;`;
- const epi = mrt
- ? `out0=vec4(y[0],y[1],y[2],y[3]);
- out1=vec4(y1[0],y1[1],y1[2],y1[3]);
- out2=vec4(y2[0],y2[1],y2[2],y2[3]);`
- : `fragColor=vec4(y[0],y[1],y[2],y[3]);`;
- return `#version 300 es
- precision highp float; precision highp int;
- uniform sampler2D u_in, u_w;
- uniform int u_mode, u_in_len, u_out_len, u_row_offset;
- uniform float u_f0, u_f1;
- uniform int u_i0, u_i1, u_i2, u_i3, u_i4, u_i5, u_i6, u_i7;
- ${pre}
- ivec2 widx(int i){ int t=i>>2; return ivec2(t&4095, t>>12); }
- float getIn(int i){ return texelFetch(u_in, widx(i), 0)[i&3]; }
- float getW(int i){ return texelFetch(u_w, widx(i), 0)[i&3]; }
- void main(){
- int px = int(gl_FragCoord.x);
- int py = int(gl_FragCoord.y);
- float y[4] = float[4](0.,0.,0.,0.);
- float y1[4] = float[4](0.,0.,0.,0.);
- float y2[4] = float[4](0.,0.,0.,0.);
- if (u_mode == 57) {
- // split-K partial GEMV; row py reads input row py (u_row_offset = row stride in floats)
- int R = u_i0;
- int PXW = u_i1;
- int K1 = u_i2;
- int sp = px / PXW;
- int col = px - sp*PXW;
- if (sp < R) {
- int c0 = sp * K1;
- float a0=0.,a1=0.,a2=0.,a3=0.;
- for (int i=0;i<256;i++){
- int c = c0 + i;
- if (i >= K1 || c >= u_in_len) break;
- vec4 w4 = texelFetch(u_w, widx(c*u_out_len + col*4), 0);
- float x = getIn(u_row_offset*py + c);
- a0 += w4.x*x; a1 += w4.y*x; a2 += w4.z*x; a3 += w4.w*x;
- }
- y[0]=a0; y[1]=a1; y[2]=a2; y[3]=a3;
- }
- } else if (u_mode == 58) {
- // single reduce over P partials -> flat fused QKV
- int P_val = u_i1;
- int PXW = u_i0;
- vec4 acc = vec4(0);
- for (int i=0;i<64;i++){
- if (i >= P_val) break;
- acc += texelFetch(u_in, ivec2(i*PXW + px, py), 0);
- }
- y[0]=acc.x; y[1]=acc.y; y[2]=acc.z; y[3]=acc.w;
- } else if (u_mode == 580) {
- // MRT reduce: Q, K, V to 3 attachments at same (px,py)
- int P_val = u_i1;
- int PXW = u_i0;
- int d4 = u_i3;
- int kv4 = u_i4;
- vec4 q4 = vec4(0);
- if (px < d4) {
- for (int i=0;i<64;i++){
- if (i >= P_val) break;
- q4 += texelFetch(u_in, ivec2(i*PXW + px, py), 0);
- }
- }
- vec4 k4 = vec4(0);
- if (px < kv4) {
- for (int i=0;i<64;i++){
- if (i >= P_val) break;
- k4 += texelFetch(u_in, ivec2(i*PXW + d4 + px, py), 0);
- }
- }
- vec4 v4 = vec4(0);
- if (px < kv4) {
- for (int i=0;i<64;i++){
- if (i >= P_val) break;
- v4 += texelFetch(u_in, ivec2(i*PXW + d4 + kv4 + px, py), 0);
- }
- }
- y[0]=q4.x; y[1]=q4.y; y[2]=q4.z; y[3]=q4.w;
- y1[0]=k4.x; y1[1]=k4.y; y1[2]=k4.z; y1[3]=k4.w;
- y2[0]=v4.x; y2[1]=v4.y; y2[2]=v4.z; y2[3]=v4.w;
- } else if (u_mode == 38) {
- // append copy: read from flat pool at (srcOff+px, srcRow)
- int srcOff = u_i0;
- int srcRow = u_i1;
- vec4 v = texelFetch(u_in, ivec2(srcOff + px, srcRow), 0);
- y[0]=v.x; y[1]=v.y; y[2]=v.z; y[3]=v.w;
- } else if (u_mode == 99) {
- // trivial: read 1 texel, write same value to all outputs
- vec4 v = texelFetch(u_in, ivec2(px, py), 0);
- y[0]=v.x; y[1]=v.y; y[2]=v.z; y[3]=v.w;
- y1[0]=v.x; y1[1]=v.y; y1[2]=v.z; y1[3]=v.w;
- y2[0]=v.x; y2[1]=v.y; y2[2]=v.z; y2[3]=v.w;
- }
- // mode 98: noop (y stays zero)
- ${epi}
- }`;
- }
- // ============================================================ Programs
- let progSingle = null, progMulti = null;
- let U_single = {}, U_multi = {};
- function compileProgram(fragSrc) {
- const vs = `#version 300 es
- void main(){
- vec2 p = vec2((gl_VertexID<<1)&2, gl_VertexID&2);
- gl_Position = vec4(p*2.0-1.0, 0.0, 1.0);
- }`;
- function sh(type, src) {
- const h = gl.createShader(type);
- gl.shaderSource(h, src);
- gl.compileShader(h);
- if (!gl.getShaderParameter(h, gl.COMPILE_STATUS))
- throw new Error((type===gl.VERTEX_SHADER?'VS':'FS')+': '+gl.getShaderInfoLog(h));
- return h;
- }
- const p = gl.createProgram();
- gl.attachShader(p, sh(gl.VERTEX_SHADER, vs));
- gl.attachShader(p, sh(gl.FRAGMENT_SHADER, fragSrc));
- gl.linkProgram(p);
- if (!gl.getProgramParameter(p, gl.LINK_STATUS))
- throw new Error('LINK: '+gl.getProgramInfoLog(p));
- return p;
- }
- function grabUniforms(prog, names){
- const U = {};
- for (const n of names) U[n] = gl.getUniformLocation(prog, n);
- return U;
- }
- const UNAMES = ['u_in','u_w','u_mode','u_in_len','u_out_len','u_row_offset','u_f0','u_f1','u_i0','u_i1','u_i2','u_i3','u_i4','u_i5'];
- function setupPrograms() {
- progSingle = compileProgram(buildShader(false));
- U_single = grabUniforms(progSingle, UNAMES);
- progMulti = compileProgram(buildShader(true));
- U_multi = grabUniforms(progMulti, UNAMES);
- // ROOT-CAUSE FIX (microbench4 v2): sampler uniforms default to texture unit 0.
- // The original bench bound texX->unit0 / texW->unit1 but never called uniform1i for
- // u_in/u_w, so BOTH samplers read unit 0: mode 57 fetched "weights" from the x
- // texture. Weight fetch widx(c*OUTQ + col*4) is in-bounds there only while
- // c*OUTQ + col*4 < xSize (c < 34 with paged x, c < 4 with the original 1792-texel x);
- // strips sp>=1 (c >= 192) read fully out of bounds -> exactly 0. The surviving
- // x*x residue (~+/-0.005) is precisely the docs' Q[0]=-0.004 K[0]=V[0]=-0.007.
- // The MRT reduce (mode 580) was exact the whole time (V2: 0.0 vs flat).
- gl.useProgram(progSingle); gl.uniform1i(U_single.u_in, 0); gl.uniform1i(U_single.u_w, 1);
- gl.useProgram(progMulti); gl.uniform1i(U_multi.u_in, 0); gl.uniform1i(U_multi.u_w, 1);
- bootLog('ok','P.compile','progSingle + progMulti(MRT) linked; samplers u_in->unit0 u_w->unit1 (root-cause fix)');
- }
- // ============================================================ Textures / FBOs
- let texX, texW, texPartial, texQkvFlat, texQPool, texKCache, texVCache;
- let fboPartial, fboQkvFlat, fboQPool, fboKCache, fboVCache, fboMulti;
- const xSize = B_MAX*D + 1024;
- const xData = new Float32Array(xSize);
- const wSize = OUTQ * D;
- const wData = new Float32Array(wSize);
- function genData(){
- for (let i = 0; i < xSize; i++) xData[i] = Math.sin(i*0.05)*0.3;
- for (let c = 0; c < D; c++)
- for (let r = 0; r < OUTQ; r++)
- wData[c*OUTQ + r] = Math.sin(r*0.01 + c*0.02)*0.05;
- bootLog('dim','V.data',`x ${xSize} f32 (${B_MAX} rows), w ${OUTQ}x${D} f32 — gen OK`);
- }
- function createTex(w, h, data) {
- const t = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, t);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, w, h, 0, gl.RGBA, gl.FLOAT, data);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- return t;
- }
- // paged upload: flat float index -> texel (t&4095, t>>12), width = PAGE
- function uploadPaged(tex, data){
- gl.bindTexture(gl.TEXTURE_2D, tex);
- const rows = Math.ceil((data.length/4) / PAGE);
- for (let row = 0; row < rows; row++) {
- const start = row*PAGE*4;
- const end = Math.min(data.length, (row+1)*PAGE*4);
- if (end > start)
- gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, row, (end-start)/4, 1, gl.RGBA, gl.FLOAT, data.subarray(start, end));
- }
- }
- function makeFBO(attachments) {
- const f = gl.createFramebuffer();
- gl.bindFramebuffer(gl.FRAMEBUFFER, f);
- for (let i = 0; i < attachments.length; i++)
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, attachments[i], 0);
- const draws = [];
- for (let i = 0; i < attachments.length; i++) draws.push(gl.COLOR_ATTACHMENT0 + i);
- gl.drawBuffers(draws);
- const st = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- if (st !== gl.FRAMEBUFFER_COMPLETE)
- throw new Error(`FBO incomplete: 0x${st.toString(16)}`);
- return f;
- }
- function setupResources() {
- if (texX) return;
- genData();
- // input activations, paged 4096-wide (v2 FIX: original had height=1 -> silent clip >16k floats)
- const xPx = Math.ceil(xSize/4);
- const xH = Math.ceil(xPx / PAGE);
- texX = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, texX);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, PAGE, xH, 0, gl.RGBA, gl.FLOAT, null);
- uploadPaged(texX, xData);
- setNearest(texX);
- bootLog(xH>1?'warn':'dim','V.xtex',`texX ${PAGE}x${xH} paged (v2 fix: multi-row, ${xPx} texels)`);
- // weights: W^T[c*OUTQ + r]
- const wPx = Math.ceil(wSize/4);
- const wH = Math.ceil(wPx / PAGE);
- texW = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, texW);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, PAGE, wH, 0, gl.RGBA, gl.FLOAT, null);
- uploadPaged(texW, wData);
- setNearest(texW);
- texPartial = createTex(PXW_OUTQ*P, POOLH, null);
- texQkvFlat = createTex(PXW_OUTQ, POOLH, null);
- texQPool = createTex(PXW_Q, POOLH, null);
- texKCache = createTex(PXW_KV, POOLH, null);
- texVCache = createTex(PXW_KV, POOLH, null);
- fboPartial = makeFBO([texPartial]);
- fboQkvFlat = makeFBO([texQkvFlat]);
- fboQPool = makeFBO([texQPool]);
- fboKCache = makeFBO([texKCache]);
- fboVCache = makeFBO([texVCache]);
- fboMulti = makeFBO([texQPool, texKCache, texVCache]);
- gl.bindVertexArray(gl.createVertexArray());
- bootLog('ok','V.fbo',`partial ${PXW_OUTQ*P}x${POOLH}, flat ${PXW_OUTQ}x${POOLH}, pools ${PXW_Q}x${POOLH} x3, fboMulti 3-att COMPLETE`);
- }
- function setNearest(t){
- gl.bindTexture(gl.TEXTURE_2D, t);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- }
- // ============================================================ f64 CPU mirror
- // y[r] = sum_c x[row*D + c] * wData[c*OUTQ + r] (plain f64 dot — the ground truth)
- const refCache = new Map();
- function cpuRef(row){
- if (refCache.has(row)) return refCache.get(row);
- const y = new Float64Array(OUTQ);
- for (let c = 0; c < D; c++) {
- const xv = xData[row*D + c];
- const base = c*OUTQ;
- for (let r = 0; r < OUTQ; r++) y[r] += xv * wData[base + r];
- }
- refCache.set(row, y);
- return y;
- }
- // ============================================================ Dispatch helpers
- function dispatchSingle(cfg) {
- gl.useProgram(progSingle);
- gl.uniform1i(U_single.u_mode, cfg.mode ?? 98);
- gl.uniform1i(U_single.u_in_len, cfg.inLen ?? D);
- gl.uniform1i(U_single.u_out_len, cfg.outLen ?? OUTQ);
- gl.uniform1i(U_single.u_row_offset, cfg.rowOff ?? D);
- gl.uniform1i(U_single.u_i0, cfg.i0 ?? 0);
- gl.uniform1i(U_single.u_i1, cfg.i1 ?? 0);
- gl.uniform1i(U_single.u_i2, cfg.i2 ?? 0);
- gl.uniform1i(U_single.u_i3, cfg.i3 ?? 0);
- gl.uniform1i(U_single.u_i4, cfg.i4 ?? 0);
- gl.uniform1i(U_single.u_i5, cfg.i5 ?? 0);
- gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, cfg.texIn ?? texX);
- gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, cfg.texW ?? texW);
- gl.bindFramebuffer(gl.FRAMEBUFFER, cfg.fbo);
- gl.viewport(0, cfg.yOff ?? 0, cfg.vpW, cfg.vpH);
- gl.drawArrays(gl.TRIANGLES, 0, 3);
- }
- function dispatchMulti(cfg) {
- gl.useProgram(progMulti);
- gl.uniform1i(U_multi.u_mode, cfg.mode ?? 580);
- gl.uniform1i(U_multi.u_in_len, cfg.inLen ?? D);
- gl.uniform1i(U_multi.u_out_len, cfg.outLen ?? OUTQ);
- gl.uniform1i(U_multi.u_row_offset, cfg.rowOff ?? D);
- gl.uniform1i(U_multi.u_i0, cfg.i0 ?? 0);
- gl.uniform1i(U_multi.u_i1, cfg.i1 ?? 0);
- gl.uniform1i(U_multi.u_i2, cfg.i2 ?? 0);
- gl.uniform1i(U_multi.u_i3, cfg.i3 ?? 0);
- gl.uniform1i(U_multi.u_i4, cfg.i4 ?? 0);
- gl.uniform1i(U_multi.u_i5, cfg.i5 ?? 0);
- gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, cfg.texIn ?? texX);
- gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, cfg.texW ?? texW);
- gl.bindFramebuffer(gl.FRAMEBUFFER, cfg.fbo ?? fboMulti);
- gl.viewport(0, cfg.yOff ?? 0, cfg.vpW, cfg.vpH);
- gl.drawArrays(gl.TRIANGLES, 0, 3);
- }
- function readRow(fbo, attachment, wTexels, y, out){
- gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
- gl.readBuffer(gl.COLOR_ATTACHMENT0 + attachment);
- gl.readPixels(0, y, wTexels, 1, gl.RGBA, gl.FLOAT, out);
- gl.readBuffer(gl.COLOR_ATTACHMENT0);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- }
- // ============================================================ Thunk builders
- function thunkPartial(B) {
- return () => dispatchSingle({
- mode: 57, fbo: fboPartial, vpW: PXW_OUTQ*P, vpH: B,
- i0: P, i1: PXW_OUTQ, i2: Math.ceil(D/P), rowOff: D,
- texIn: texX, texW: texW,
- });
- }
- function thunkReduce(B) {
- return () => dispatchSingle({
- mode: 58, fbo: fboQkvFlat, vpW: PXW_OUTQ, vpH: B,
- i0: PXW_OUTQ, i1: P, texIn: texPartial,
- });
- }
- function thunkReduceMRT(B) {
- return () => dispatchMulti({
- mode: 580, fbo: fboMulti, vpW: PXW_Q, vpH: B,
- i0: PXW_OUTQ, i1: P, i2: 0, i3: PXW_Q, i4: PXW_KV,
- texIn: texPartial,
- });
- }
- function thunkAppendK(b, startPos) {
- return () => dispatchSingle({
- mode: 38, fbo: fboKCache, vpW: PXW_KV, vpH: 1, yOff: startPos + b,
- i0: PXW_Q, i1: b, texIn: texQkvFlat,
- });
- }
- function thunkAppendV(b, startPos) {
- return () => dispatchSingle({
- mode: 38, fbo: fboVCache, vpW: PXW_KV, vpH: 1, yOff: startPos + b,
- i0: PXW_Q + PXW_KV, i1: b, texIn: texQkvFlat,
- });
- }
- function thunksCurrent(B, startPos) {
- const th = [thunkPartial(B), thunkReduce(B)];
- for (let b = 0; b < B; b++) { th.push(thunkAppendK(b, startPos)); th.push(thunkAppendV(b, startPos)); }
- return th;
- }
- function thunksMRT(B) {
- return [thunkPartial(B), thunkReduceMRT(B)];
- }
- // ============================================================ Measurement
- function median(a){ const s=[...a].sort((x,y)=>x-y); const m=Math.floor(s.length/2); return s.length%2?s[m]:(s[m-1]+s[m])/2; }
- function fmt(ms){
- if (ms < 0.001) return (ms*1e6).toFixed(2)+'ns';
- if (ms < 1) return (ms*1000).toFixed(2)+'µs';
- return ms.toFixed(2)+'ms';
- }
- function measureSeq(thunks, drawCount) {
- const runOnce = () => { for (const t of thunks) t(); };
- for (let i = 0; i < MEAS.warm; i++) runOnce();
- const rb = new Float32Array(4);
- gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, rb);
- const est = [];
- for (let s = 0; s < MEAS.estLoops; s++) {
- const t0 = performance.now();
- for (let i = 0; i < MEAS.estReps; i++) runOnce();
- gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, rb);
- est.push(performance.now() - t0);
- }
- const estPer = Math.max(0.0005, median(est) / MEAS.estReps);
- const reps = Math.max(MEAS.minReps, Math.min(MEAS.maxReps, Math.ceil(MEAS.target / estPer)));
- const samples = [];
- for (let s = 0; s < MEAS.samples; s++) {
- const t0 = performance.now();
- for (let i = 0; i < reps; i++) runOnce();
- gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, rb);
- samples.push(performance.now() - t0);
- }
- const med = median(samples);
- const mn = Math.min(...samples);
- return { perRep: med / reps, reps, totalMs: med, confidence: mn / med, drawCount };
- }
- // ============================================================ Comparison core
- function compare(got, ref, refOff){
- let maxAbs = 0, maxRel = 0, worst = -1;
- for (let i = 0; i < got.length; i++) {
- const r = ref[refOff + i];
- const d = Math.abs(got[i] - r);
- const rel = d / Math.max(Math.abs(r), 0.05);
- if (d > maxAbs) { maxAbs = d; worst = i; }
- if (rel > maxRel) maxRel = rel;
- }
- return { maxAbs, maxRel, worst };
- }
- function maxDiff(a, b){
- let m = 0;
- for (let i = 0; i < a.length; i++){ const d = Math.abs(a[i]-b[i]); if (d > m) m = d; }
- return m;
- }
- function f6(x){ return (x>=0?'+':'') + x.toFixed(6); }
- function fe(x){ return x.toExponential(2); }
- // Full correctness ladder at batch B, spot-checked at `rows`.
- // V1: mode-58 flat reduce vs f64 mirror (covers 57 partial + uploads + readback)
- // V2: mode-580 MRT vs mode-58 flat (isolates 580 indexing on the SAME partials)
- // V3: mode-580 attachments vs f64 mirror (end-to-end)
- // DV: real distinctness max|K-V|, |Q-K|, |Q-V| (the docs' K==V==−0.007 anomaly detector)
- function verifyLadder(B, rows){
- thunkPartial(B)(); thunkReduce(B)(); thunkReduceMRT(B)();
- const out = { B, rows: [], v1:{maxAbs:0,ok:true}, v2:{maxAbs:0,ok:true},
- v3:{maxAbs:0,ok:true}, dist:{kv:0,qk:0,qv:0,ok:true} };
- const flat = new Float32Array(PXW_OUTQ*4);
- const atts = [new Float32Array(PXW_Q*4), new Float32Array(PXW_Q*4), new Float32Array(PXW_Q*4)];
- for (const row of rows) {
- const ref = cpuRef(row);
- readRow(fboQkvFlat, 0, PXW_OUTQ, row, flat);
- const c1 = compare(flat, ref, 0);
- out.v1.maxAbs = Math.max(out.v1.maxAbs, c1.maxAbs);
- for (let a = 0; a < 3; a++) readRow(fboMulti, a, PXW_Q, row, atts[a]);
- const segs = [[0,0],[768,768],[1536,1536]];
- const segNames = ['Q','K','V'];
- const per = [];
- for (let a = 0; a < 3; a++) {
- const cFlat = compare(atts[a], flat, segs[a][1]); // V2: att vs flat segment
- const cRef = compare(atts[a], ref, segs[a][0]); // V3: att vs f64 mirror
- out.v2.maxAbs = Math.max(out.v2.maxAbs, cFlat.maxAbs);
- out.v3.maxAbs = Math.max(out.v3.maxAbs, cRef.maxAbs);
- per.push({ seg: segNames[a], flat: cFlat, ref: cRef });
- }
- const kv = maxDiff(atts[1], atts[2]), qk = maxDiff(atts[0], atts[1]), qv = maxDiff(atts[0], atts[2]);
- out.dist.kv = Math.max(out.dist.kv, kv); out.dist.qk = Math.max(out.dist.qk, qk); out.dist.qv = Math.max(out.dist.qv, qv);
- out.rows.push({ row, c1, per, kv, qk, qv,
- k8: Array.from(atts[1].slice(0,8)), v8: Array.from(atts[2].slice(0,8)),
- refk8: Array.from(ref.slice(768,776)), refv8: Array.from(ref.slice(1536,1544)) });
- }
- out.v1.ok = out.v1.maxAbs < TOL.passAbs;
- out.v2.ok = out.v2.maxAbs < TOL.passAbs;
- out.v3.ok = out.v3.maxAbs < TOL.passAbs;
- out.dist.ok = out.dist.kv > TOL.distMin && out.dist.qk > TOL.distMin && out.dist.qv > TOL.distMin;
- return out;
- }
- function ladderLog(tag, L){
- bootLog(L.v1.ok?'ok':'bad', tag+'.v1', `flat(58) vs f64 mirror: B=${L.B} maxabs ${fe(L.v1.maxAbs)} ${L.v1.ok?'PASS':'FAIL'}`);
- bootLog(L.v2.ok?'ok':'bad', tag+'.v2', `MRT(580) vs flat(58) same partials: maxabs ${fe(L.v2.maxAbs)} ${L.v2.ok?'PASS':'FAIL'}`);
- bootLog(L.v3.ok?'ok':'bad', tag+'.v3', `MRT(580) vs f64 mirror: maxabs ${fe(L.v3.maxAbs)} ${L.v3.ok?'PASS':'FAIL'}`);
- bootLog(L.dist.ok?'ok':'bad', tag+'.kv', `distinctness max|K-V|=${L.dist.kv.toFixed(4)} max|Q-K|=${L.dist.qk.toFixed(4)} max|Q-V|=${L.dist.qv.toFixed(4)} ${L.dist.ok?'PASS (K!=V is real)':'FAIL (K==V anomaly REPRODUCED)'}`);
- const r0 = L.rows[0];
- bootLog('dim', tag+'.k8', `K[0..7]=${r0.k8.map(v=>v.toFixed(3)).join(' ')}`);
- bootLog('dim', tag+'.refk8',`refK[0..7]=${r0.refk8.map(v=>v.toFixed(3)).join(' ')}`);
- }
- // ============================================================ Tests
- const results = [];
- const issues = {};
- function addResult(test, config, m) {
- results.push({ test, config, ...m });
- renderResults();
- }
- function setIssue(id, verdict, metric, label, action, detail) {
- issues[id] = { id, verdict, metric, label, action, detail };
- renderIssueMap();
- console.log(`[issue ${id}] ${verdict} metric=${metric} ${label} — ${action}`);
- }
- async function testF() {
- const outs = [];
- for (const n of [1, 2, 4, 8, 16, 32]) {
- const thunks = [];
- for (let i = 0; i < n; i++)
- thunks.push(() => dispatchSingle({ mode: 98, fbo: fboQkvFlat, vpW: PXW_Q, vpH: 1 }));
- const m = measureSeq(thunks, n);
- addResult('F', `noop ×${n}`, m);
- outs.push({ n, perRep: m.perRep });
- }
- const perDraw16 = outs[4].perRep / 16;
- const perDraw32 = outs[5].perRep / 32;
- const floor = Math.min(perDraw16, perDraw32);
- let verdict, action;
- if (floor < 0.003) { verdict='pass'; action=`draw floor is cheap (${(floor*1e3).toFixed(2)}µs) — count dispatches`; }
- else if (floor < 0.010) { verdict='warn'; action='moderate floor (3-10µs) — count dispatches'; }
- else { verdict='fail'; action='high floor — reduce draw count or switch to WebGPU'; }
- setIssue('F', verdict, floor*1e3, 'µs/draw', action,
- `noop×16 ${(perDraw16*1e3).toFixed(2)}µs/draw, ×32 ${(perDraw32*1e3).toFixed(2)}µs/draw (readPixels-sync inflated at low N — ×16/×32 is the estimate)`);
- return floor;
- }
- async function testS() {
- const mrtThunk = () => dispatchMulti({ mode: 99, fbo: fboMulti, vpW: PXW_Q, vpH: 1, texIn: texX });
- const mMRT = measureSeq([mrtThunk], 1);
- addResult('S', 'MRT ×1 (3 out)', mMRT);
- const sepThunks = [
- () => dispatchSingle({ mode: 99, fbo: fboQPool, vpW: PXW_Q, vpH: 1, texIn: texX }),
- () => dispatchSingle({ mode: 99, fbo: fboKCache, vpW: PXW_Q, vpH: 1, texIn: texX }),
- () => dispatchSingle({ mode: 99, fbo: fboVCache, vpW: PXW_Q, vpH: 1, texIn: texX }),
- ];
- const mSep = measureSeq(sepThunks, 3);
- addResult('S', '3 × sep (1 out)', mSep);
- const ratio = mMRT.perRep / Math.max(0.0001, mSep.perRep);
- let verdict, action;
- if (ratio < 0.8) { verdict='pass'; action=`MRT saves ${((1-ratio)*100).toFixed(0)}% per trivial 3-target write — multi-target write is genuinely cheaper`; }
- else if (ratio < 1.05) { verdict='warn'; action='MRT ≈ separate — marginal benefit'; }
- else { verdict='fail'; action='MRT slower than separate — skip arch shift'; }
- setIssue('S', verdict, ratio, 'MRT/sep', action, `MRT ${fmt(mMRT.perRep)} vs sep ${fmt(mSep.perRep)}`);
- }
- async function testQ() {
- const th = thunksCurrent(1, 0);
- const m = measureSeq(th, th.length);
- addResult('Q', 'current 57+58+38K+38V (B=1)', m);
- setIssue('Q', 'pass', m.perRep*1e3, 'µs/rep', 'decode baseline: 4 draws (57+58+38K+38V) — keep for decode',
- `per-rep ${fmt(m.perRep)} over ${th.length} draws`);
- return m;
- }
- async function testQM(_floorUs, qRef) {
- const th = thunksMRT(1);
- const m = measureSeq(th, th.length);
- addResult('QM', 'MRT 57+580 (B=1)', m);
- if (!qRef) { setIssue('QM','warn', 0, 'n/a', 'no Q baseline measured — run Q first', ''); return m; }
- const ratio = m.perRep / Math.max(1e-7, qRef.perRep);
- const savings = 1 - ratio;
- let verdict, action;
- if (ratio <= 0.98) { verdict='pass'; action=`decode slice saves ${(savings*100).toFixed(1)}% — still noise-level per token`; }
- else if (ratio <= 1.02) { verdict='warn'; action='savings ≈ 0 (docs predict 3.6% on the slice = 0.7%/token) — not worth the Q-pool memory'; }
- else { verdict='fail'; action='MRT decode path SLOWER — keep decode on 3-dispatch'; }
- setIssue('QM', verdict, savings*100, '% saved', action,
- `MRT ${fmt(m.perRep)} vs current ${fmt(qRef.perRep)} (${th.length} vs ${qRef.drawCount ?? 4} draws) — 2 appends eliminated`);
- return m;
- }
- async function testP() {
- const thCur = thunksCurrent(8, 0);
- const mCur = measureSeq(thCur, thCur.length);
- addResult('P', `current (B=8, ${thCur.length} draws)`, mCur);
- const thMRT = thunksMRT(8);
- const mMRT = measureSeq(thMRT, thMRT.length);
- addResult('P', `MRT (B=8, ${thMRT.length} draws)`, mMRT);
- const ratio = mMRT.perRep / Math.max(1e-7, mCur.perRep);
- let verdict, action;
- if (ratio < 0.85) { verdict='pass'; action=`MRT saves ${((1-ratio)*100).toFixed(0)}% on B=8 prefill slice`; }
- else if (ratio < 1.0) { verdict='warn'; action='MRT saves <15% on prefill — marginal'; }
- else { verdict='fail'; action='MRT slower on prefill — skip'; }
- setIssue('P', verdict, ratio, 'MRT/cur B=8', action,
- `MRT ${fmt(mMRT.perRep)} vs current ${fmt(mCur.perRep)} — 16 appends eliminated`);
- }
- async function testP100(floorUs) {
- // correctness spot rows FIRST (outside the timed region)
- const L = verifyLadder(100, [0, 50, 99]);
- ladderLog('p100', L);
- const spotOK = L.v1.ok && L.v2.ok && L.v3.ok;
- const thCur = thunksCurrent(100, 0);
- const mCur = measureSeq(thCur, thCur.length);
- addResult('P100', `current (B=100, ${thCur.length} draws)`, mCur);
- const thMRT = thunksMRT(100);
- const mMRT = measureSeq(thMRT, thMRT.length);
- addResult('P100', `MRT (B=100, ${thMRT.length} draws)`, mMRT);
- const saved = mCur.perRep - mMRT.perRep;
- const floorModel = 200 * (floorUs ?? 0.00266);
- const explained = saved > 0 ? Math.min(1, floorModel / saved) : 0;
- const ratio = mMRT.perRep / Math.max(1e-7, mCur.perRep);
- let verdict, action;
- if (!spotOK) { verdict='fail'; action='B=100 correctness FAILED — do not integrate'; }
- else if (ratio < 0.85) { verdict='pass'; action=`B=100 confirms extrapolation: ${fmt(saved)} saved/rep, floor model explains ${(explained*100).toFixed(0)}%`; }
- else if (ratio < 1.0) { verdict='warn'; action='B=100 savings <15% — floor model overestimated'; }
- else { verdict='fail'; action='MRT slower at B=100 — docs extrapolation broken'; }
- setIssue('P100', verdict, ratio, 'MRT/cur B=100', action,
- `saved ${fmt(saved)} vs floor model ${(floorModel*1e3).toFixed(0)}µs (200 appends × floor); spot rows {0,50,99} ${spotOK?'OK':'FAILED'}; ` +
- `extrapolated ×32 layers ≈ ${(saved*32*1e3).toFixed(0)}µs/prefill (slice-level saving × all layers)`);
- }
- async function testV() {
- const L = verifyLadder(8, [0, 3, 7]);
- ladderLog('V', L);
- // cross-check the f64 mirror itself against the independent numpy ground truth
- const ref0 = cpuRef(0);
- const gtOK = Math.abs(ref0[0] - GT_ROW0.q) < 1e-4 && Math.abs(ref0[768] - GT_ROW0.k) < 1e-4 && Math.abs(ref0[1536] - GT_ROW0.v) < 1e-4;
- bootLog(gtOK?'ok':'bad','V.cpu',`f64 mirror row0: Q=${f6(ref0[0])} K=${f6(ref0[768])} V=${f6(ref0[1536])} — numpy GT ${f6(GT_ROW0.q)}/${f6(GT_ROW0.k)}/${f6(GT_ROW0.v)} ${gtOK?'MATCH':'MISMATCH'}`);
- window.__ladderV = L;
- const r0 = L.rows[0];
- if (L.v1.ok && L.v2.ok && L.v3.ok && L.dist.ok) {
- setIssue('V', 'pass', L.v3.maxAbs, 'maxabs vs f64', 'MRT writes Q,K,V correctly; K != V verified on FULL 768-float rows',
- `V1 ${fe(L.v1.maxAbs)} / V2 ${fe(L.v2.maxAbs)} / V3 ${fe(L.v3.maxAbs)}; max|K-V|=${L.dist.kv.toFixed(3)}; K[0]=${r0.k8[0].toFixed(4)} V[0]=${r0.v8[0].toFixed(4)} (docs' -0.007/-0.007 NOT reproduced)`);
- } else if (!L.v2.ok) {
- setIssue('V', 'fail', L.v2.maxAbs, 'maxabs MRT/flat', 'mode 580 MRT reduce diverges from mode 58 flat on the SAME partials -> 580 indexing bug',
- JSON.stringify(L.rows[0].per));
- } else if (!L.v1.ok) {
- setIssue('V', 'fail', L.v1.maxAbs, 'maxabs flat/f64', 'flat(58) wrong vs f64 mirror while MRT==flat -> bug in 57 partial, weight/x upload, or readback',
- `row0 K[0]=${r0.k8[0].toFixed(4)} vs ref ${r0.refk8[0].toFixed(4)}`);
- } else {
- setIssue('V', 'fail', L.dist.kv, 'max|K-V|', 'K==V anomaly REPRODUCED — values match but are not distinct (docs\' suspicion confirmed)',
- `max|K-V|=${L.dist.kv.toFixed(4)}`);
- }
- return L;
- }
- // ============================================================ Render
- function renderResults() {
- const tb = document.getElementById('resultsBody');
- tb.innerHTML = '';
- for (const r of results) {
- const tr = document.createElement('tr');
- tr.innerHTML = `
- <td>${r.test}</td><td>${r.config}</td>
- <td class="num">${fmt(r.perRep)}</td><td class="num">${r.reps}</td>
- <td class="num">${r.totalMs.toFixed(1)}</td><td class="num">${r.confidence.toFixed(2)}</td>
- <td class="num">${r.drawCount}</td>`;
- tb.appendChild(tr);
- }
- }
- function fmtMetric(m){
- if (m !== 0 && Math.abs(m) < 0.001) return m.toExponential(2);
- return m.toFixed(3);
- }
- function renderIssueMap() {
- const tb = document.getElementById('issueMapBody');
- const order = ['F','S','Q','QM','P','P100','V'];
- const names = {
- F: 'Dispatch floor', S: 'MRT vs separate (trivial)',
- Q: 'Current QKV path B=1', QM: 'MRT QKV path B=1',
- P: 'Prefill B=8', P100: 'Prefill B=100 + spot rows', V: 'MRT correctness ladder',
- };
- tb.innerHTML = '';
- for (const id of order) {
- const tr = document.createElement('tr');
- const iss = issues[id];
- if (!iss) {
- tr.className = 'pending';
- tr.innerHTML = `<td>${id}</td><td>${names[id]}</td><td class="num">—</td><td class="verdict">pending</td><td>—</td>`;
- } else {
- tr.className = iss.verdict;
- tr.innerHTML = `<td>${id}</td><td>${names[id]}</td>` +
- `<td class="num">${fmtMetric(iss.metric)} ${iss.label}</td>` +
- `<td class="verdict">${iss.verdict}</td><td>${iss.action}</td>`;
- }
- tb.appendChild(tr);
- }
- }
- function renderInterpretation() {
- const el = document.getElementById('interpretation');
- const lines = [];
- lines.push('<b>Summary:</b>');
- for (const id of ['F','S','Q','QM','P','P100','V']) {
- const i = issues[id];
- if (i) lines.push(` <span class="${i.verdict==='pass'?'ok':i.verdict==='warn'?'warn':'bad'}">[${i.verdict}]</span> ${id} — ${i.detail}`);
- }
- lines.push('');
- lines.push('<b>What v2 fixes vs microbench4 (docs appendage):</b>');
- lines.push(' • Q/QM now call setIssue — issue map no longer shows "pending"');
- lines.push(' • V is a ladder: V1 flat-vs-f64, V2 MRT-vs-flat (same partials), V3 MRT-vs-f64, over FULL 768-float rows at rows {0,3,7} + real K!=V distinctness');
- lines.push(' • K[0]==V[0]==−0.007 ROOT-CAUSED live on SwiftShader: the bench never set the u_in/u_w sampler uniforms (GLSL samplers default to unit 0) → mode 57 fetched weights from the x texture; only c<⌈xSize/OUTQ⌉ terms were in-bounds, strips sp≥1 exactly zero → a ~±0.005 x·x residue that still passes nonzero+distinctness smoke checks. MRT reduce (580) was EXACT the whole time (V2 diff 0.0). Fix: gl.uniform1i(u_in,0)+gl.uniform1i(u_w,1) after link — one line, driver-independent.');
- lines.push(' • P100: B=100 prefill (202 vs 2 draws) with per-row inputs + spot-row correctness {0,50,99}');
- lines.push(' • v2 latent fix: texX is paged 4096-wide now — original height=1 silently clipped inputs >16k floats (breaks B≥22)');
- lines.push('');
- lines.push('<b>Arch shift memory analysis (docs):</b>');
- lines.push(' Q pool must be (d/4, maxLen) for MRT to align Q, K, V at the same (x, p).');
- const sizes = [
- ['25M (d=512, len=1024)', 512, 1024],
- ['1B (d=2048, len=2048)', 2048, 2048],
- ['7B (d=4096, len=4096)', 4096, 4096],
- ['13B (d=5120, len=4096)', 5120, 4096],
- ];
- for (const [name, d, len] of sizes) {
- const mb = d * len * 4 / (1024*1024);
- lines.push(` ${name}: Q pool = ${mb.toFixed(1)} MB (shared across layers)`);
- }
- lines.push(' • GQA models (70B: d=8192, kvd=1024): Q width ≠ K/V width — MRT needs equal dims; pad = 8× KV waste ⇒ out');
- lines.push(' • decode (startPos>0): Q pool needs maxLen rows — priced above; prefill (startPos=0): B rows only — free');
- lines.push('');
- const fails = Object.values(issues).filter(i => i.verdict === 'fail');
- const warns = Object.values(issues).filter(i => i.verdict === 'warn');
- if (fails.length) {
- lines.push('<b>Priority fixes:</b>');
- for (const i of fails) lines.push(`- <b>${i.id}</b>: ${i.action}`);
- }
- if (warns.length) {
- lines.push('<b>Secondary:</b>');
- for (const i of warns) lines.push(`- <b>${i.id}</b>: ${i.action}`);
- }
- if (!fails.length && !warns.length && issues.V) {
- lines.push('<b>Decision (per docs):</b> implement MRT for PREFILL ONLY, gate on startPos === 0 — memory-free, biggest win at high B. '+
- 'Keep decode on the current 3-dispatch path. Then profile the REAL model\'s prefill with MRT on: this bench\'s GEMV is layout-bound, '+
- 'the measured % is draw-floor savings; the real prefill gains depend on its floor-vs-bandwidth mix.');
- }
- el.innerHTML = lines.join('<br>');
- }
- // ============================================================ Orchestrator
- const TESTS = [
- ['chkF','F',testF], ['chkS','S',testS], ['chkQ','Q',testQ], ['chkQM','QM',testQM],
- ['chkP','P',testP], ['chkP100','P100',testP100], ['chkV','V',testV],
- ];
- let sweepToken = 0;
- async function runTests(enabledIds) {
- const myToken = ++sweepToken;
- const btn = document.getElementById('btnSweep');
- btn.disabled = true;
- window.__benchDone = false;
- const t0 = performance.now();
- try {
- if (!progSingle) setupPrograms();
- setupResources();
- let floorUs = null, qRef = null;
- // V first — never trust timings from an engine that fails correctness
- if (enabledIds.includes('V')) { await testV(); if (myToken !== sweepToken) return; }
- for (const [chkId, tid, fn] of TESTS) {
- if (tid === 'V' || !enabledIds.includes(tid)) continue;
- document.getElementById('status').textContent = `running ${tid}…`;
- try {
- const r = await fn(floorUs, qRef);
- if (tid === 'F') floorUs = r;
- if (tid === 'Q') qRef = r;
- } catch (e) {
- document.getElementById('status').textContent = `test ${tid} crashed: ${e.message}`;
- setIssue(tid, 'fail', 0, 'error', `crashed: ${e.message}`, String(e.message).slice(0,200));
- }
- if (myToken !== sweepToken) return;
- await new Promise(r => setTimeout(r, 20));
- }
- renderInterpretation();
- document.getElementById('status').textContent = `done in ${((performance.now()-t0)/1000).toFixed(1)}s`;
- } catch (e) {
- document.getElementById('status').textContent = 'FATAL: ' + e.message;
- console.error(e);
- bootLog('bad','FATAL', e.message);
- }
- window.__benchDone = true;
- btn.disabled = false;
- }
- document.getElementById('btnSweep').onclick = () => {
- const ids = TESTS.filter(([id]) => document.getElementById(id).checked).map(([,tid]) => tid);
- runTests(ids);
- };
- document.getElementById('btnClear').onclick = () => {
- sweepToken++;
- results.length = 0;
- Object.keys(issues).forEach(k => delete issues[k]);
- renderResults(); renderIssueMap(); renderInterpretation();
- document.getElementById('status').textContent = 'cleared';
- window.__benchDone = true;
- };
- // programmatic hook for headless harness
- window.__bench = {
- state: () => ({ issues, results, boot: bootLines }),
- bootText: () => bootLines.map(l => l.line).join('\n'),
- runSweep: (ids) => runTests(ids),
- ladder: () => window.__ladderV,
- };
- // ============================================================ Boot
- (async () => {
- if (!gl) return;
- try {
- setupPrograms();
- setupResources();
- renderIssueMap();
- bootLog('ok','boot',`ready — ${FAST?'fast':'full'} measure profile, TOL pass ${TOL.passAbs}`);
- // self-test on load: correctness ladder + decode slice timing (minichat style: fail fast, numbers first)
- await runTests(['V','Q','QM']);
- window.__benchReady = true;
- document.getElementById('status').textContent +=
- '\nSelf-test done (V/Q/QM auto-ran). Use "Run selected" for the full sweep.';
- } catch (e) {
- document.getElementById('status').textContent = 'FATAL: ' + e.message;
- console.error(e);
- bootLog('bad','FATAL', e.message);
- window.__benchReady = true;
- }
- })();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment