Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

softfp8

kernels

Your A100 has FP8. Your T4 doesn't. Neither does your 2080 Ti, your 3060, or the P40 collecting dust in your homelab. softfp8 is a Triton GEMV for batch-1 decoding that stores weights as packed E5M2 bytes and turns them back into fp32 inside registers, inside the K-loop — the memory bus never sees an fp16 copy of your weights, so decode gets ~2× faster on hardware that can't do FP8 at all. Software FP8, as in: the emulation is the feature.

K-loop decode animation

(one int32 load per four weights; bytes peel off into FMAs — nothing ever materializes as an expanded fp16 matrix)

What's actually going on

Decode is not a compute problem. At batch 1 your arithmetic intensity is ~2 FLOP per byte and even a 3060's ridge point is two orders of magnitude away. So the whole game is bytes moved:

weight format bytes streamed per token (7B) predicted tok/s, RTX 3090 @ 65% BW
fp16 14.0 GB 43.5
E5M2 (this repo) 7.0 GB 86.9
theoretical 4-bit 3.5 GB 173.9

E5M2 is the format with the cheapest software decode: its exponent field lines up with fp16's after a shift, so a 0x4b0000__ trick plus exp2 converts a packed byte to a float in a couple of integer ops — no conversion tables, no ldgsts, nothing SM-version-specific. Four codes ride in every int32, so one 128-bit load feeds four FMAs.

byte layout

Does it compute the right answer?

The kernel decode is bit-exact against torch.float8_e5m2: every code, sign / exponent / mantissa reconstructed by integer bit-twiddling, checked against PyTorch's hardware cast in test_cast_roundtrip_exact. The fused GEMV then matches the dequant-then-matmul reference to 1.6e-6 max abs error, plotted across 3,840 outputs so it isn't a cherry-picked shape:

parity scatter

Bandwidth-model check — the speedup claim as a graph, per GPU, not a table you have to trust:

bandwidth

Positioning (who else does this?)

what it needs what it trades
TensorRT-Model-Optimizer / TorchAO FP8 Hopper/Ada FP8 units free lunch, but only on new cards
bitsandbytes NF4/INT8 own kernels, fp16 compute good, but 8-bit is the floor
softfp8 any GPU Triton compiles for E5M2's 2-bit mantissa: 4.9% output rel-err
QLoRA NF4 4-bit, block scales requires dequant ladder; fp8-exact

The niche: you already own an old GPU, you decode at batch 1, and your quality loss at 8 bits is already under your noise floor — so why is your memory traffic still fp16-shaped?

Layout

softfp8/
├── softfp8/core.py             # pack/decode/quantize + the Triton kernel
├── tests/test_core.py          # 6: bit-exactness vs torch cast, parity, ratio algebra
├── demos/demo.py               # writes receipts/receipt.json
└── demos/make_figures.py       # regenerates every figure above from live computation

Reproduce

pip install -e ".[dev]"
pytest tests/ -q && python demos/demo.py && python demos/make_figures.py

CI badge: see the shields above — same commands, GitHub-hosted.

Honest limits

  • GEMV only (M=1). The packed-decode idea ports to tiled GEMM but the win shrinks as M grows — it's a bandwidth trick, and bandwidth only dominates at small M. Say so in the paper and in the code.
  • 4.9% output rel-err is E5M2, not this kernel: any fp8 weight format pays that. Activation precision is untouched (fp32 in this path).
  • Predicted tok/s is bytes ÷ measured-bandwidth × 0.65, with 0.65 from public copy-kernel literature. Run gembench-style measurement on your card for the real coefficient.

About

Software-FP8 (E5M2) weight-only GEMV: 4 codes per int32, decoded in-register inside the K-loop — 2x decode bandwidth on GPUs without FP8 hardware

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages