Without caching, generating token t+1 naively means re-running the whole sequence through the model again, recomputing keys and values for every position — O(t) wasted work per step. Since keys and values for already-seen positions never change (they only depend on the frozen input token at that position), they can be computed once and reused. Implement single-head causal self-attention generation with a KV cache: a "prefill" pass over the whole prompt at once (building the initial cache), then generating n_new_tokens one at a time, each time appending only the new token's key/value to the cache instead of recomputing the whole sequence.
# Prefill: compute K, V once for every prompt position and cache them.
K_cache, V_cache = X_prompt @ W_K, X_prompt @ W_V
# Each decode step, compute K/V ONLY for the new token, append to cache.
k_new, v_new = x_new @ W_K, x_new @ W_V; K_cache <- concat(K_cache, k_new)
# Attend the new token's query against the FULL cache (old + new).
out_new = softmax(q_new @ K_cache^T / sqrt(d_k)) @ V_cache
Prefill is just standard causal attention over the whole prompt — reuse [../01-attention-mechanisms/01-scaled-dot-product-attention]'s masked-softmax machinery for it. In the decode loop, only ever concatenate ONE new row onto K_cache/V_cache per step — recomputing cached rows anywhere in the loop defeats the entire point of this exercise.
Click "Run Tests" to test your implementation