~/satyajit

Runtime dynamic compression: 1.5 bits per weight, counting only the experts in memory

mdjsonmcp

2026-09-26 · 23 min · quantization · mixture-of-experts · inference-optimization · offloading · llm · explainer

On 22 September Tim Dettmers announced a "runtime dynamic compression framework that achieves 1.5-2.0 bit compression at high quality", integrated into bitsandbytes2, "which starts as a private beta today". The paper is Runtime Dynamic Compression of Mixture of Experts: twelve pages, one author at Carnegie Mellon, set in the ICLR 2026 template while calling itself "the current version of this draft".

I read it end to end, digitized its result plots from the PDF's vector paths, and took apart the one piece of bitsandbytes2 anyone can download. The short version: this is not a new quantizer. It is an allocator and a runtime for mixture-of-experts (MoE) models. Offline, it picks a codec for every layer from a 13-rung ladder, and a fraction of that layer's experts to drop. At runtime it keeps changing which experts are resident. And "bits per weight" means resident bytes divided by all of the model's parameters. The experts that are not resident still exist, in CPU memory or on an NVMe disk, waiting to be swapped in.

What is decided offline, and what at runtime

"Runtime dynamic" is easy to over-read. Here is the split, from Sections 3 and 5:

DecisionWhenHow
Codec per layer, 0.7 to 5 bits per weightofflineiterative sensitivity probing and a Lagrangian allocator
Fraction of each layer's experts kept residentofflinethe same allocator, the same byte budget
Which experts are resident in each layerruntime, every 256 tokensdynamic REAP, up to four swaps per layer
Codec and count of resident expertsfixedswaps keep "the expert count and codecs fixed"
Bit width per token or per requestnever—

No per-token bit allocation, no codebook that changes at runtime. One byte budget is spent on two axes, how precisely each layer is stored and how many of its experts sit in fast memory, and only the membership of the second moves while the model runs.

Why static quantization runs out below 2 bits

Dynamic, or mixed-precision, quantization spends more bits on sensitive layers and fewer on the rest. Unsloth's dynamic GGUFs are the paper's reference point; I have audited one layer by layer for GLM-5.3-Flash. For dense models the paper calls this "largely solved": in its Figure 2, on a dense Qwen 3.8 27B, bnb2, QTIP, Unsloth and GSQ-RCO sit nearly on top of each other above 2.5 bits.

Below 2 bits, three things break.

Sensitivities stop adding up. An allocator needs a cost per layer, and the cheap way to get one is to compress one layer at a time on an untouched model. That works while compression is mild: "degradations remain additive under weak compression but become super-additive under strong compression". A layer that looks insensitive beside 16-bit neighbours can be the one that tips the model over once those neighbours are at 1.5 bits.

The ladder is too coarse. With codecs at 1, 2, 3, 4 and 5 bits, a layer whose sensitivity jumps between 1 and 2 bits has nowhere to sit. The paper blames this for QTIP's weaker showing: fractional QTIP codecs are possible, but encoding a matrix "can take days of GPU time".

Quantization alone does not reach the device. A 552B model at 2 bits is 138 GB of weights (my arithmetic). The paper's answer is a second axis, removing experts, without freezing the removal.

The codec: rotate, then look up a Gaussian codebook

bnb2 quantizes with HIGGS (Malinovskii et al.). A randomized Hadamard rotation spreads outliers across a block so the rotated weights look Gaussian, and a fixed codebook built for Gaussian vectors encodes them. It is the rotate-first idea behind TurboQuant, applied to weights with vector codes: a pp-dimensional group of weights becomes one kk-bit index, the rate is k/pk/p bits per weight, and fractional rates come for free. The paper's ladder:

{0.7, 1, 1.125, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3, 4, 5} bits per weight\{0.7,\ 1,\ 1.125,\ 1.25,\ 1.5,\ 1.75,\ 2.0,\ 2.25,\ 2.5,\ 2.75,\ 3,\ 4,\ 5\}\ \text{bits per weight}

The paper prints no kernel, but the bitsandbytes-kernels wheel on PyPI (more on it below) has a quantize_vq_l2 that does exactly this:

