Quazim0t0 commited on
Commit
9cb6d04
·
verified ·
1 Parent(s): 61bbf62

Upload web/public/webgpu.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. web/public/webgpu.js +54 -16
web/public/webgpu.js CHANGED
@@ -510,7 +510,7 @@
510
  enc.copyBufferToBuffer(bufC, 0, read, 0, bytesC);
511
  device.queue.submit([enc.finish()]);
512
  await read.mapAsync(GPUMapMode.READ);
513
- const out = new Int32Array(read.getMappedRange().slice(0));
514
  read.unmap();
515
  return { out, bufC, read };
516
  }
@@ -524,7 +524,7 @@
524
  { binding: 0, resource: { buffer: bufX } }, { binding: 1, resource: { buffer: bufW } },
525
  { binding: 2, resource: { buffer: lutBuf } }, { binding: 3, resource: { buffer: bufC } },
526
  { binding: 4, resource: { buffer: bufD } } ], m, n);
527
- [bufX, bufW, bufD, r.bufC, r.read].forEach(b => b.destroy());
528
  return r.out;
529
  }
530
 
@@ -540,7 +540,7 @@
540
  { binding: 0, resource: { buffer: bufQ } }, { binding: 1, resource: { buffer: bufK } },
541
  { binding: 2, resource: { buffer: bufQs } }, { binding: 3, resource: { buffer: bufKs } },
542
  { binding: 4, resource: { buffer: bufO } }, { binding: 5, resource: { buffer: bufD } } ], T, T, B * heads, d.acc);
543
- [bufQ, bufK, bufQs, bufKs, bufD, r.bufO, r.read].forEach(b => b.destroy());
544
  return r.out;
545
  }
546
  async function gpuAttCtx(device, pipeline, aq, vq, as, vs, d) {
@@ -554,7 +554,7 @@
554
  { binding: 0, resource: { buffer: bufA } }, { binding: 1, resource: { buffer: bufV } },
555
  { binding: 2, resource: { buffer: bufAs } }, { binding: 3, resource: { buffer: bufVs } },
556
  { binding: 4, resource: { buffer: bufO } }, { binding: 5, resource: { buffer: bufD } } ], T, hd, B * heads, d.acc);
557
- [bufA, bufV, bufAs, bufVs, bufD, r.bufO, r.read].forEach(b => b.destroy());
558
  return r.out;
559
  }
560
 
@@ -585,9 +585,9 @@
585
  enc.copyBufferToBuffer(bufO, 0, read, 0, m * n * 4);
586
  device.queue.submit([enc.finish()]);
587
  await read.mapAsync(GPUMapMode.READ);
588
- const out = new Float32Array(read.getMappedRange().slice(0));
589
  read.unmap();
590
- [bufA, bufB, bufP, bufD1, bufO, bufD2, read].forEach(b => b.destroy());
591
  return out;
592
  }
593
 
@@ -605,7 +605,7 @@
605
  enc.copyBufferToBuffer(bufO, 0, read, 0, bytesO);
606
  device.queue.submit([enc.finish()]);
607
  await read.mapAsync(GPUMapMode.READ);
608
- const buf = read.getMappedRange().slice(0);
609
  const out = raw ? new Int32Array(buf) : new Float32Array(buf);
610
  read.unmap();
611
  return { out, bufO, read };
@@ -623,7 +623,7 @@
623
  { binding: 2, resource: { buffer: lutBuf } }, { binding: 3, resource: { buffer: bufR } },
624
  { binding: 4, resource: { buffer: bufS } }, { binding: 5, resource: { buffer: bufO } },
625
  { binding: 6, resource: { buffer: bufD } } ], m, n, batch, d.acc);
626
- [bufX, bufW, bufR, bufS, bufD, r.bufO, r.read].forEach(b => b.destroy());
627
  return r.out;
628
  }
629
 
@@ -645,7 +645,7 @@
645
  { binding: 0, resource: { buffer: bufX } }, { binding: 1, resource: { buffer: bufW } },
646
  { binding: 2, resource: { buffer: bufR } }, { binding: 3, resource: { buffer: bufS } },
