Gunnar

nordisk/znippy-zoomies

znippy-zoomies

znippy-zoomies

Single-purpose parallel Rust primitives built on one all-core engine, not rayon's fork-join model.

Decompressor benchmarks

Each of our parallel decoders against the standard tools for its own format, grouped by format — every one of ours vs every one of theirs, in two modes. One deterministic 1 GB corpus, min-of-3. Columns are hand-picked (MB/s · cores · wall s · peak RSS). A 🏆 marks a decoder that beat a long-reigning record — see records.toml.

Multi core variants — all cores

gzip

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-gz576010.240.18416.60
pigz589.321.271.742.50

bzip2

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-bz2744.3911.621.3880.60
lbzip2759.5511.761.3590.60

zip

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-zip28063.100.36119.50
unzip294.170.993.483.10

jar

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-jar28143.190.36119.20
jar372.870.992.751.80

Single core variants — one core each

gzip

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-gz_st655.620.991.56239.80
gzip684.250.981.501.90
pigz589.321.271.742.50

bzip2

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-bz2_st129.291.017.9275.60
bzip260.02117.065.10
lbzip2_st70.64114.507.30
pbzip260.29116.9841.80

zip

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-zip_st13540.980.7612.10
unzip294.170.993.483.10

jar

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-jar_st13560.980.7612.20
jar372.870.992.751.80

cores_busy = avg CPU cores busy during decode (12 = full saturation). znippy-* run all cores; _st rows are pinned to one worker. 🏆 = beat a long-reigning record (records.toml).

Latest run — 32 cores, 2026-07-23: the harness refused to publish it

Not benched. This repo contributed no numbers to the latest run.

nornir-bench entrypointexamples/nornir-bench.rs, osm-katana/examples/nornir-bench.rs
criterion [[bench]] targets15 (Cargo.toml ×5, lgz/Cargo.toml ×1, linflate/Cargo.toml ×2, ljar/Cargo.toml ×2, lzip-parallel/Cargo.toml ×1, osm-katana/Cargo.toml ×4)
host-stamped resultnone

An entrypoint exists but produced no run — it was not part of the latest pass, or it exited without emitting a BenchRun.

The published tables above are untouched by this run, and that is deliberate: a preview may never overwrite a measured, published table. nornir docs render hides a preview run from every marker that has not opted in with preview=yes, so the blocks below are the only ones it can reach.

Nothing here failed. The decoders did exactly what they were asked and the roundtrip tests passed — but the OSM-XML input was 24 MB against the 1024 MB heavy floor, so the harness declined to emit a publishable BenchRun for the whole run rather than let an under-floor pass become a number somebody quotes. Read every row below as a preview: honest measurements of a workload too small to publish.

gzip — preview

v0.1.20 · PREVIEW · AMD Ryzen Threadripper PRO 3975WX 32-Cores · 499 GiB · corpus 1024 MB · osm-katana input 24 MB · min-of-3 · 32 cores · 2026-07-23

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-gz1280013.870.08733.50
pigz459.191.232.232.10
gzip538.9511.901.70

bzip2 — preview

v0.1.20 · PREVIEW · AMD Ryzen Threadripper PRO 3975WX 32-Cores · 499 GiB · corpus 1024 MB · osm-katana input 24 MB · min-of-3 · 32 cores · 2026-07-23

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-bz2116426.600.88188.90
lbzip2129629.370.79259.90
bzip246.11122.214.80
pbzip246.21122.1685.60

one core each — preview

v0.1.20 · PREVIEW · AMD Ryzen Threadripper PRO 3975WX 32-Cores · 499 GiB · corpus 1024 MB · osm-katana input 24 MB · min-of-3 · 32 cores · 2026-07-23

workloaddecompress_mbscores_busydecompress_speak_mem_mb
znippy-gz_st131310.78239.30
gzip538.9511.901.70
znippy-bz2_st56.33118.1874.40
bzip246.11122.214.80
lbzip2_st54.91118.656.90
pbzip246.21122.1685.60

OSM convert — preview, and the arm that put the whole run under the floor

