Spaces:
Running
Running
roofline: fill buffer non-zero + exceed cache for real VRAM bw
Browse files- roofline.html +27 -3
roofline.html
CHANGED
|
@@ -22,6 +22,19 @@ 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>;
|
|
@@ -48,15 +61,23 @@ fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
|
| 48 |
dev.lost.then(i => say("✗ device lost: "+(i&&i.message||i.reason||""),"no"));
|
| 49 |
dev.pushErrorScope("validation");
|
| 50 |
const info=ad.info||{};
|
| 51 |
-
//
|
| 52 |
-
|
|
|
|
|
|
|
| 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=
|
| 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; }
|
|
@@ -65,6 +86,9 @@ fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
|
| 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();
|
|
|
|
| 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 |
+
// FILL: write a non-zero, non-compressible pattern so the read pass hits real VRAM
|
| 26 |
+
// (an uninitialised STORAGE buffer is zero/DCC-compressed → the memory controller
|
| 27 |
+
// never fetches it → measured bandwidth is fictional). Must run before timing.
|
| 28 |
+
const FILL = `
|
| 29 |
+
@group(0) @binding(0) var<storage, read_write> d : array<vec4<u32>>;
|
| 30 |
+
@group(0) @binding(1) var<uniform> P : vec4<u32>; // x=#vec4 elems, y=total threads
|
| 31 |
+
@compute @workgroup_size(256)
|
| 32 |
+
fn main(@builtin(global_invocation_id) gid:vec3<u32>){
|
| 33 |
+
let n=P.x; let stride=P.y; var i=gid.x;
|
| 34 |
+
loop { if(i>=n){break;}
|
| 35 |
+
d[i]=vec4<u32>(i*2654435761u+1u, i*40503u+7u, i^0x9e3779b9u, i*2246822519u+3u);
|
| 36 |
+
i=i+stride; }
|
| 37 |
+
}`;
|
| 38 |
const WGSL = `
|
| 39 |
@group(0) @binding(0) var<storage, read> data : array<vec4<u32>>;
|
| 40 |
@group(0) @binding(1) var<storage, read_write> sink : array<u32>;
|
|
|
|
| 61 |
dev.lost.then(i => say("✗ device lost: "+(i&&i.message||i.reason||""),"no"));
|
| 62 |
dev.pushErrorScope("validation");
|
| 63 |
const info=ad.info||{};
|
| 64 |
+
// Buffer must exceed the GPU's last-level cache (RDNA-3 Infinity Cache is up to 96 MB)
|
| 65 |
+
// so re-reads actually miss to VRAM. Aim as large as the device allows, floor 512 MB.
|
| 66 |
+
const wantBytes=Math.min(L.maxStorageBufferBindingSize, L.maxBufferSize, 1024*1024*1024);
|
| 67 |
+
const bytes=Math.floor(Math.max(512*1024*1024, wantBytes)/16)*16;
|
| 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=4;
|
| 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]));
|
| 75 |
+
// fill pipeline (non-zero pattern) — its own bind group, same buffer
|
| 76 |
+
const fmod=dev.createShaderModule({code:FILL});
|
| 77 |
+
const fci=await fmod.getCompilationInfo(); const fer=fci.messages.filter(m=>m.type==="error");
|
| 78 |
+
if(fer.length){ say("✗ FILL WGSL: "+fer[0].message,"no"); return; }
|
| 79 |
+
const fpipe=dev.createComputePipeline({layout:"auto",compute:{module:fmod,entryPoint:"main"}});
|
| 80 |
+
const fbg=dev.createBindGroup({layout:fpipe.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:buf}},{binding:1,resource:{buffer:P}}]});
|
| 81 |
const mod=dev.createShaderModule({code:WGSL});
|
| 82 |
const ci=await mod.getCompilationInfo(); const er=ci.messages.filter(m=>m.type==="error");
|
| 83 |
if(er.length){ say("✗ WGSL: "+er[0].message,"no"); return; }
|
|
|
|
| 86 |
const rb=dev.createBuffer({size:4, usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});
|
| 87 |
const scopeErr = await dev.popErrorScope();
|
| 88 |
if(scopeErr){ say("✗ GPU validation: "+scopeErr.message,"no"); return; }
|
| 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 |
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; }
|
| 93 |
say("timing "+(bytes/1048576*R).toFixed(0)+" MB reads…");
|
| 94 |
await once();
|