The film
About 4:30, hosted by Pylon. It asks you one question and waits for your answer before it shows you. The guide's voice is not recorded yet, so for now the film plays with captions; the full words are also below.
Nothing loads until you press play.
The lab: the trays
Pick a kernel and a size; count every read and check the answer with the emulator; then, if your browser has WebGPU, run the real kernel on your own chip and time it. In the film, at 1,024 wide, tiles of 16 took 134,217,728 reads against 2,147,483,648.
Counting runs in this page. Timing runs the kernel on your own graphics chip, only when you press Time. Nothing is sent anywhere.
Everything the film says
Matmul tiling
Matmul tiling.
Most of the arithmetic in a language model is multiplying tables of numbers. Tonight Pylon shows a classic trick for doing that fast on a graphics chip: carry a tray, not one can at a time.
One table times another
Multiplying two square tables, each number of the answer is a row of the first table times a column of the second: multiply pairs, add them up.
For tables 1,024 wide, one answer number needs 2,048 reads from the warehouse, 1,024 from each table.
One can at a time
The simple kernel gives each thread one answer number and lets it fetch everything itself. Every number of each table is then fetched 1,024 times, once for every thread that needs it.
In all, 2,147,483,648 reads from the warehouse, for one multiplication.
A tray to the bench
Now give a crew of threads a square of the answer. Together they carry a square tile of each table to their bench, the chip's small fast shared memory, and wait until every thread has put its part down.
Then each thread works through the tile on the bench. When every thread is done, the crew fetches the next pair of tiles. With tiles 16 wide, each number is fetched 64 times instead of 1,024.
Tiles 16 wide. How many times fewer reads from the warehouse? (the film pauses here)
Counting the reads
Reads from the warehouse: 2,147,483,648 for the simple kernel; 268,435,456 with tiles of 8; 134,217,728 with tiles of 16. The wider the tile, the fewer the trips.
Another way to say it: work done per byte fetched. The simple kernel does 0.25 operations per byte, counting each multiply and each add as one. Tiles of 16 do 3.97. Vasily Volkov and James Demmel used this counting in 2008 when they tuned matrix code for graphics chips.
Wait for the whole tray
The waiting matters. If a thread starts working before the crew has put the whole tile down, it can read a can from the last tile.
We broke the kernel that way on purpose. In our emulator, under the worst timing the missing wait allows, its answers came out wrong at every size we tried, and the checker, which compares each number with a careful answer computed on the main processor, caught it.
What the count leaves out
These are counts of reads asked for, not times. Real chips have caches that can serve repeated reads, so the simple kernel may not be as slow as its count says. How much it gains depends on the chip.
The lab runs both kernels on your own chip, if your browser allows it, and times them beside the count.
Goodnight
Carry a tray once, use it many times. Fast matrix code on graphics chips is built on the same idea.
Next in the cannery: attention itself, done in one trip. Goodnight.
Sources
Every claim in the film, with where it comes from and what that source does not prove. The numbers about the model are computed from its weights, which you can load and check: facts.json.
Night School's matmul emulator and kernels, built for this film. media/school/tiling/facts.json, written by tools/school/fill_tiling.mjs from system/school/tiling.mjs: naive and tiled WGSL matmul kernels, an exact emulator of global and shared memory traffic, barriers and arithmetic for n = 64 to 1,024, a float32 tolerance and a missing-barrier control. https://github.com/HarperZ9/HarperZ9.github.io/tree/main/media/school/tiling
Used for: n = 1,024: global loads 2,147,483,648 (naive), 268,435,456 (tile 8), 134,217,728 (tile 16). Flops per global byte 0.25, 1.99, 3.97. The stale-tile kernel fails at every size.
Does not prove: Any timing; caches are not modelled.
NVIDIA CUDA C++ Programming Guide, release 12.6 (archived), shared memory. NVIDIA (2024). CUDA C++ Programming Guide, release 12.6, section 3.2.4 (shared memory matrix multiplication example). https://docs.nvidia.com/cuda/archive/12.6.0/cuda-c-programming-guide/
Used for: With shared memory tiles, A is read B.width / block_size times from global memory and B is read A.height / block_size times.
Does not prove: Anything about other vendors' chips.
Volkov and Demmel 2008, Benchmarking GPUs to tune dense linear algebra. Volkov, V., Demmel, J. W. (2008). Benchmarking GPUs to tune dense linear algebra. Proceedings of SC08. https://bebop.cs.berkeley.edu/pubs/volkov2008-benchmarking.pdf
Used for: Blocking reduces global memory traffic for matrix multiply in proportion to the block size.
Does not prove: Anything about this emulator.
What this lesson leaves out
Counts of reads asked for, not times; caches can serve repeated reads, so the simple kernel may be faster than its count suggests.
The missing-wait failure was shown in the emulator under a worst-case schedule, not on a chip.
The kernels were checked against the main processor within a float32 error bound; they were not timed on a graphics chip for this film.
Before this: Threads, workgroups and memory (K21). Earlier: Quantisation (S18).