Throughput over iterations
Step line: accepted reference after each iteration. Dots: each candidate’s full-run reviews/s, coloured by decision. Dashed markers are milestones; hover or tap anything for details.
Cumulative speedup
Accepted reference divided by the original no-prefetch baseline.
Replicated-block gains
Bars: mean per-update gain from interleaved reference/candidate blocks. Markers: one-sided lower confidence bound. Promotion needs a positive lower bound.
Milestones
What changed and why it mattered. Curated milestones by default; pick decisions to list every matching iteration. Colours match the chart above.
How one update flows through CPU and GPU
The machine: AMD Ryzen 7 9850X3D (8 physical cores) and an RTX 5080. Five preparation workers each own a physical core; the main process uses the other three. Numbers below come from the accepted configuration of iteration 0090.
Preparation workers
- Read a user’s review history from parquet.
- Cut it into contexts of 128 reviews.
- Encode card, deck, note, preset and user IDs and the time features.
- Pack 512 contexts (up to 65,536 reviews) into one batch, crossing user boundaries so batches stay full (F29).
- Write the batch into one shared-memory buffer (F20/B01).
Main process
- Take the next ready batch, pin it, and start one asynchronous copy to the GPU (F20).
- Launch the forward pass, loss and backward pass through native C++ dispatch instead of Python autograd (F65).
- Launch grouped gradient copies from BF16 to the FP32 master weights, clipping and AdamW (F23).
- Read the loss and gradient norm of update n−2, not n, so it never stalls (F33/F33b).
GPU
- Expand the encoded features in device memory (F14).
- Run the RWKV recurrence forward and backward with exact NVRTC kernels (F31, F90). The WKV backward is the largest kernel, about 76 ms per large update.
- Apply the weight update; the FP32 master weights then refresh the BF16 copy used for the next update.
Why everything overlaps
Switch between the overlapped pipeline used today and a naive loop that does one step at a time.
The GPU is still idle about 40% of the time. One update launches thousands of small kernels, and on Windows (WDDM) every launch costs CPU time, so the main thread’s dispatch is now the bottleneck. That is why most recent gains came from launching less (native dispatch, F65), synchronizing less (F33, F37) and grouping copies (F23), rather than from faster maths.
How to read this
- Reviews/s is training throughput on a fixed cohort of 80 users (7,654,975 review presentations), including data loading, worker start-up, transfers and updates. Model: compact model (outside research). The original Alex/default model was only measured in early iterations and is not plotted.
- Exact parity gate. A candidate must reproduce the accepted run’s four loss values, every update, gradient norms and final weights bit for bit. Faster-but-different variants are kept separately (“fastmode”) and never become the reference.
- Machine-state noise. Identical single runs differ by roughly 5–10% depending on GPU clocks, background load and Windows scheduling, so a single full run can go down even when a change helps. That is why the accepted line is not monotonic (for example, around 0072).
- Replicated blocks. From iteration 0048 onward, candidates are measured in interleaved reference/candidate blocks. A block records the per-update gain; the campaign promotes only when the lower confidence bound is positive, marks a stack candidate when the mean is promising but not proven, extends with more blocks when undecided, and rejects otherwise.
- Decisions. “Accepted” becomes the new reference; everything else is kept only as an optional component or discarded.
All iterations
| Iter | Track | Reviews/s | × baseline | Decision | Block mean | Lower bound |
|---|