feat: fpz compressed pack format with batched GPU decode - #32
feat: fpz compressed pack format with batched GPU decode#32AlperenKonukbay wants to merge 8 commits into
Conversation
The fpz format: bf16 macroblocks stored as split byte-plane zstd (low/ mantissa plane raw — measured incompressible — high/sign-exponent plane compressed), 1.401x smaller on the real 38GB LTX transformer pack, byte-exact on read. v2 stores the high plane as many independent 16B- aligned zstd chunks — the shape a GPU decoder needs. Read paths: - CPU decode (thread-pooled, GIL-released zstd) for any device target. - Batched GPU decode (FLASHPACK_FPZ_GPU_DECODE=1 [+ FLASHPACK_FPZ_GPU_LL=1 for the batched C path]): ctypes bindings to libnvcomp's batched Zstd decompressor (flashpack/_nvcomp_ll.py; the pybind nvcomp wrapper never exposes the batched API and its per-chunk Python objects are a measured 7x floor). Per batch: one pinned int64 chunk table, one H2D, two on-device base-address adds, one foreign call. Measured: 38GB restored in 1.67-1.85s page-hot (H200 AND B200, byte parity everywhere), decode fully hidden under NVMe reads when cold. - fpz_gpu_warmup(): pays nvcomp's one-time CUDA kernel-load off the hot path (26.7s worst-case observed lazy -> ~1s residual with warmup+EAGER). - Optional fused interleave Triton kernel (default off; ~2% at best — HBM absorbs the strided writes — kept as dormant infrastructure). Streaming encoder writes packs without an uncompressed scratch file (38GB repack: 50min -> ~3.5min). Everything is opt-in via pack_to_file(compress="fpz-bf16"); plain packs are byte-for-byte unaffected. Built on the affinity-clamp + distributed-hardening branch; the combined tree is byte-identical to the gated speed/fpz-v1 head (f2f1bf0), on which every receipt in the PR body was measured. Full development history: https://github.com/fal-ai/flashpack/tree/speed/fpz-v1 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review cleanup — no functional additions: - The batched libnvcomp path is now THE GPU decode; the pybind-wrapper path (7x slower, only ever a comparison baseline) is removed along with its per-chunk Array objects, DecompressConfig cache, torch-allocator adapter and the FLASHPACK_FPZ_GPU_LL selection gate. Path selection is automatic: FLASHPACK_FPZ_GPU_DECODE=1 uses the batched decoder when libnvcomp is present, the pack is v2 and its chunk alignment satisfies the decompressor; otherwise it falls back to the threaded CPU decode with a warn-once explaining why. - The optional fused-interleave Triton kernel is removed (measured ~2%; the strided copies run at HBM speed). - Per-thread timing trace now goes through logging.debug (was print) with compacted buckets; FLASHPACK_FPZ_GPU_TORCH_ALLOC and the now-dead _env_flag_default helper are gone. - Tests updated accordingly: wrapper/allocator tests replaced by coverage of the new automatic fallback; docstrings rewritten as contracts. - fpz-gpu extra now depends only on nvidia-libnvcomp-cu12 (the pybind wheel is no longer used). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… policy as the O_DIRECT reader tests
The assertions carry the contract; the capsys-disabled prints were bench instrumentation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Review by fal-9000 (GPT-5.6 Sol, xhigh) — reviewed at head Two blockers:
|
Pre-merge regression gate (same-boot interleaved A/B on a 38 GB pack): the shipped 64 KiB default -- chosen to match nvcomp's default chunk size -- decodes 2.6x slower than 1 MiB on the threaded CPU path (39.5 s vs 15.3 s hot; per-chunk overhead x 512 chunks per frame) for a compression-ratio difference under 1% (1.412x vs 1.401x). Every GPU batched-decode receipt (1.67-1.85 s per 38 GB) was earned on 1 MiB packs, so the default now matches the receipted configuration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stacked on #31; once it merges this diff is fpz-only.
Format
pack_to_file(..., compress="fpz-bf16")stores bf16 macroblocks as split byte-planes: the mantissa plane raw (incompressible in practice), the sign/exponent plane zstd-compressed. 1.401x smaller on a real 38 GB transformer pack, byte-exact on read. The v2 layout splits the compressed plane into independent 16-byte-aligned chunks — the shape a batched GPU decoder needs. Opt-in; plain packs are unaffected. The encoder streams, so a 38 GB repack needs no uncompressed scratch file (50 min → ~3.5 min).Read paths
FLASHPACK_FPZ_GPU_DECODE=1): ctypes bindings to libnvcomp's batched Zstd decompressor (_nvcomp_ll.py). The pybind nvcomp wrapper doesn't expose the batched C API and its per-chunk Python objects cost ~7x, so the batched path is the only GPU path shipped. Per batch: one pinned chunk table, one H2D copy, one foreign call — Python overhead independent of chunk count. Selection is automatic, with warn-once CPU fallback when libnvcomp is missing, the pack is v1, or its alignment doesn't satisfy the decompressor.fpz_gpu_warmup(): pays nvcomp's one-time CUDA module load off the hot path (worst observed 26.7 s lazy → ~1 s).Measurements (38 GB pack, byte parity verified on every run)
Stable across 5+ boots on 2 GPU classes (B200 is a single boot).
The default chunk size is now 1 MiB, matching the receipted configuration. It originally shipped at 64 KiB (nvcomp's default); a pre-merge regression gate (same-boot interleaved A/B, 38 GB pack) measured 64 KiB decoding 2.6x slower than 1 MiB on the threaded CPU path (39.5 vs 15.3 s hot — the path every consumer without the GPU extra gets) and 11% slower on the batched GPU path, for a compression-ratio difference under 1% (1.412x vs 1.401x). The same gate also live-verified the warn-once CPU fallback: with
FLASHPACK_FPZ_GPU_DECODE=1and libnvcomp absent, every read fell back cleanly with the install hint, parity intact. Trade-off: after an explicit page-cache drop, the fpz buffered refill can be slower than the raw path's O_DIRECT read on some nodes; the wire-cold tier still favors fpz on bytes moved. An O_DIRECT option for fpz reads is a natural follow-up.nvidia-libnvcomp-cu12lives in a separatefpz-gpuextra (NVIDIA-proprietary; kept out of default deps pending license review). 291 tests green.