v0.1.20 · PREVIEW · AMD Ryzen Threadripper PRO 3975WX 32-Cores · 499 GiB · corpus 1024 MB · osm-katana input 24 MB · min-of-3 · 32 cores · 2026-07-23

workloadconvert_mbscores_busyconvert_speak_mem_mb
osm-katana2003.170.12301.70
osm-katana_st72.730.970.33262.90
osmium68.571.310.3535.40

No zip / jar rows: those arms did not run in this pass. The host string above is the harness's own — it composes machine from the CPU/RAM/corpus it measured and ignores NORNIR_MACHINE, so this run reads as its Threadripper rather than as oden. To promote any of this to a published table: NORNIR_ZOOMIES_CORPUS_MB=1024 and an osm-katana input ≥ 1024 MB (or NORNIR_OSM_KATANA_CLIP=<region> against a planet file).

Compressor benchmarks

Everything above is decode. ldeflate (published 2026-08-03) is the workspace's one write-side crate — the parallel DEFLATE/zlib engine plus the gzip, ZIP and JAR deflators built on it. It gets its own section rather than a column above, because a compressor cannot be raced the way a decoder can: the bake-off reconstructs one fixed archive, while gzip -6 and this crate emit different bytes, so MB/s alone ranks nothing. The contested number is what the schedule costs against the same crate's serial path over the same objects at the same level — so the baseline row is compress_objects_serial, not gzip.

plan::compress_objects is the only scheduler in the crate. gzip, ZIP and JAR route through it and differ only in framing — Container::wrap puts a header and trailer around a body the planner already produced, and the three containers added no scheduling code at all. The planner picks a unit kind per object by size: Unit::Batch below coalesce_below = 64 KiB (one task drains many, reusing one encoder), Unit::Whole, or Unit::Segment above 4 MiB (cut at Z_FULL_FLUSH, so one big blob cannot serialise a worker). Unit::Batch covers 93.8% of real git objects — measured over 48 917 of them — which is why one Config serves both git and LFS. limits.rs refuses an amplifying request past MAX_PIECES = 262 144 rather than attempting it.

Scaling — the same crate against its own serial path

oden · AMD Ryzen Threadripper PRO 3975WX 32-Cores · 499 GiB · 30 770 real git objects, 715.4 MB, median 986 B · level 6 · min-of-3 · load 40–47 · 2026-08-04

workloadmb_per_seccores_busyspeedup_vs_serialspeedup ÷ cores held
deflate.repack_git_objects_st72.5—1.00—
deflate.repack_git_objects_mt169.20.930.951.02
deflate.repack_git_objects_mt2137.11.841.891.03
deflate.repack_git_objects_mt4258.53.553.571.01
deflate.repack_git_objects_mt8424.45.865.861.00
deflate.repack_git_objects_mt16621.18.488.571.01
deflate.repack_git_objects_mt842.712.1511.630.96

Read the last column. Above 8 threads the speedup_vs_serial column measures the box, not the code — oden is shared and this crate takes every core it can get, so a run that asks for 32 threads and is given 12.15 cores returns 11.63×. Across a 7-run batch on 2026-08-03 the 32-thread speedup spanned 10.38×–23.20× (123.5% spread) while the CPU cost of the schedule held to 1.025×–1.054× of serial, and efficiency against cores actually held stayed in a 94.9–97.6% band at every occupancy. An earlier headline of 28.30× / 88% / 2266 MB/s is not refuted — carried forward it predicts 29.2–31.2× at that occupancy — but it was a statement about machine occupancy dressed as one about the crate, it cannot be reproduced on a shared box, and it is retired as a crate claim. Full tables, the negative controls and both falsified hypotheses: benchmarks.md.

Never compress what is already compressed

precompressed.rs gates the entropy coder on hint → extension → magic-byte probe — no entropy estimate, no tunable threshold, at most 16 byte compares. Acting on a store verdict is free because stored_deflate emits RFC 1951 §3.2.4 stored blocks: a length-prefixed memcpy, not flate2 at level 0, which would still build, reset and walk the encoder.

oden · 24 real git packfiles + pack indexes · 113.8 MiB · level 6 · min-of-3 · decided on CPU seconds · load 66 · 2026-08-04