# bitsandbytes/functional.py, bitsandbytes-kernels 0.50.0.dev0 (abridged)
W_blocks = W.reshape(N, n_rot, R)                    # R = 1024 by default
l2_norms = W_blocks.norm(dim=2).clamp_(min=1e-12)    # one scale per row per block
W_unit = W_blocks / l2_norms.unsqueeze(2)
W_rot = W_unit.reshape(N * n_rot, R).to(torch.bfloat16)
_apply_hadamard_vq(W_rot, R, signs=signs)             # sign flip, then Hadamard
W_rot = W_rot.float() * (R**0.5)                      # to unit-Gaussian scale
# ...
packed, _ = quantize_vq_bitplane(vq_flat, p=p, codebook=codebook, index_bits=index_bits)
l2_scales = l2_norms.to(torch.float16)

That makes the overhead easy to count. One fp16 norm per 1,024 weights is 16/1024≈0.01616/1024 \approx 0.016 bits per weight, and the codebook is shared rather than stored per layer. The wheel's own allocator adds exactly that, scale_overhead = 16 / rot_blocksize, to every codec's rate. It lists 22 weight codecs, vector length 2 to 16 and index 4 to 20 bits, at 16 distinct rates from 1 to 5 bits. There is no 0.7-bit rung, and the paper's ladder has 13 rungs while its Section 4 prices "16 codecs"; the wheel looks like an earlier cut of the same system.

The rotation also makes allocation cheap. After it, a layer's quantization error is its weight variance times a constant of the codebook. The wheel ships those constants, measured on Gaussian data: 0.429 at 1 bit, 0.201 at 1.5, 0.094 at 2, 0.026 at 3 and 0.0068 at 4, within a factor of 1.5 to 1.7 of the Gaussian bound 2−2b2^{-2b} from 1 to 4 bits (my arithmetic). The paper fits its ladder with

φ(b)=2 a−c b,a=1.710,c=2.264,\varphi(b) = 2^{\,a - c\,b},\qquad a = 1.710,\quad c = 2.264,

and notes that cc is close to the 2 in the ideal Gaussian law D(b)=σ22−2bD(b) = \sigma^2 2^{-2b}. It is a fit, not a literal error: read as a normalized squared error, φ(0.7)=1.09\varphi(0.7) = 1.09 would be worse than rounding every weight to zero. What matters is the consequence. If distortion follows from the bit width alone, each layer only has to be probed at two codecs, and the rest interpolates.

Iterative sensitivity probing

Section 3.1 turns allocation into a loop. At iteration tt the model has a configuration c(t)c^{(t)} that gives each layer ii a codec qq and an expert-removal fraction rr. A layer's sensitivity to a setting is measured on the whole model, every other layer left where it is:

si(t)(q,r)=L(c(t)[i←(q,r)])−L(c(t))s_i^{(t)}(q, r) = \mathcal{L}\big(c^{(t)}[i \leftarrow (q, r)]\big) - \mathcal{L}\big(c^{(t)}\big)

L\mathcal{L} is log-perplexity on a calibration set (RedPajama). With bi(q,r)b_i(q, r) the bytes layer ii needs at a setting and BB the budget, the allocation is

min⁡{qi,ri}∑isi(t)(qi,ri)subject to∑ibi(qi,ri)≤B\min_{\{q_i, r_i\}} \sum_i s_i^{(t)}(q_i, r_i) \quad \text{subject to} \quad \sum_i b_i(q_i, r_i) \le B

A multiplier λ≥0\lambda \ge 0 prices every byte in units of log-perplexity. For a fixed λ\lambda each layer chooses on its own,

ci(t+1)(λ)=arg⁡min⁡(q,r)  si(t)(q,r)+λ bi(q,r),c_i^{(t+1)}(\lambda) = \arg\min_{(q, r)} \; s_i^{(t)}(q, r) + \lambda\, b_i(q, r),

and the dual search raises λ\lambda until the total fits. Settings are discrete, so the result "need not consume every available byte".

The chicken and egg is in sis_i. Sensitivities are only honest near the "edge of instability", where nothing has collapsed yet but any further compression would, and you only get there with a good allocation. ISP iterates its way there. Probe the uncompressed model, which only exposes the catastrophically fragile layers. Compress what looks safe. Re-probe under that compression, which exposes the next set. Stop when the allocation stops changing. The toy below runs that loop on the paper's ladder and distortion law, with twelve invented layers and an invented interaction between neighbours:

iterative sensitivity probing · toy model, paper's ladder and distortion law
probe onceround 1avg 1.231.751.1251.250.71.511.250.71.511.251.75ISPround 2avg 1.251.511.511.511.511.511.251.25layeramplitudeL01.6L10.5L20.8L30.35L41.1L50.4L60.6L70.3L80.9L90.45L100.7L111.4
uniform (1.25 everywhere)
toy loss 6.981
probe once (round 1)
toy loss 7.629
ISP, round 2
toy loss 6.799

