Humuhumu33 commited on
Commit
082923c
·
verified ·
1 Parent(s): d88b0fc

Upload roofline.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. roofline.html +45 -44
roofline.html CHANGED
@@ -4,7 +4,7 @@
4
  :root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--warn:#e0a94a;--line:#1e2836}
5
  *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:760px;margin:0 auto}
6
  h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
7
- .card{font-family:ui-monospace,monospace;padding:14px 16px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:12px}
8
  .big{font-size:22px;font-weight:700}.k{color:var(--dim)}.v{font-weight:600}
9
  .ok{color:var(--ok)}.no{color:var(--no)}.warn{color:var(--warn)}
10
  table{width:100%;border-collapse:collapse;font-family:ui-monospace,monospace;font-size:13px}
@@ -13,78 +13,79 @@
13
  </style></head><body>
14
  <h1>BitNet decode <span style="color:var(--ac)">roofline</span> on your GPU</h1>
15
  <p class="sub">Measures your GPU's achievable VRAM read bandwidth, then computes the batch-1 decode ceiling for BitNet-2B (0.69&nbsp;GB read per token) and how far the current kernel is from it.</p>
16
- <div id="status" class="card">measuring…</div>
17
  <div id="out"></div>
18
 
19
  <script type="module">
20
  const $=s=>document.querySelector(s), st=$("#status"), out=$("#out");
21
- const MODEL_GB = 0.69; // BitNet-2B t2 weights read per token
22
- const CUR_MS = 18.4; // measured ternary-kernel ms/tok on this app (RDNA-3)
 
23
 
24
- // A pure VRAM read-bandwidth kernel: grid-stride read the whole buffer, XOR-accumulate (defeats DCE),
25
- // every thread writes its accumulator so no read is optimized away. Time best-of-N → achievable GB/s.
26
  const WGSL = `
27
  @group(0) @binding(0) var<storage, read> data : array<vec4<u32>>;
28
  @group(0) @binding(1) var<storage, read_write> sink : array<u32>;
29
- @group(0) @binding(2) var<uniform> P : vec4<u32>; // x=#vec4 elems, y=total threads
30
  @compute @workgroup_size(256)
31
  fn main(@builtin(global_invocation_id) gid:vec3<u32>){
32
- let n=P.x; let stride=P.y; var acc=vec4<u32>(0u);
33
- var i=gid.x;
34
- loop { if(i>=n){break;} acc=acc ^ data[i]; i=i+stride; }
35
  sink[gid.x]=acc.x ^ acc.y ^ acc.z ^ acc.w;
36
  }`;
37
 
38
  (async()=>{
39
- if(!navigator.gpu){ st.innerHTML='<span class="no">✗ No WebGPU. Open in Chrome/Edge or a recent browser.</span>'; return; }
40
- const ad=await navigator.gpu.requestAdapter({powerPreference:"high-performance"});
 
 
 
 
41
  const L=ad.limits;
42
- const dev=await ad.requestDevice({requiredLimits:{maxStorageBufferBindingSize:L.maxStorageBufferBindingSize,maxBufferSize:L.maxBufferSize,maxComputeWorkgroupsPerDimension:L.maxComputeWorkgroupsPerDimension}});
 
 
 
 
 
43
  const info=ad.info||{};
44
- // buffer: as large as the binding allows, capped at 1 GiB, 16-byte aligned
45
- const cap=Math.min(L.maxStorageBufferBindingSize, 1024*1024*1024);
46
- const bytes=Math.floor(cap/16)*16;
47
  const nVec=bytes/16;
 
48
  const buf=dev.createBuffer({size:bytes, usage:GPUBufferUsage.STORAGE});
49
- const TOTAL=Math.min(L.maxComputeWorkgroupsPerDimension,65535)*256;
50
  const sink=dev.createBuffer({size:TOTAL*4, usage:GPUBufferUsage.STORAGE});
51
  const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
52
- dev.queue.writeBuffer(P,0,new Uint32Array([nVec, TOTAL, 0,0]));
53
  const mod=dev.createShaderModule({code:WGSL});
54
  const ci=await mod.getCompilationInfo(); const er=ci.messages.filter(m=>m.type==="error");
55
- if(er.length){ st.innerHTML='<span class="no">✗ WGSL: '+er[0].message+'</span>'; return; }
56
  const pipe=dev.createComputePipeline({layout:"auto",compute:{module:mod,entryPoint:"main"}});
57
  const bg=dev.createBindGroup({layout:pipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:buf}},{binding:1,resource:{buffer:sink}},{binding:2,resource:{buffer:P}}]});