647
  { binding: 4, resource: { buffer: bufO } }, { binding: 5, resource: { buffer: bufD } } ], m, n, batch, d.acc);
648
- [bufX, bufW, bufR, bufS, bufD, r.bufO, r.read].forEach(b => b.destroy());
649
  return r.out;
650
  }
651
 
@@ -670,7 +670,9 @@
670
  }
671
  const bufRs = up(device, xs, SU), bufCs1 = up(device, w1s, SU), bufCs2 = up(device, w2s, SU);
672
  const bufH = mk(device, m * h * 4, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC);
673
- const bufMX = mk(device, m * 4, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC); // zero-initialized
 
 
674
  const UU = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
675
  const bufD1 = up(device, new Uint32Array([m, kw1, h, 1]), UU); // flags=1: fused ReLU
676
  const bufDM = up(device, new Uint32Array([m, h, 0, 0]), UU);
@@ -680,6 +682,7 @@
680
  : [bufA, bufB, env.lutBuf, bufR, bufC, bufO, bufD])
681
  .map((b, i) => ({ binding: i, resource: { buffer: b } })) });
682
  const enc1 = device.createCommandEncoder();
 
683
  let pass = enc1.beginComputePass();
684
  pass.setPipeline(env.gemm);
685
  pass.setBindGroup(0, gemmBind(bufX, bufW1, bufRs, bufCs1, bufH, bufD1));
@@ -696,9 +699,9 @@
696
  enc1.copyBufferToBuffer(bufMX, 0, readM, 0, m * 4);
697
  device.queue.submit([enc1.finish()]);
698
  await Promise.all([readH.mapAsync(GPUMapMode.READ), readM.mapAsync(GPUMapMode.READ)]);
699
- const h1 = new Float32Array(readH.getMappedRange().slice(0)); readH.unmap();
700
  // the atomicMax'ed u32 bit patterns ARE the f32 |max| values
701
- const mx = new Float32Array(readM.getMappedRange().slice(0)); readM.unmap();
702
  // scale derivation in JS f64 — exactly rounded, identical on every device
703
  // (WGSL division is 2.5 ULP, which is why it never runs on the GPU)
704
  const sc = root.Verified.scalesFromAbsMax(mx);
@@ -722,18 +725,53 @@
722
  enc2.copyBufferToBuffer(bufO, 0, readO, 0, m * n * 4);
723
  device.queue.submit([enc2.finish()]);
724
  await readO.mapAsync(GPUMapMode.READ);
725
- const out = new Float32Array(readO.getMappedRange().slice(0)); readO.unmap();
726
- [bufX, bufW1, bufW2, bufRs, bufCs1, bufCs2, bufH, bufMX, bufD1, bufDM,
727
- bufInv, bufHs, bufQ, bufDQ, bufD2, bufO, readH, readM, readO].forEach(b => b.destroy());
728
  return { h1, out };
729
  }
730
 
731
- function mk(device, size, usage) { return device.createBuffer({ size, usage }); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732
  function up(device, arr, usage) {
733
  const b = mk(device, Math.max(16, arr.byteLength), usage);
734
  device.queue.writeBuffer(b, 0, arr);
735
  return b;
736
  }
 
 
 
 
 
 
 
 
 
737
 
738
  // ---- canonical kernel probe -------------------------------------------------
739
  // The weight hash CANNOT catch a device whose kernel is wrong: weights only
 
510
  enc.copyBufferToBuffer(bufC, 0, read, 0, bytesC);
511
  device.queue.submit([enc.finish()]);
512
  await read.mapAsync(GPUMapMode.READ);
513
+ const out = new Int32Array(read.getMappedRange(0, bytesC).slice(0)); // pooled: map only the logical range
514
  read.unmap();
515
  return { out, bufC, read };
516
  }
 
524
  { binding: 0, resource: { buffer: bufX } }, { binding: 1, resource: { buffer: bufW } },
525
  { binding: 2, resource: { buffer: lutBuf } }, { binding: 3, resource: { buffer: bufC } },
526
  { binding: 4, resource: { buffer: bufD } } ], m, n);
527
+ release(device, [bufX, bufW, bufD, r.bufC, r.read]);
528
  return r.out;
