# Pay the Transfer Tax

*Task 2 of 4 · [Measuring Speed Honestly](https://gpu.rocks/learn/measuring-speed-honestly-b9188894.md) · GPU.js Learn*

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.

## Figures

- **same +1 either way — the bill tracks bytes, not math**

## Goal

**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

- Kernel returns `data[this.thread.x] + 1` — one instruction, on purpose
- In `timeKernel` (already `async` for you): `await` one *untimed* call to warm it up
- Then time 20 awaited calls with `Date.now()` and return the average ms per call
- Log both costs (the `small:`/`big:` lines are already wired up)

## 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

```js
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.

## Starter code

```js
// One-instruction kernel, two payload sizes. Cost tracks bytes, not math.
const gpu = new GPU({ mode });

function makePlusOne(n) {
  return gpu.createKernel(function (data) {
    // TODO: return this thread's element, plus one
    return data[this.thread.x];
  }, { output: [n] });
}

const smallKernel = makePlusOne(1024);   // small = 1,024 values
const bigKernel = makePlusOne(65536);    // big = 65,536 values

async function timeKernel(kernel, arg) {
  // TODO: warm up with one untimed call (task 1!),
  // then time 20 calls and return the average ms per call
  return 0;
}

console.log('small:', await timeKernel(smallKernel, small), 'ms/call');
console.log('big:', await timeKernel(bigKernel, big), 'ms/call');
```

---

Interactive version: https://gpu.rocks/learn/measuring-speed-honestly-b9188894/2

[Previous task](https://gpu.rocks/learn/measuring-speed-honestly-b9188894/1.md) · [Next task](https://gpu.rocks/learn/measuring-speed-honestly-b9188894/3.md)