58
  const rb=dev.createBuffer({size:4, usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});
59
- const wg=Math.min(L.maxComputeWorkgroupsPerDimension,65535);
 
60
  async function once(){ const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(pipe); p.setBindGroup(0,bg); p.dispatchWorkgroups(wg); p.end(); e.copyBufferToBuffer(sink,0,rb,0,4); const t0=performance.now(); dev.queue.submit([e.finish()]); await rb.mapAsync(GPUMapMode.READ); rb.unmap(); return performance.now()-t0; }
61
- st.textContent="warming + timing "+(bytes/1073741824).toFixed(2)+" GB reads…";
62
- await once(); await once(); // warm
63
- let best=1e9; for(let k=0;k<12;k++){ best=Math.min(best, await once()); }
64
- const gbps = (bytes/1073741824)/(best/1000); // achievable read GB/s
65
- const roofTok = gbps/MODEL_GB; // batch-1 decode ceiling
66
- const curTok = 1000/CUR_MS; // current ~54
67
- const curGBps = MODEL_GB/(CUR_MS/1000); // current effective ~37
68
- const pct = 100*curGBps/gbps; // how much of roofline the kernel reaches
69
- const gap = gbps/curGBps; // headroom multiplier
70
-
71
- st.innerHTML='<span class="ok">done</span> · adapter: <b>'+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim()+'</b>';
72
- const reachable = roofTok>=1000;
73
- const specNeeded = Math.max(1, 1000/roofTok);
74
  out.innerHTML=`
75
  <div class="card"><span class="k">Achievable VRAM read bandwidth</span><br><span class="big">${gbps.toFixed(0)} GB/s</span></div>
76
- <div class="card">
77
- <table>
78
- <tr><td>Weights read / token (BitNet-2B t2)</td><td class="n">${MODEL_GB} GB</td></tr>
79
- <tr><td><b>Bandwidth roofline (batch-1 ceiling)</b></td><td class="n"><b>${roofTok.toFixed(0)} tok/s</b></td></tr>
80
- <tr><td>Current kernel</td><td class="n">${curTok.toFixed(0)} tok/s · ${curGBps.toFixed(0)} GB/s</td></tr>
81
- <tr><td>Current kernel reaches</td><td class="n ${pct<20?'no':pct<60?'warn':'ok'}">${pct.toFixed(1)}% of roofline</td></tr>
82
- <tr><td>Kernel headroom to roofline</td><td class="n">${gap.toFixed(1)}×</td></tr>
83
- </table>
84
- </div>
85
  <div class="verdict ${reachable?'ok':'warn'}">${reachable
86
- ? '✓ >1000 tok/s is within the bandwidth roofline ('+roofTok.toFixed(0)+') on this GPU — a bandwidth-optimal ternary kernel ('+gap.toFixed(1)+'× headroom) gets there; speculative decode is margin.'
87
- : '⚠ This GPU\\'s roofline is '+roofTok.toFixed(0)+' tok/s at batch-1. A perfect kernel reaches ~'+roofTok.toFixed(0)+'; >1000 needs speculative decode accepting ≥'+specNeeded.toFixed(1)+' tokens/pass (or a dedicated card with more bandwidth).'}
88
  </div>`;
 
89
  })();
90
  </script></body></html>
 
4
  :root{--bg:#0a0d13;--panel:#111825;--ink:#e8ebf1;--dim:#8b95a7;--ac:#7c5cff;--ok:#48c26c;--no:#f0616d;--warn:#e0a94a;--line:#1e2836}
5
  *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 -apple-system,Segoe UI,Roboto,monospace;padding:24px;max-width:760px;margin:0 auto}