Re-probing under round 1's allocation moved 7 of 12 layers (solid bars). It evens out neighbours that were both pushed low, because now each probe sees them compressed together. The price of a bit, the multiplier λ, settled at 1.335 toy-loss units.

Illustrative: the layer amplitudes and the neighbour term are invented. The ladder, the distortion law and the probe-then-allocate loop are the paper's.

At a 1.25-bit budget the toy shows the failure plainly. Probing once on the uncompressed model gives a worse allocation than uniform 1.25 bits, toy loss 7.63 against 6.98, because it happily puts adjacent layers at 0.7 and 1 bit. The second round probes under that allocation, sees those neighbours compressed together, evens them out, and lands at 6.80. At 2.5 bits one round is enough: costs there are still additive.

Probing is the expensive part: ten codecs and ten removal levels are 100 evaluations per layer, which the paper says adds up to hundreds of GPU hours per ISP iteration. Two shortcuts from Section 4 cut that down. Only the extreme codecs are probed, 0.75 and 4.0 bits, and everything between is interpolated through φ\varphi. And the cost of removing a fraction ff of a layer's experts, dℓ(f)=ln⁡PPLℓ(f)−ln⁡PPLℓ(0)d_\ell(f) = \ln \mathrm{PPL}_\ell(f) - \ln \mathrm{PPL}_\ell(0), is modelled as a power law with one exponent for the whole network and one amplitude per layer:

dℓ(f)=aℓf p,(p,{aℓ})=arg⁡min⁡p,{aℓ}∑ℓ∑f∈F(aℓf p−dℓ(f))2d_\ell(f) = a_\ell f^{\,p}, \qquad (p, \{a_\ell\}) = \arg\min_{p,\{a_\ell\}} \sum_{\ell} \sum_{f \in F} \big(a_\ell f^{\,p} - d_\ell(f)\big)^2

It is fitted on three removal levels (none, 25% and 95%) and recovers the allocation with a Spearman correlation of about 0.9. That leaves 2 × 2 probes per layer plus one baseline, where 16 codecs and 20 removal levels would need 320: the "80x less computational cost" is 320 / 4. The runs use two ISP rounds; for GLM-5.3-Flash and DeepSeek-V4.1 the budget is annealed from 4.5 bits to 2.25, then 2.

The text is not fully consistent here: one description probes removal at 5% and 95%, the formal one at 25% and 95%, and the lowest probed codec is 0.75 bits on a ladder that starts at 0.7. It is the texture of a draft, not a change of method.

Dynamic REAP: the part that runs

REAP (Lasby et al.) prunes MoE experts in one shot. For expert ee in layer ii it averages the gate-weighted output norm over the calibration tokens that route to it,

Si,eREAP=1∣Xi,e∣∑x∈Xi,egi,e(x) ∥fi,e(x)∥2S^{\text{REAP}}_{i,e} = \frac{1}{|\mathcal{X}_{i,e}|} \sum_{x \in \mathcal{X}_{i,e}} g_{i,e}(x)\, \big\lVert f_{i,e}(x) \big\rVert_2

keeps the KiK_i best, and deletes the rest. Altar-1 is what that looks like shipped: GLM-5.3 with 168 of its 256 routed experts kept. Routing is confined to the resident set RiR_i by masking the router logits before top-kk selection, z~i,e=−∞\tilde z_{i,e} = -\infty for e∉Rie \notin R_i.

Dynamic REAP keeps the mask and changes its membership:

  1. The router computes logits for every expert anyway, so each expert's unmasked routing probability pi,e(t)p_{i,e}^{(t)} is tracked whether it is resident or not. Non-resident experts collect evidence without being run.
  2. Two exponential averages smooth it, mi,e,h(t)=βhmi,e,h(t−1)+(1−βh) pi,e(t)m_{i,e,h}^{(t)} = \beta_h m_{i,e,h}^{(t-1)} + (1 - \beta_h)\, p_{i,e}^{(t)} with βh=2−1/h\beta_h = 2^{-1/h} and half-lives of 100 and 1,000 tokens. The rank is their mean.
  3. Every 256 routed tokens, non-residents in descending rank are paired with residents in ascending rank. If at least four pairs have an incoming score more than 5% above the outgoing one, the first four are exchanged. Otherwise nothing moves, "to prevent thrashing". Swapped experts sit out until the next checkpoint.
  4. Incoming experts are prepared asynchronously from CPU memory or NVMe and swapped in at a decode-token boundary when ready, so no token waits for a transfer and the resident budget never grows.
