MXFP4/community docs · rev 2026.09

Home/Guides/Choosing Block Size

Guide · 15 minutes

Choosing Block Size

One scale per k elements. The overhead is exact arithmetic; the benefit depends entirely on how your outliers are distributed.

Abstract repeating grid of memory package tops on a dark board
Ordered rows of memory packages.

The arithmetic

Block size is a straight trade between scale overhead and outlier containment. The overhead is exact and easy to compute: one 8-bit scale per k elements adds 8/k bits per element.

Storage cost by block size, elements at 4 bits plus one 8-bit E8M0 scale per block. Exact arithmetic, not a benchmark.
block size kbits/elementvs bf16scales per 4096-row
85.003.20x512
164.503.56x256
324.253.76x128
644.1253.88x64
1284.063.94x32

Above 32 the returns are tiny — going from 32 to 128 saves 0.19 bits per element, about 4% of the total, while quadrupling the number of neighbours an outlier can damage. Below 32 the cost climbs quickly and, more importantly, you usually leave the hardware-native path.

Why 32 is the default

32 sits at the knee of the overhead curve and matches the block size wired into native block-scaled matrix units. Choosing anything else is choosing to run an emulated kernel. That may still be the right call, but it should be a deliberate one.

The containment argument

The reason to consider a smaller block is entirely about outliers. One large value forces the shared exponent up and coarsens the grid for everything else in its block. With k = 32 that is 31 damaged neighbours; with k = 16 it is 15.

Whether that matters depends on how the outliers are distributed. If they are sparse and randomly placed, halving the block size roughly halves the number of affected elements. If they are systematic — a whole channel that is consistently large — halving the block size does almost nothing, because the outlier is present in every block along that channel anyway. Diagnose which case you are in before paying for smaller blocks.

python tools/outlier_profile.py --layer layers.31.self_attn.o_projblocks with amax/median > 8 : 4.1% (k=32)blocks with amax/median > 8 : 4.0% (k=16)verdict: outliers are channel-systematic, not sparse smaller blocks will not help much here - consider rotation

Rules of thumb

  • Start at 32. It is the default for good reasons and it is the only size with a reliable native path.
  • Try 16 on specific layers that your sensitivity ranking flagged, not globally. Mixed block sizes across a model are usually fine as long as the runtime supports them.
  • Do not go above 64. The remaining storage saving is negligible and the outlier exposure grows linearly.
  • If 16 helps a lot, the real problem is outlier structure. A rotation or a mixed-precision exclusion will probably help more, and cost less.
  • Check the axis before the size. A wrong reduction axis looks exactly like a block size problem and is far more common.
Corrections

Found an error, or a result that disagrees? This is a community reference. Corrections with a reproducible test case are the most useful thing you can send us.

Open a correction · Community