6
  h1{font-size:20px;margin:0 0 4px}.sub{color:var(--dim);font-size:13px;margin:0 0 18px}
7
+ .card{font-family:ui-monospace,monospace;padding:14px 16px;border:1px solid var(--line);border-radius:10px;background:var(--panel);margin-bottom:12px;word-break:break-word}
8
  .big{font-size:22px;font-weight:700}.k{color:var(--dim)}.v{font-weight:600}
9
  .ok{color:var(--ok)}.no{color:var(--no)}.warn{color:var(--warn)}
10
  table{width:100%;border-collapse:collapse;font-family:ui-monospace,monospace;font-size:13px}
 
13
  </style></head><body>
14
  <h1>BitNet decode <span style="color:var(--ac)">roofline</span> on your GPU</h1>
15
  <p class="sub">Measures your GPU's achievable VRAM read bandwidth, then computes the batch-1 decode ceiling for BitNet-2B (0.69&nbsp;GB read per token) and how far the current kernel is from it.</p>
16
+ <div id="status" class="card">starting…</div>
17
  <div id="out"></div>
18
 
19
  <script type="module">
20
  const $=s=>document.querySelector(s), st=$("#status"), out=$("#out");
21
+ const MODEL_GB = 0.69, CUR_MS = 18.4;
22
+ const say = (t,cls) => { st.textContent = t; st.className = "card" + (cls?" "+cls:""); };
23
+ window.addEventListener("unhandledrejection", e => say("✗ unhandled: " + (e.reason && (e.reason.message||e.reason)), "no"));
24
 
 
 
25
  const WGSL = `
26
  @group(0) @binding(0) var<storage, read> data : array<vec4<u32>>;
27
  @group(0) @binding(1) var<storage, read_write> sink : array<u32>;
28
+ @group(0) @binding(2) var<uniform> P : vec4<u32>; // x=#vec4 elems, y=total threads, z=repeat
29
  @compute @workgroup_size(256)
30
  fn main(@builtin(global_invocation_id) gid:vec3<u32>){
31
+ let n=P.x; let stride=P.y; let R=P.z; var acc=vec4<u32>(0u);
32
+ for(var rep=0u; rep<R; rep=rep+1u){ var i=gid.x; loop { if(i>=n){break;} acc=acc ^ data[i]; i=i+stride; } }
 
33
  sink[gid.x]=acc.x ^ acc.y ^ acc.z ^ acc.w;
34
  }`;
35
 
36
  (async()=>{
37
+ try{
38
+ if(!navigator.gpu){ say("✗ No WebGPU. Open in Chrome/Edge or a recent browser.","no"); return; }
39
+ say("requesting adapter…");
40
+ let ad = await navigator.gpu.requestAdapter({powerPreference:"high-performance"});
41
+ if(!ad) ad = await navigator.gpu.requestAdapter();
42
+ if(!ad){ say("✗ no GPU adapter available.","no"); return; }
43
  const L=ad.limits;
44
+ say("requesting device…");
45
+ const dev=await ad.requestDevice({requiredLimits:{
46
+ maxStorageBufferBindingSize:L.maxStorageBufferBindingSize, maxBufferSize:L.maxBufferSize,
47
+ maxComputeWorkgroupsPerDimension:L.maxComputeWorkgroupsPerDimension }});
48
+ dev.lost.then(i => say("✗ device lost: "+(i&&i.message||i.reason||""),"no"));
49
+ dev.pushErrorScope("validation");
50
  const info=ad.info||{};
51
+ // buffer capped by BOTH limits and 256 MB (safe on every GPU; we re-read it R times for signal)
52
+ const bytes=Math.floor(Math.min(L.maxStorageBufferBindingSize, L.maxBufferSize, 256*1024*1024)/16)*16;
 
53
  const nVec=bytes/16;
54
+ say("allocating "+(bytes/1048576).toFixed(0)+" MB…");
55
  const buf=dev.createBuffer({size:bytes, usage:GPUBufferUsage.STORAGE});
56
+ const wg=Math.min(L.maxComputeWorkgroupsPerDimension,65535), TOTAL=wg*256, R=16;
57
  const sink=dev.createBuffer({size:TOTAL*4, usage:GPUBufferUsage.STORAGE});
58
  const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
59
+ dev.queue.writeBuffer(P,0,new Uint32Array([nVec, TOTAL, R, 0]));
60
  const mod=dev.createShaderModule({code:WGSL});
61
  const ci=await mod.getCompilationInfo(); const er=ci.messages.filter(m=>m.type==="error");
62
+ if(er.length){ say("✗ WGSL: "+er[0].message,"no"); return; }
63
  const pipe=dev.createComputePipeline({layout:"auto",compute:{module:mod,entryPoint:"main"}});
64
  const bg=dev.createBindGroup({layout:pipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:buf}},{binding:1,resource:{buffer:sink}},{binding:2,resource:{buffer:P}}]});