529
  }
530
 
 
540
  { binding: 0, resource: { buffer: bufQ } }, { binding: 1, resource: { buffer: bufK } },
541
  { binding: 2, resource: { buffer: bufQs } }, { binding: 3, resource: { buffer: bufKs } },
542
  { binding: 4, resource: { buffer: bufO } }, { binding: 5, resource: { buffer: bufD } } ], T, T, B * heads, d.acc);
543
+ release(device, [bufQ, bufK, bufQs, bufKs, bufD, r.bufO, r.read]);
544
  return r.out;
545
  }
546
  async function gpuAttCtx(device, pipeline, aq, vq, as, vs, d) {
 
554
  { binding: 0, resource: { buffer: bufA } }, { binding: 1, resource: { buffer: bufV } },
555
  { binding: 2, resource: { buffer: bufAs } }, { binding: 3, resource: { buffer: bufVs } },
556
  { binding: 4, resource: { buffer: bufO } }, { binding: 5, resource: { buffer: bufD } } ], T, hd, B * heads, d.acc);
557
+ release(device, [bufA, bufV, bufAs, bufVs, bufD, r.bufO, r.read]);
558
  return r.out;
559
  }
560
 
 
585
  enc.copyBufferToBuffer(bufO, 0, read, 0, m * n * 4);
586
  device.queue.submit([enc.finish()]);
587
  await read.mapAsync(GPUMapMode.READ);
588
+ const out = new Float32Array(read.getMappedRange(0, m * n * 4).slice(0)); // pooled: logical range only
589
  read.unmap();
590
+ release(device, [bufA, bufB, bufP, bufD1, bufO, bufD2, read]);
591
  return out;
592
  }
593
 
 
605
  enc.copyBufferToBuffer(bufO, 0, read, 0, bytesO);
606
  device.queue.submit([enc.finish()]);
607
  await read.mapAsync(GPUMapMode.READ);
608
+ const buf = read.getMappedRange(0, bytesO).slice(0); // pooled: logical range only
609
  const out = raw ? new Int32Array(buf) : new Float32Array(buf);
610
  read.unmap();
611
  return { out, bufO, read };
 
623
  { binding: 2, resource: { buffer: lutBuf } }, { binding: 3, resource: { buffer: bufR } },
624
  { binding: 4, resource: { buffer: bufS } }, { binding: 5, resource: { buffer: bufO } },
625
  { binding: 6, resource: { buffer: bufD } } ], m, n, batch, d.acc);
626
+ release(device, [bufX, bufW, bufR, bufS, bufD, r.bufO, r.read]);
627
  return r.out;
628
  }
629
 
 
645
  { binding: 0, resource: { buffer: bufX } }, { binding: 1, resource: { buffer: bufW } },
646
  { binding: 2, resource: { buffer: bufR } }, { binding: 3, resource: { buffer: bufS } },
647
  { binding: 4, resource: { buffer: bufO } }, { binding: 5, resource: { buffer: bufD } } ], m, n, batch, d.acc);
648
+ release(device, [bufX, bufW, bufR, bufS, bufD, r.bufO, r.read]);
649
  return r.out;
650
  }
651
 
 
670
  }
671
  const bufRs = up(device, xs, SU), bufCs1 = up(device, w1s, SU), bufCs2 = up(device, w2s, SU);
672
  const bufH = mk(device, m * h * 4, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC);
673
+ // rowmax accumulates with atomicMax and NEEDS zeros. A fresh buffer was
674
+ // zero-initialized; a POOLED buffer is not — cleared in the encoder below.
675
+ const bufMX = mk(device, m * 4, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST);
676
  const UU = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
677
  const bufD1 = up(device, new Uint32Array([m, kw1, h, 1]), UU); // flags=1: fused ReLU
678
  const bufDM = up(device, new Uint32Array([m, h, 0, 0]), UU);
 
682
  : [bufA, bufB, env.lutBuf, bufR, bufC, bufO, bufD])
683
  .map((b, i) => ({ binding: i, resource: { buffer: b } })) });
684
  const enc1 = device.createCommandEncoder();
