Spaces:
Running
Running
roofline: batched passes + onSubmittedWorkDone for real bw
Browse files- roofline.html +17 -6
roofline.html
CHANGED
|
@@ -68,7 +68,7 @@ fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
|
| 68 |
const nVec=bytes/16;
|
| 69 |
say("allocating "+(bytes/1048576).toFixed(0)+" MB…");
|
| 70 |
const buf=dev.createBuffer({size:bytes, usage:GPUBufferUsage.STORAGE});
|
| 71 |
-
const wg=Math.min(L.maxComputeWorkgroupsPerDimension,65535), TOTAL=wg*256, R=
|
| 72 |
const sink=dev.createBuffer({size:TOTAL*4, usage:GPUBufferUsage.STORAGE});
|
| 73 |
const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
|
| 74 |
dev.queue.writeBuffer(P,0,new Uint32Array([nVec, TOTAL, R, 0]));
|
|
@@ -89,11 +89,22 @@ fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
|
| 89 |
// write the non-zero pattern into VRAM before we time reads
|
| 90 |
say("filling "+(bytes/1048576).toFixed(0)+" MB with non-zero pattern…");
|
| 91 |
{ const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(fpipe); p.setBindGroup(0,fbg); p.dispatchWorkgroups(wg); p.end(); dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); }
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
const roofTok=gbps/MODEL_GB, curTok=1000/CUR_MS, curGBps=MODEL_GB/(CUR_MS/1000);
|
| 98 |
const pct=100*curGBps/gbps, gap=gbps/curGBps, reachable=roofTok>=1000, specNeeded=Math.max(1,1000/roofTok);
|
| 99 |
say("done · adapter: "+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim(),"ok");
|
|
|
|
| 68 |
const nVec=bytes/16;
|
| 69 |
say("allocating "+(bytes/1048576).toFixed(0)+" MB…");
|
| 70 |
const buf=dev.createBuffer({size:bytes, usage:GPUBufferUsage.STORAGE});
|
| 71 |
+
const wg=Math.min(L.maxComputeWorkgroupsPerDimension,65535), TOTAL=wg*256, R=1;
|
| 72 |
const sink=dev.createBuffer({size:TOTAL*4, usage:GPUBufferUsage.STORAGE});
|
| 73 |
const P=dev.createBuffer({size:16, usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});
|
| 74 |
dev.queue.writeBuffer(P,0,new Uint32Array([nVec, TOTAL, R, 0]));
|
|
|
|
| 89 |
// write the non-zero pattern into VRAM before we time reads
|
| 90 |
say("filling "+(bytes/1048576).toFixed(0)+" MB with non-zero pattern…");
|
| 91 |
{ const e=dev.createCommandEncoder(); const p=e.beginComputePass(); p.setPipeline(fpipe); p.setBindGroup(0,fbg); p.dispatchWorkgroups(wg); p.end(); dev.queue.submit([e.finish()]); await dev.queue.onSubmittedWorkDone(); }
|
| 92 |
+
// Batch many read passes into ONE command buffer, submit once, sync with
|
| 93 |
+
// onSubmittedWorkDone. Moving tens of GB makes GPU time >> fixed submit/map
|
| 94 |
+
// latency, so wall-clock reflects real VRAM bandwidth (not the ~0.1 ms overhead floor).
|
| 95 |
+
const PASSES=64; // 64 × buffer ≈ 32–64 GB of reads
|
| 96 |
+
async function timeReads(passes){
|
| 97 |
+
const e=dev.createCommandEncoder();
|
| 98 |
+
for(let k=0;k<passes;k++){ const p=e.beginComputePass(); p.setPipeline(pipe); p.setBindGroup(0,bg); p.dispatchWorkgroups(wg); p.end(); }
|
| 99 |
+
const t0=performance.now();
|
| 100 |
+
dev.queue.submit([e.finish()]);
|
| 101 |
+
await dev.queue.onSubmittedWorkDone();
|
| 102 |
+
return performance.now()-t0;
|
| 103 |
+
}
|
| 104 |
+
say("timing "+(bytes/1048576*PASSES/1024).toFixed(1)+" GB of reads…");
|
| 105 |
+
await timeReads(4); // warm up (compile, clocks up)
|
| 106 |
+
let best=1e9; for(let k=0;k<5;k++){ best=Math.min(best, await timeReads(PASSES)); }
|
| 107 |
+
const gbps = (bytes*PASSES/1073741824)/(best/1000);
|
| 108 |
const roofTok=gbps/MODEL_GB, curTok=1000/CUR_MS, curGBps=MODEL_GB/(CUR_MS/1000);
|
| 109 |
const pct=100*curGBps/gbps, gap=gbps/curGBps, reachable=roofTok>=1000, specNeeded=Math.max(1,1000/roofTok);
|
| 110 |
say("done · adapter: "+((info.vendor||"?")+" "+(info.architecture||"")+" "+(info.device||"")).trim(),"ok");
|