Two-panel schematic. Top panel, Offline: Iterative Sensitivity Probing. A START box labelled uncompressed model feeds a PROBE box (compress one layer at a time, measure the perplexity change for all layers i, codecs q and removal levels r, s_i(q, r) = L(i ← (q, r)) − L), which feeds a COMPRESS box (spend the byte budget where it buys the most quality; Lagrangian dual, perplexity per byte; minimize the summed sensitivities subject to the bytes staying within B). An arrow loops back from COMPRESS to PROBE, labelled repeat until the allocation stops changing. Bottom panel, Runtime: Dynamic REAP. A GPU lane shows a row of decode tokens with ticks at t, t+T, up to t+6T. From a CPU lane and a lower NVMe disk lane, dashed lines marked prepare expert start early and curved arrows land on later tokens, at t+3T from the CPU and t+5T from the disk, labelled instantaneous swap when ready.
The two halves of the method: offline, probe each layer under the current compression and reallocate the byte budget with a Lagrangian dual until the allocation settles; at runtime, prepare experts from CPU memory or NVMe at a fixed cadence and swap them in when ready (Dettmers, Figure 3).

The paper's evidence for the runtime half is a hit-rate grid on Qwen3.6-35B-A3B at 50% residency, 128 of 256 experts per layer. Calibrate the resident set on one domain, evaluate on another, and count the share of routing probability that lands on resident experts:

Two five-by-five heatmaps of hit rate in percent, rows the evaluation domain and columns the calibration domain, both ordered arXiv, Chinese, Code, Math, Wikipedia. Static REAP, left, is blue on the diagonal (arXiv 89.0, Chinese 84.3, Code 70.5, Math 69.2, Wikipedia 88.2) and mostly red elsewhere, down to 0.9 for Chinese text with a Code-calibrated set, 4.2 and 4.3 for Chinese with Math- and arXiv-calibrated sets. Dynamic REAP, right, is uniformly blue: 96.6, 96.2, 96.4, 96.5, 96.4 for arXiv; 96.0 to 96.6 for Chinese; 92.5 to 92.8 for Code; 94.2 to 94.6 for Math; 96.9 to 97.0 for Wikipedia.
Share of routing probability that lands on resident experts, Qwen3.6-35B-A3B at 128 of 256 experts per layer. A static set tracks its calibration corpus; the dynamic set holds 92.5-97.0% on every pair (Dettmers, Figure 5).

Static REAP is good on its own domain, 70.5% to 89.0% on the diagonal, and poor almost everywhere else: the Code-calibrated set catches 0.9% of the routing mass on Chinese text. Dynamic REAP holds 92.5% to 97.0% on all 25 pairs, and beats static REAP even where static was calibrated on the test domain. That last part is the interesting one. Routing drifts within a document, so the average routing over a dataset is a poor proxy even for that dataset.

Two limits. Hit rate is routing coverage, not quality; the paper reports no perplexity for static against dynamic REAP. And it reports no transfer volume or bandwidth for the swaps, which is the cost the design exists to hide. Four swaps per 256 tokens replace at most 3.1% of a 128-expert resident set per checkpoint; how many bytes that is depends on expert size, which the paper does not give.

The toy below implements the swap rule as written, on a synthetic 32-expert layer and a stream of four "domains":

static vs dynamic REAP · one toy layer, the paper's swap rule
calibrate on
domain A · calibrationdomain Bdomain Cdomain D0%25%50%75%100%ticks: checkpoints that swapped four experts (4 of 32 checkpoints)
hit rateABCDall
static94.2%28.9%78.2%35.9%59.3%
dynamic94.2%80.7%85.9%86.8%86.9%

The static set is right only on its own domain. The dynamic set starts from the same experts, and after each shift it climbs back in steps of four swaps per checkpoint, lagging by the time the running averages need to notice. More residency helps both; it helps static REAP only where its calibration domain overlaps the traffic.

Illustrative: synthetic routing, 32 experts, one layer. The swap rule is the paper's; the percentages are not.

With 16 of 32 experts resident and the static set calibrated on domain A, the toy's static hit rate falls from 94.2% on A to 28.9% on B. The dynamic set recovers to 80.7% on B and averages 86.9% against 59.3% for static, after only four swap events, because a few experts carry most of each domain's routing mass. The numbers are the toy's; the shape is the point.

