Task 2 of 4
A kernel call isn't just compute. Every invocation ships your input array from
JavaScript to GPU memory, runs, then ships the result back. For a one-instruction kernel
like value + 1, the arithmetic is nearly free — the ride is the whole
bill.
Below, the same trivial kernel runs on 1,024 values and on 65,536 values — 64× the data, one instruction per thread either way. Warm up first (task 1!), then measure, and read the two numbers together: 64× the payload does not cost 64× the time — on this page the big kernel usually lands under twice the small one — because most of a call is a fixed toll paid before any of your data moves. The part that does grow grows with bytes moved, not with arithmetic performed; the arithmetic here was free all along.
+ 1 kernel and the
timeKernel helper — warm up, then average 20 timed calls — and log the
per-call cost for both payload sizes.data[this.thread.x] + 1 — one instruction, on purposetimeKernel (already async for you): await one untimed call to warm it upDate.now() and return the average ms per callsmall:/big: lines are already wired up)makePlusOne builds two separate kernels, and each one
compiles on its own first call. Without the warm-up, the big kernel's timing would
include a compile — task 1's lie all over again.
await kernel(arg);
const t0 = Date.now();
for (let i = 0; i < 20; i++) await kernel(arg);
return (Date.now() - t0) / 20;cudaMemcpy across PCIe is the
classic hot spot in CUDA and ROCm profiles, WebGPU makes you stage the copies explicitly
with writeBuffer and mapAsync, and Apple's unified memory exists
precisely to shrink this tax. Arithmetic is cheap; moving bytes is not.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.