Task 2 of 4

Pay the Transfer Tax

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.

same +1 either way — the bill tracks bytes, not math
Goal: finish the + 1 kernel and the timeKernel helper — warm up, then average 20 timed calls — and log the per-call cost for both payload sizes.

Requirements

Hint 1 — why warm up here too?

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.

Hint 2 — the helper body
await kernel(arg);
const t0 = Date.now();
for (let i = 0; i < 20; i++) await kernel(arg);
return (Date.now() - t0) / 20;

Same idea elsewhere

The bus is the bottleneck everywhere: 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.

All tasks in Measuring Speed Honestly

  1. The First Call Is a Lie
  2. Pay the Transfer Tax
  3. Two Machines, Two Answers
  4. When the CPU Wins

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.