probecpu-s per GiBwall MB/sratio
off — deflate everything22.85184.81.0041
on — store the already-compressed0.99724.60.9999

→ 23.1× less CPU, 95.7% saved, output +0.41% larger.

Decided on CPU seconds, not wall time: at load 66 the wall column is noise (the same arm read 267.5 / 2075.5 MB/s on a quieter box) while cpu-s per GiB moved under 2%. Quote the saving, not the multiplier — across four runs on 2026-08-03/04 the multiplier wandered 19.4×–23.1× because its denominator is ~1 cpu-s/GiB, while the saving held at 94.8–95.7% and the size penalty came out byte-identical (+0.41%) every time. It independently reproduces znippy's own 92–95% figure. Negative controls, same harness: with the probe given nothing to find (git loose objects) it collapses to 1.03×, and with the probe vetoed by ContentHint::Compressible over the same packfiles to 1.04× at byte-identical output — so the 23× is a property of the probe.

OSM converter

osm-katana (in-repo sub-crate) converts OSM-XML/PBF → GeoParquet all-core on a no-barrier gatling pipeline, head-to-head with the legacy reference tools osmium / osmconvert for the same input. One convert pass, min-of-3. Columns: MB/s, cores busy, wall seconds, peak RSS (MB).

v0.1.19 · AMD Ryzen 9 7900 12-Core Processor · 61 GiB · corpus 1024 MB · min-of-3 · 12 cores · 2026-07-13

workloadconvert_mbscores_busyconvert_speak_mem_mb
osm-katana115.401.350.21307.70
osmium126.201.310.1936.60
osmconvert136.700.910.18267.20

convert_mbs = input MB / wall; cores_busy = average CPU cores busy (12 = full saturation on this box). osm-katana runs all cores; osmium / osmconvert are the single-stream C++ references, benched only when installed. By default a small synthetic OSM-XML corpus exercises the emit path on any box — the HEADLINE number comes from the off-box planet run (NORNIR_OSM_KATANA_INPUT=<planet.osm> nornir bench run znippy-zoomies). Region clip headline (cut Sweden out of the planet with our tool): add NORNIR_OSM_KATANA_CLIP=10.9,55.3,24.2,69.1.

Staged pipeline

The classic planet chain — each stage eats the previous stage's output file: xml2-pbf (OSM XML/bz2 → PBF) → pbf2-geo (PBF → GeoParquet) → geo2-arrow (GeoParquet → Arrow IPC). NORNIR_OSM_KATANA_CHAIN=1 nornir bench run znippy-zoomies warehouses one row per stage (stage_mbs = that stage's input MB / wall); point NORNIR_OSM_KATANA_INPUT at a planet for the heavy chain. Off by default — it adds a full three-stage pass — so the table lands after that run.

Docs

License

MIT. The stree module retains Ragnar Groot Koerkamp's copyright notice (see LICENSE).

Files

.cargo/
.githooks/
.nornir/
benches/
docs/
examples/
gatling/
lbzip2/
ldeflate/
lgz/
linflate/
ljar/
lzip-parallel/
osm-katana/
src/
tests/
xtask/
znippy-zip-core/
znippy-zoomies-build-thing/
zstd-sys-rs/
.gitattributes
.gitignore
Cargo.lock
Cargo.toml
LICENSE
README.md
brokkr.toml
rustfmt.toml

The list is cut short here. Clone the repository for every file.

Newest commits

32a931b455fcMerge fix/small-4-2026-10-25 into main: no file addresses our repositories on the old forge (A28 N7-left)2026-10-25
a69561829e2aGREEN: the READMEs and design notes link our real homes (A28 N7-left; guard 4/0; docs only)2026-10-25
2de62a3b64d4TEST: no file addresses our repositories on the old forge (A28 N7-left; RED: 34 lines in READMEs and design notes)2026-10-25
20212149c73dMerge fix/stale-detached-locks-2026-10-25 into main: follow the stale detached locks to the sibling mains2026-10-25
65a8a886cb58Follow the stale detached xtask/Cargo.lock to the sibling mains2026-10-25