685
+ enc1.clearBuffer(bufMX, 0, m * 4); // pooled buffer: zero the atomicMax accumulator
686
  let pass = enc1.beginComputePass();
687
  pass.setPipeline(env.gemm);
688
  pass.setBindGroup(0, gemmBind(bufX, bufW1, bufRs, bufCs1, bufH, bufD1));
 
699
  enc1.copyBufferToBuffer(bufMX, 0, readM, 0, m * 4);
700
  device.queue.submit([enc1.finish()]);
701
  await Promise.all([readH.mapAsync(GPUMapMode.READ), readM.mapAsync(GPUMapMode.READ)]);
702
+ const h1 = new Float32Array(readH.getMappedRange(0, m * h * 4).slice(0)); readH.unmap();
703
  // the atomicMax'ed u32 bit patterns ARE the f32 |max| values
704
+ const mx = new Float32Array(readM.getMappedRange(0, m * 4).slice(0)); readM.unmap();
705
  // scale derivation in JS f64 — exactly rounded, identical on every device
706
  // (WGSL division is 2.5 ULP, which is why it never runs on the GPU)
707
  const sc = root.Verified.scalesFromAbsMax(mx);
 
725
  enc2.copyBufferToBuffer(bufO, 0, readO, 0, m * n * 4);
726
  device.queue.submit([enc2.finish()]);
727
  await readO.mapAsync(GPUMapMode.READ);
728
+ const out = new Float32Array(readO.getMappedRange(0, m * n * 4).slice(0)); readO.unmap();
729
+ release(device, [bufX, bufW1, bufW2, bufRs, bufCs1, bufCs2, bufH, bufMX, bufD1, bufDM,
730
+ bufInv, bufHs, bufQ, bufDQ, bufD2, bufO, readH, readM, readO]);
731
  return { h1, out };
732
  }
733
 
734
+ // ---- buffer pool ------------------------------------------------------------
735
+ // Every dispatch used to create and destroy its buffers — ~19 create/destroy
736
+ // pairs per MLP chain call, per layer, per step. Creation cost is pure driver
737
+ // overhead, and at these GEMM sizes it is a large share of the step. The pool
738
+ // recycles buffers by (usage, size-bucket): same bytes uploaded, same regions
739
+ // read, ZERO math change — the kernels only ever index inside the logical
740
+ // dims, so the stale tail of a bucketed buffer is never read. The one
741
+ // exception is a buffer a kernel expects ZEROED (the rowmax accumulator):
742
+ // pooled buffers are NOT fresh, so that one is cleared explicitly in the
743
+ // encoder (see gpuMlpChain). Correctness is enforced where it always was:
744
+ // the exact init gates, the live audit, and the per-step probe hash all run
745
+ // through these pooled paths.
746
+ const _pools = new WeakMap(); // device -> Map(key -> free list)
747
+ function _poolOf(device) {
748
+ let p = _pools.get(device);
749
+ if (!p) { p = new Map(); _pools.set(device, p); }
750
+ return p;
751
+ }
752
+ function mk(device, size, usage) {
753
+ let cap = 256; while (cap < size) cap *= 2;
754
+ const key = usage + ":" + cap;
755
+ const list = _poolOf(device).get(key);
756
+ if (list && list.length) return list.pop();
757
+ const b = device.createBuffer({ size: cap, usage });
758
+ b._poolKey = key;
759
+ return b;
760
+ }
761
  function up(device, arr, usage) {
762
  const b = mk(device, Math.max(16, arr.byteLength), usage);
763
  device.queue.writeBuffer(b, 0, arr);
764
  return b;
765
  }
766
+ function release(device, bufs) {
767
+ const p = _poolOf(device);
768
+ for (const b of bufs) {
769
+ if (!b || !b._poolKey) { if (b) b.destroy(); continue; }
770
+ let list = p.get(b._poolKey);
771
+ if (!list) { list = []; p.set(b._poolKey, list); }
772
+ if (list.length < 64) list.push(b); else b.destroy();
773
+ }
774
+ }
775
 
776
  // ---- canonical kernel probe -------------------------------------------------
777
  // The weight hash CANNOT catch a device whose kernel is wrong: weights only