# One Kernel, Any Size

*Task 5 of 5 · [Matrix Multiply](https://gpu.rocks/learn/matrix-multiply-972e080b.md) · GPU.js Learn*

Every kernel so far had its size welded on: `output: [16, 16]`, loop
to 16. Real code multiplies whatever matrices show up. gpu.js has three switches for
that: `dynamicOutput: true` lets you call `kernel.setOutput([n, n])`
before each run, `dynamicArguments: true` lets the input arrays change size
between calls, and `loopMaxIterations` raises the safety cap so the loop bound
can be a *runtime argument* instead of a constant.

Pass the size in as a plain number, loop `k < size`, and one kernel
object serves an 8×8 and a 48×48 multiply back to back. This is the payoff of the module:
the naive triple loop from task 2, now packaged as a function that scales.

## Goal

**Goal:** make `multiply(a, b)` work for any square size up
to 64 using a *single* kernel — verify it on the 8×8 and 48×48 pairs provided.

## Requirements

- Kernel options: `dynamicOutput`, `dynamicArguments`, and `loopMaxIterations: 64`
- Take `size` as a third kernel argument and loop `k < size`
- In `multiply`, call `matmul.setOutput([n, n])` before `await`-ing the kernel
- Exactly one `createKernel` call serves both sizes

## Hint 1 — why the cap?

On the GPU backend a loop bound that isn't a compile-time constant becomes

```js
for (i = 0; i < LOOP_MAX; i++) {
  if (!(i < size)) break;
  // …
}
```

in the shader — `loopMaxIterations` *is* that LOOP_MAX. Set it to the
largest size you'll ever pass: 64 here.

## Hint 2 — sizing per call

Inside `multiply` — which is `async`, because a kernel call
is awaited:

```js
const n = a.length;
matmul.setOutput([n, n]);
return await matmul(a, b, n);
```

— set the launch shape first,
then invoke with the size as the last argument. Its callers then
`await multiply(…)` in turn.

## Hint 3 — the kernel

```js
function (a, b, size) {
  let sum = 0;
  for (let k = 0; k < size; k++) {
    sum += a[this.thread.y][k] * b[k][this.thread.x];
  }
  return sum;
}
```

with options

```js
{
  dynamicOutput: true,
  dynamicArguments: true,
  loopMaxIterations: 64,
}
```

## Same idea elsewhere

Shipping one kernel that covers a size range is standard practice everywhere:
CUDA kernels take M, N, K as launch parameters and pick grid dimensions at call time,
WebGPU dispatches a runtime-computed number of workgroups, and Metal binds sizes through
a constant buffer. Compile once, launch at any size — exactly what you just built.

## Starter code

```js
// One kernel, any size — no rebuilding between calls.
const gpu = new GPU({ mode });

// TODO: this kernel is welded to 8×8. Free it: dynamicOutput,
// dynamicArguments, loopMaxIterations: 64, and a size argument.
const matmul = gpu.createKernel(function (a, b) {
  let sum = 0;
  for (let k = 0; k < 8; k++) {
    sum += a[this.thread.y][k] * b[k][this.thread.x];
  }
  return sum;
}, { output: [8, 8] });

async function multiply(a, b) {
  const n = a.length;
  // TODO: point the kernel at an n×n launch before invoking,
  // and pass n in so the loop knows where to stop.
  return await matmul(a, b);
}

console.log('8×8  C[0][0] =', (await multiply(smallA, smallB))[0][0]);
console.log('48×48 C[0][0] =', (await multiply(bigA, bigB))[0][0]);
```

---

Interactive version: https://gpu.rocks/learn/matrix-multiply-972e080b/5

[Previous task](https://gpu.rocks/learn/matrix-multiply-972e080b/4.md)
