/ concept-collection / math-webgpu-sandbox
Sign in
concept-collection / math-webgpu-sandbox
math-webgpu-sandbox
Go to fileHistoryFork
.githubRemove CPU comparison run and timing table; tighten footnote
examplesScale examples to 1-4 s of GPU work so tic/toc comparisons are meaningful
scriptsScale examples to 1-4 s of GPU work so tic/toc comparisons are meaningful
srcRemove CPU comparison run and timing table; tighten footnote
testMATLAB-syntax scripts on WebGPU: fused kernels, tic/toc timing, CPU comparison
.gitignoreMATLAB-syntax scripts on WebGPU: fused kernels, tic/toc timing, CPU comparison
CLAUDE.mdRemove CPU comparison run and timing table; tighten footnote
index.htmlRemove CPU comparison run and timing table; tighten footnote
package-lock.jsonMATLAB-syntax scripts on WebGPU: fused kernels, tic/toc timing, CPU comparison
package.jsonMATLAB-syntax scripts on WebGPU: fused kernels, tic/toc timing, CPU comparison
README.mdRemove CPU comparison run and timing table; tighten footnote
test.htmlMATLAB-syntax scripts on WebGPU: fused kernels, tic/toc timing, CPU comparison
tsconfig.jsonMATLAB-syntax scripts on WebGPU: fused kernels, tic/toc timing, CPU comparison
vite.config.tsMATLAB-syntax scripts on WebGPU: fused kernels, tic/toc timing, CPU comparison

math-webgpu-sandbox#

Write a MATLAB script, run it on your GPU. The sandbox compiles MATLAB-syntax scripts to fused WebGPU compute kernels, times them with the script's own tic/toc, and — because everything you write is plain MATLAB — you can paste the same script into real MATLAB and compare the numbers. An optional in-browser CPU run through numbl's normal engine gives a third column without leaving the page.

Live page: https://concept-collection.github.io/math-webgpu-sandbox/

n = 8000000;
x = rand(n, 1);
y = zeros(n, 1);
tic;
for k = 1:600
  y = y + 0.1*sin(x + k) .* exp(-x) + x.^2;
end
toc
fprintf('checksum %.4f\n', mean(y));

That loop body is one GPU kernel, compiled once and replayed 600 times — about five billion element-updates, a few seconds on an integrated GPU.

How it works#

The script is parsed and lowered by numbl's JIT pipeline (reached through a numbl-src vite alias, the same arrangement turing-surface uses), which fixes every type and shape at compile time — n = 2048; A = rand(n); pins static shapes via exact-value propagation. The back end is this repo's: a planner maps the typed IR onto WebGPU, and an executor replays the resulting op sequence.

  • Fusion. numbl's inline pass folds single-use temps back into their consumer, and a sandbox-side pass (src/mgpu/fuse.ts) folds the rest — transcendentals, comparisons, logicals — which numbl's C backend declines but WGSL handles. One source line becomes one kernel. Generators fuse too: x = 2*rand(n,1) - 1 is a single kernel that hashes its way to uniform variates per element, touching no other buffer.
  • Reductions (sum/mean/prod/max/min/norm/dot) take a fused loader, so sum(a.*b + c) reads its operands exactly once. Vectors (and X(:)) reduce fully in two passes; matrices reduce per column, which in MATLAB's column-major layout is the contiguous direction.
  • A * B is a 16×16-tiled shared-memory GEMM (column-major, adapted from matmul-bench); matrix transpose is a tiled relayout; vector transpose, X(:) and reshape are views of the same buffer (free) when the source is never reassigned.
  • for loops replay, they don't unroll. The body is planned once; the loop variable lives in a dynamic-offset uniform with one slot per iteration, and all iterations are encoded into one submit. rand inside a loop mixes the iteration counter into its stream, so every pass draws fresh values.
  • tic/toc are synchronization points: pending GPU work is submitted and awaited, then wall-clock time is taken on the host. That is the same thing MATLAB's synchronous tic/toc measures, which is what makes the number comparable when you paste the script there.

What is (and isn't) supported#

Supported: elementwise math on real arrays (including comparisons, &/|/~, two-arg max/min, mod/rem, .^), zeros/ones/eye/rand/randn/ linspace/ranges, A*B, transpose, the reductions above, A(:)/reshape, counted for loops, tic/toc, disp/fprintf, semicolon-suppressed or echoed assignments, and literal row/matrix constants.

Deliberately rejected, with a source-located error rather than a wrong answer: indexing/slicing beyond (:), if/while, complex numbers, user-defined functions, variables that change size, and printing inside for loops (the body has nowhere to run host I/O — it replays on the GPU).

The comparison is honest, with two caveats#

  • The GPU computes in f32; WebGPU has no f64. MATLAB defaults to double. Timing comparisons are still meaningful; for the closest apples-to-apples, use single arrays in MATLAB. Values agree to single precision, and the scripts print checksums so you can see that they do.
  • rand/randn here are deterministic counter-based generators (PCG-flavored hash of element index and call site). Statistics match MATLAB's; individual draws do not, so checksums of random data are close but not equal.

Development#

npm install        # numbl must be checked out as a sibling: ../../numbl
npm run dev
npm run test:node  # correctness suite on desktop WebGPU (Dawn)
npm run test:gpu   # same suite in headless Chrome (SwiftShader fallback)

The GPU compiler runs from numbl's TypeScript sources directly (through the numbl-src alias), so no numbl build is needed.

License#

Apache-2.0

moveopenescclose