The results, read off the vector plots

The paper reports results only as plots, so I digitized them: PyMuPDF returns each matplotlib marker as a filled path whose centre is the data point, and the gridlines calibrate the axes. The Unsloth footprints come back as 2.13, 2.32, 2.54, 2.95, 3.15 and 3.89 bits, a good sign the calibration is right.

Line chart of WikiText-2 perplexity, about 3 to 8.7, against bits per weight, 1.0 to 4.3, for four mixture-of-experts models. Solid bnb2 curves sit left of and below dotted Unsloth curves. Qwen 3.6-35B-A3B bnb2 falls from 8.67 at 1.0 bit to 6.87 at 3.89 bits; its Unsloth curve runs from 7.69 at 2.13 bits to 6.94 at 3.89. Qwen 3.8-Flash-Next bnb2 falls from 6.42 at 1.25 bits to 4.88 at 3.30; Unsloth from 5.62 at 2.72 to 4.90 at 3.30. GLM-5.3 Flash bnb2 falls steeply from 7.24 at 1.25 bits to 4.32 at 2.79; Unsloth from 6.34 at 2.39 to 4.79 at 2.79. DeepSeek-V4.1 bnb2 is lowest, 3.64 at 1.0 bit to 3.00 at 3.0 bits, with an uncompressed star at 4.33 bits and 2.88.
WikiText-2 perplexity against bits per weight, bnb2 (solid) against Unsloth dynamic GGUF (dotted); DeepSeek-V4.1 is compared only with its own uncompressed checkpoint (Dettmers, Figure 4).

Evaluation is WikiText-2 perplexity through llama.cpp's standard tool, in 512-token chunks with the last 256 scored. bnb2 is allocated to each Unsloth checkpoint's footprint, then run with dynamic REAP swapping four experts per layer every 256 tokens. For each Unsloth point there are two readings: bnb2's perplexity at the same bits, and the bits bnb2 needs to match Unsloth's perplexity, which is my linear interpolation along the bnb2 curve.

equal-perplexity gap · digitized from the paper's Figure 4
4.55.05.56.06.51.01.52.02.53.03.54.0bits per weight0.88 bitsbnb2Unsloth GGUF
Unsloth checkpoint:

Unsloth at 2.72 bits scores 5.62. At the same footprint bnb2 scores 4.98 (−11.4%). bnb2 reaches 5.62 at 1.84 bits: 0.88 bits fewer per weight.

Reported perplexities, digitized from the PDF's vector plot; the equal-quality crossing is my linear interpolation between plotted points. bnb2 bits count resident weights only.

ModelUnsloth bitsUnsloth pplbnb2 ppl, same bitsbnb2 bits, same pplGap
Qwen3.6-35B-A3B2.137.697.371.600.53
Qwen3.6-35B-A3B2.327.447.231.880.45
Qwen3.6-35B-A3B2.547.347.171.980.56
Qwen3.6-35B-A3B2.957.207.032.420.53
Qwen3.6-35B-A3B3.157.156.942.590.56
Qwen3.6-35B-A3B3.896.946.873.200.69
Qwen3.8-Flash-Next2.725.624.981.840.88
Qwen3.8-Flash-Next2.845.344.942.110.73
Qwen3.8-Flash-Next3.115.134.912.430.68
Qwen3.8-Flash-Next3.304.904.883.190.11
GLM-5.3-Flash2.396.344.901.510.88
GLM-5.3-Flash2.505.774.821.750.76
GLM-5.3-Flash2.615.114.602.180.43
GLM-5.3-Flash2.794.794.322.520.27

Perplexities are the paper's, digitized; the last two columns are my interpolation.

The abstract says bnb2 lands "about 0.4 to 1 bit per weight below Unsloth". On the plotted points the gap is 0.11 to 0.88 bits: widest at the low end of each Unsloth ladder, narrowest where the curves converge. No point reaches a full bit. The paper's worked examples round in its favour: it pairs Unsloth's Qwen 3.8 Flash Next at 2.7 bits with bnb2 at 1.7, where I get 1.84, and Qwen 3.6 at 2.2 with 1.5, where I get 1.60. Its GLM example, 2.8 against 2.5, I reproduce at 2.52, and that is a 0.3-bit gap, under the abstract's own floor.