65
  const rb=dev.createBuffer({size:4, usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});
66
+ const scopeErr = await dev.popErrorScope();
67
+ if(scopeErr){ say("✗ GPU validation: "+scopeErr.message,"no"); return; }
68
  async function once(){ const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(pipe); p.setBindGroup(0,bg); p.dispatchWorkgroups(wg); p.end(); e.copyBufferToBuffer(sink,0,rb,0,4); const t0=performance.now(); dev.queue.submit([e.finish()]); await rb.mapAsync(GPUMapMode.READ); rb.unmap(); return performance.now()-t0; }
69
+ say("timing "+(bytes/1048576*R).toFixed(0)+" MB reads…");
70
+ await once();
71
+ let best=1e9; for(let k=0;k<10;k++){ best=Math.min(best, await once()); }
72
+ const gbps = (bytes*R/1073741824)/(best/1000);
73
+ const roofTok=gbps/MODEL_GB, curTok=1000/CUR_MS, curGBps=MODEL_GB/(CUR_MS/1000);
74
+ const pct=100*curGBps/gbps, gap=gbps/curGBps, reachable=roofTok>=1000, specNeeded=Math.max(1,1000/roofTok);
75
+ say("done · adapter: "+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim(),"ok");
 
 
 
 
 
 
76
  out.innerHTML=`
77
  <div class="card"><span class="k">Achievable VRAM read bandwidth</span><br><span class="big">${gbps.toFixed(0)} GB/s</span></div>
78
+ <div class="card"><table>
79
+ <tr><td>Weights read / token (BitNet-2B t2)</td><td class="n">${MODEL_GB} GB</td></tr>
80
+ <tr><td><b>Bandwidth roofline (batch-1 ceiling)</b></td><td class="n"><b>${roofTok.toFixed(0)} tok/s</b></td></tr>
81
+ <tr><td>Current kernel</td><td class="n">${curTok.toFixed(0)} tok/s · ${curGBps.toFixed(0)} GB/s</td></tr>
82
+ <tr><td>Current kernel reaches</td><td class="n ${pct<20?'no':pct<60?'warn':'ok'}">${pct.toFixed(1)}% of roofline</td></tr>
83
+ <tr><td>Kernel headroom to roofline</td><td class="n">${gap.toFixed(1)}×</td></tr>
84
+ </table></div>
 
 
85
  <div class="verdict ${reachable?'ok':'warn'}">${reachable
86
+ ? '✓ >1000 tok/s is within this GPU\\'s roofline ('+roofTok.toFixed(0)+') — a bandwidth-optimal ternary kernel ('+gap.toFixed(1)+'× headroom) gets there; spec-decode is margin.'
87
+ : '⚠ This GPU\\'s batch-1 roofline is '+roofTok.toFixed(0)+' tok/s. A perfect kernel reaches ~'+roofTok.toFixed(0)+'; >1000 needs speculative decode accepting ≥'+specNeeded.toFixed(1)+' tokens/pass, or a card with more bandwidth.'}
88
  </div>`;
89
+ }catch(e){ say("✗ "+(e && (e.message||e)), "no"); }
90
  })();
91
  </script></body></html>