GPU·

LazyWeights paper — Decoding weights between DRAM and L2 — What does it mean?

The news. On November 12, 2026, researchers from the University of Texas at Austin and Case Western Reserve University posted LazyWeights: Decode-on-Demand Edge LLMs on arXiv. It keeps llama.cpp's integer formats, tunes GPTQ so the codes compress better, Huffman-codes them losslessly, and decodes them in a small unit between the memory controller and L2. Coded Phi-3-mini, Mistral-7B and SmolLM-1.7B files come out 5.7–31.4% smaller than the best llama.cpp format at the same perplexity budget (the largest allowed rise in perplexity, a standard quality score, over the 16-bit model). The decoder exists as a synthesized chip design and an FPGA (reprogrammable chip) prototype, not a shipping chip, so its latency and energy numbers are projections. Read the paper →

Picture the warehouse at the end of a narrow road. To produce one token, the GPU must receive every weight in the model once — the whole warehouse crosses the road for every word it writes. Each weight then takes part in only about two arithmetic operations, so the workshop sits idle most of the time, waiting for trucks. This is why decode is memory-bound: time per token is roughly the bytes shipped divided by the road's capacity. On the 8 GB Jetson Orin Nano Super in the paper, that road carries 102 GB/s, and the authors' own fixed-width kernels already use 40–64% of it, so time per token tracks bytes per token.

So the obvious move is to ship fewer bytes. Fixed-width formats such as Q5_K_M or IQ4_XS give every weight the same number of bits, but the weights are bell-shaped: the middle codes appear far more often than the extreme ones, and the code entropy is well below the format's width. Vacuum-packing the parcels with a Huffman code removes that slack. The entropy idea itself is not new — the D-Quant explainer covers it for the KV cache. LazyWeights' contribution is the answer to a different question: where the parcels get unpacked.

Prefill vs Decode on the Roofline

Decode~1 FLOP/bytePrefill~100 FLOP/byte← memory-boundcompute-bound →

Same GPU, fundamentally different bottlenecks

There are three places to put the unpacking crew, and two of them fail. Unpack in the warehouse — expand the weights back to full width in DRAM after loading — and you save storage space, but full-size boxes still cross the road every token, so nothing gets faster. Unpack at the workbench — decode inside the GPU's GEMV kernel — and the road does carry small parcels, but every worker stops to cut tape: the kernel spends instructions on bit manipulation instead of math. On the Orin Nano Super, the paper measures software decoders running 15–20× slower than the rate DRAM delivers, which makes a token 2.5–3.2× slower than simply storing the same codes at fixed width.

LazyWeights unpacks at the loading dock: a small decode unit between the memory controller and L2, so DRAM stores and moves only coded bytes while L2 receives ordinary fixed-width codes. The GPU kernel is untouched, the decoded weights are bit-identical to a valid GGUF file, and model quality depends only on the quantizer. In the memory ladder, the decode happens on the one rung where bytes are actually counted.

A dock crew that is slower than the road just moves the traffic jam. Huffman decoding is sequential, so LazyWeights reshapes the parcels for many crews at once. Each 64×256 tile of codes is split into 32 independent lane streams of two rows each, and their 32-bit words are interleaved so one wide, aligned read fetches the next word for every lane. Codes are length-limited to 11 bits, so an 11-bit window always contains a whole codeword, and in 4-bit formats two codes form one 8-bit pair symbol, so each step yields two weights.

The crew is sized from the road. To keep pace, the unit must decode θ* = 8β / b_c weights per second, where β is DRAM bandwidth and b_c is coded bits per weight — 151–229 billion weights per second at 102 GB/s across the paper's operating points. Four 32-lane engines at 1 GHz (0.206 mm² in a pre-layout 7 nm-class design estimate) cover five of the six points. The authors also compared rANS, a coder with a better theoretical rate: it never compressed better here, and at equal throughput it needed up to 4.0× the area, because its per-symbol step includes a multiply where Huffman needs only a compare and a shift.

The packing itself is also tuned. llama.cpp fits each small sub-block of weights to its own range, which spreads codes evenly across the alphabet — right for fixed width, wrong for a coder. LazyWeights lets sub-blocks share one step size and sometimes widens that step, so codes bunch up where the weights do, and relies on GPTQ's error feedback to absorb the coarser grid. Huffman coding alone gives 6.2–20.7% savings; the quantizer change provides the rest of the 5.7–31.4% reduction, sometimes letting a coded wider format undercut a narrower llama.cpp format at equal quality.

Worked example (Mistral-7B on the Orin Nano Super). Hold three things fixed: the model, the 102 GB/s road, and a 1% perplexity budget. The paper's llama.cpp baseline is Q5_K_M at 5.668 bits per weight; LazyWeights reaches the budget at 3.886 bits per weight, about 3.5 GB of weights. Bytes per token scale with bits per weight, so the fixed-width file ships about 3.5 × 5.668 / 3.886 ≈ 5.1 GB per token. The bandwidth floor is then 5.1 / 102 ≈ 50 ms versus 3.5 / 102 ≈ 34 ms — 31% less, before any kernel overhead. The paper's per-token times sit above those floors, 83.3 ms for llama.cpp and 53.0 ms with the unit (projected conservatively from the same codes run at fixed width). Now move the crew to the workbench: applying the paper's general 2.5–3.2× software-decoding penalty to that 53.0 ms gives roughly 130–170 ms (illustrative) — the same small file, now slower than the uncompressed baseline. Where you unpack decides whether compression is a speed-up or a slow-down.

Where the weights are unpackedWhat crosses the DRAM busWhat the GPU kernel doesResult in the paper
In DRAM, after loading (warehouse)Full fixed-width bytesUnchangedSaves storage, not bandwidth (§IV-D)
Inside the GEMV kernel (workbench)Coded bytesSpends instructions on bit manipulationToken 2.5–3.2× slower than fixed width on Orin Nano Super (§I)
Unit between DRAM and L2 (loading dock)Coded bytes onlyUnchangedUp to 36% lower latency and 38% lower energy per token on Orin, projected, not measured silicon (abstract)

Two limits keep this in proportion. First, the dock is a projection, not a built chip: its latency and energy are estimated from measured device numbers and a pre-layout chip-design estimate, and the FPGA prototype decodes bit-exactly but at 112.5 MHz, not 1 GHz. Second, the trick shrinks only the weights. At long context the KV cache, left in fp16, becomes a larger share of each pass — for Mistral-7B at 4K tokens it is 0.54 GB against 3.5 GB of weights — so the relative saving falls as context grows. It does combine with other levers: prompt-lookup speculation cuts weight passes per token while the unit cuts bytes per pass, which takes the Orin gain to up to 54% lower latency. On the 4 GB Jetson Nano, where Mistral-7B does not fit at all, the coded size decides whether the model runs, and the paper reports it streaming 6.6× faster than llama.cpp at the 1% budget.

Goes deeper in: GPU & CUDA → Roofline Model → Prefill vs Decode

Related explainers

Frequently Asked Questions

Check what you knowMap your AI & GPU knowledge across every track — free, role-based