Task 1 of 4
The first time you invoke a kernel, gpu.js does far more than run it: it
transpiles your JavaScript function to shader code, hands it to the GPU
driver to compile and link, allocates buffers — and then runs it.
In auto mode there is more still: that first call is also where gpu.js asks the
browser for a WebGPU adapter and rebuilds your kernel for it, so the backend swap happens
inside the first await too. Every call after it skips straight to the run.
So timing the first call measures the compiler, not your kernel. How wide the gap looks depends on the backend and on what the driver has already cached: on this page the first call typically costs a few times a warm one on WebGPU and ten times or more on WebGL, and on a big kernel with a cold shader cache it is wider still. What never changes is that it happens exactly once — which is why every honest benchmark warms up first and throws that first measurement away.
Date.now() to time the
first call and the warmed-up average separately — and log both.Math.sin(x / 100) * 100 where x is this thread's indexDate.now() and log it: first call: N mswarm call: N msSnapshot the clock, do the work, subtract:
const t0 = Date.now();
// … the work …
console.log('first call:', Date.now() - t0, 'ms');One stopwatch around a loop of 10 calls, then divide:
t0 = Date.now();
for (let i = 0; i < 10; i++) await wave();
console.log('warm call:', (Date.now() - t0) / 10, 'ms');createComputePipeline, Metal
compiles MSL when the pipeline state is created. Benchmarking guides on all of them open
with the same rule — discard the first iteration.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.