At matched footprints the picture is stronger. At 2.39 bits GLM-5.3-Flash scores 4.90 under bnb2 and 6.34 under Unsloth, 22.7% lower. GLM is also the paper's stated failure: its bnb2 curve climbs steeply below 2 bits, to 7.24 at 1.25, and the limitations section says dynamic compression of it "was not very successful". The paper also suggests that models with large "engram" tables, 51B parameters in Qwen 3.8 Flash Next (its N-gram embeddings) and 196B in DeepSeek-V4.1, degrade less when heavily quantized, in line with the precision scaling laws. The plot supports that less than the sentence does. From about 2 bits down to 1.25, DeepSeek-V4.1's perplexity rises 9%, level with the engram-free Qwen 3.6 at 10%, and Qwen 3.8 Flash Next rises 22%.

DeepSeek-V4.1 has no Unsloth curve, only its own uncompressed point at 4.33 bits per weight and perplexity 2.88. At 4.33 bits it is not a 16-bit reference; presumably it is the checkpoint as released, which is already low precision. Against it, bnb2 is +26.3% perplexity at 1 bit, +15.6% at 1.5, +9.3% at 2 and +4.3% at 3. Whether 9% more perplexity at 2 bits counts as "high quality" depends on the workload, and WikiText-2 cannot say.

Figure 1 plots the same points against gigabytes, with device lines. Dividing each point's size by its bits per weight gives one constant per model, 34.7B, 128.8B, 312B and 566B weights' worth, close to the 35B, 125B, 320B and 552B the text names. So the size axis is weights and nothing else.

Log-scale chart of WikiText-2 perplexity, 3 to 11, against model size in gigabytes, about 4 to 310. Dashed vertical device lines mark 6 GB phone, 10 GB MacBook, 24 GB RTX 3090/4090, 32 GB RTX 5090, 96 GB RTX PRO 6000 and 128 GB DGX Spark/Strix/MacBook. Qwen 3.6-35B-A3B bnb2 spans about 4 to 17 GB, perplexity 8.7 to 6.9, just below its Unsloth points from 9 to 17 GB. Qwen 3.8-Flash-Next bnb2 spans about 20 to 53 GB, perplexity 6.4 to 4.9, with its second point on the 24 GB line; its Unsloth points start at 44 GB. GLM-5.3 Flash bnb2 spans about 49 to 109 GB, perplexity 7.2 to 4.3, against Unsloth points from 93 to 109 GB. DeepSeek-V4.1 bnb2 spans about 71 to 212 GB, perplexity 3.6 to 3.0, and the uncompressed DeepSeek-V4.1 star sits near 307 GB at 2.9.
The same results against resident weight size, with common memory sizes drawn in. The size axis counts weights only: no KV cache, activations or runtime (Dettmers, Figure 1).

Read that way, the headline pairings are:

What the paper does not measure

What you can run today

The announcement is explicit that bnb2 is a private beta with limited places. Here is what is public:

So today you can install kernels on Python 3.11 and quantize a matrix with VQ-L2. You cannot run the paper's system: no ISP, no dynamic REAP runtime, no compressed models. I read the wheel's source and did not run it; it is a dev build a month older than the paper, and the beta may differ.

Where it sits

Offloaders such as FreeToken, which the paper cites, keep every expert and fetch the missing ones per token; Edge0 predicts them a token early to hide the read. REAP pays once, in quality, and never fetches. Dynamic REAP sits between: approximate, but fetching on a 256-token clock that keeps transfers off the critical path. And where scalar formats like NVFP4 win on hardware support, vector codes win on rate granularity, which is what a sub-2-bit allocator needs.

The takeaway

The contribution is not a better way to write a weight in 1.5 bits. It is two ideas about where the bytes go. Measure each layer's cost under the compression you actually plan to ship, because below 2 bits costs stop adding up. And stop treating expert removal as a decision made once from a calibration set: the router already scores every expert on every token, so keep the ones the current text wants resident and fetch the rest in the background. The evidence for both is thinner than the headline, one metric and one baseline, but the second idea has a clean receipt in Figure 5, and neither one depends on the codec.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "Runtime dynamic compression: 1.5 bits per weight, counting only the experts in memory", ai.thesatyajit.com, September 2026.

bibtex
@misc{ghana2026runtimedynamiccompression,
  author = {Satyajit Ghana},
  title  = {Runtime dynamic compression: 1.5 bits per weight, counting only the experts in memory},
  url    = {https://ai.thesatyajit.com/articles/runtime-dynamic-compression},
  year   = {2026}
}
share