Is multithreaded LZ4 actually competitive?
Benchmarking the opt-in --mt/-T# LZ4 build against reference parallel
compressors — pigz, pzstd and multithreaded zstd — at equal thread counts.
Earlier work showed the multithreaded LZ4 benchmark mode runs 13–16× faster than single-threaded LZ4. That alone doesn't prove much: any tool looks good against its own single-threaded self. The question answered here is whether those speeds hold up against other parallel compressors given the same hardware and the same number of threads.
Setup
| Item | Detail |
|---|---|
| Machine | 2× Intel Xeon @ 2.80 GHz — 16 physical cores, 32 hardware threads, 125 GB RAM |
| LZ4 | v1.10.0 dev + opt-in MT bench mode, PGO/LTO/native build (~/LZ4-ks/lz4), level -1 |
| References | pigz 2.8 (parallel gzip), pzstd 1.5.5 (parallel zstd frontend), zstd 1.5.5 (native -T#), all at their fastest level -1 |
| Corpora | silesia.tar (212 MB), enwik8 (100 MB); for wall-clock runs each was concatenated ×8 (1.7 GB / 800 MB) so the fastest runs still take hundreds of milliseconds |
| Method | All I/O in tmpfs (/dev/shm), output to /dev/null, best of 3 runs, idle machine; every tool's output decompressed and md5-verified against the original (8/8 checks passed) |
1 · In-memory benchmark: LZ4 -b1 -T# vs zstd -b1 -T#
Both tools ship an official in-memory benchmark mode that removes all file I/O — the cleanest apples-to-apples comparison of the compression engines themselves. (pigz and pzstd have no such mode, so they appear only in the file test below.)
Compression throughput, silesia.tar (MB/s)
| Threads | LZ4 MT | zstd MT | LZ4 advantage | LZ4 scaling | zstd scaling |
|---|---|---|---|---|---|
| 1 | 555.5 | 348.6 | 1.59× | 1.0× | 1.0× |
| 8 | 3,478.2 | 2,111.3 | 1.65× | 6.3× | 6.1× |
| 16 | 5,566.6 | 2,569.6 | 2.17× | 10.0× | 7.4× |
| 32 | 7,154.5 | 3,106.9 | 2.30× | 12.9× | 8.9× |
In-memory compression, silesia.tar, level -1.
Compression throughput, enwik8 (MB/s)
| Threads | LZ4 MT | zstd MT | LZ4 advantage | LZ4 scaling | zstd scaling |
|---|---|---|---|---|---|
| 1 | 408.5 | 261.9 | 1.56× | 1.0× | 1.0× |
| 8 | 3,245.3 | 1,425.5 | 2.28× | 7.9× | 5.4× |
| 16 | 3,501.7 | 1,945.0 | 1.80× | 8.6× | 7.4× |
| 32 | 6,680.3 | 2,331.9 | 2.86× | 16.4× | 8.9× |
Decompression
LZ4's MT benchmark decodes independent blocks in parallel: 18.0–33.6 GB/s on
silesia (T8→T32) and up to 36.7 GB/s on enwik8. The zstd format cannot be decoded in
parallel from a standard stream, so zstd's bench holds at ~0.9–1.1 GB/s regardless of
-T. LZ4-MT decode is therefore ~30× faster than zstd's at 32 threads — though
the fairer parallel-decode rival is pzstd, covered next.
2 · File-based wall clock: all four CLIs, real files
Real end-to-end runs — read a tmpfs file, compress (or decompress) to /dev/null —
measure each tool's whole pipeline, not just its engine. Throughput is uncompressed bytes
per second of wall time, best of 3.
Compression, silesia ×8 (1.7 GB), MB/s
| Threads | LZ4 -T# | pigz | pzstd | zstd -T# |
|---|---|---|---|---|
| 1 | 427 | 90 | 345 | 333 |
| 8 | 2,034 | 695 | 2,540 | 2,401 |
| 16 | 2,024 | 1,351 | 2,989 | 2,936 |
| 32 | 1,955 | 1,314 | 3,369 | 3,266 |
File-mode compression, best configuration per tool, silesia×8.
Decompression, silesia ×8 (1.7 GB), MB/s
| Threads | LZ4 CLI | pigz | pzstd | zstd |
|---|---|---|---|---|
| 1 | 1,724 | 280 | 1,081 | 1,024 |
| 8 | 1,727 | 282 | 6,917 | 1,011 |
| 16 | 1,725 | 287 | 8,025 | 1,023 |
| 32 | 1,731 | 287 | 7,856 | 1,019 |
enwik8×8 shows the same pattern: LZ4 wins single-threaded compression (332 vs 264/248/81 MB/s) and single-threaded decode; pzstd wins multi-threaded file compression (2,897 MB/s at p32 vs LZ4's 2,000 plateau) and parallel file decode (6,730 MB/s).
Compression ratio (unchanged by threading)
| Corpus | LZ4 -1 | pigz -1 | pzstd -1 | zstd -1 |
|---|---|---|---|---|
| silesia | 2.10 | 2.75 | 2.88 | 2.89 |
| enwik8 | 1.75 | 2.37 | 2.46 | 2.46 |
LZ4 trades ratio for speed by design — that trade-off is identical in the single-threaded version and is not an artifact of the MT work.
Verdict
Yes — the 13–16× MT speedups are competitive, not an artifact of a weak baseline.
- Scaling quality: on this 16-core box, MT LZ4 scales 12.9–16.4× at 32 threads, better than zstd's own MT bench (8.9×) and pzstd (9.8×), and on par with pigz's near-linear 15× (which starts from a 6× slower baseline). Nobody exceeds ~16× here — physical cores plus hyperthreading set that ceiling.
- Absolute engine speed: at every equal thread count, in-memory MT LZ4 compresses 1.6–2.9× faster than MT zstd, and its parallel decode (18–37 GB/s) is far beyond anything the gzip or zstd formats can do.
- Sanity: the numbers agree with the earlier measurements (7.9 GB/s at auto threads ≈ the 7.15 GB/s at fixed T32 here), and every output was decompressed and checksum-verified.
Honest caveats
- The file CLI is not as fast as the bench harness. LZ4's
-T#file-mode compression plateaus at ~2 GB/s beyond 8 threads because its read→dispatch→write pipeline is single-threaded, so pzstd/zstd (~3.3 GB/s) win the file-CLI race at 16–32 threads, and pzstd's parallel file decode (~8 GB/s) beats LZ4's single-threaded CLI decode (~1.7 GB/s). The 13–16× figures describe the in-memory benchmark mode (pure engine throughput), not the file CLI. - Ratio differs: gzip/zstd at level -1 compress silesia ~2.9× vs LZ4's 2.1×. If output size matters more than speed, pzstd is the better tool; if raw speed matters, LZ4 wins.
- Thread counts above 16 mostly buy hyperthreading gains; all tools flatten past the 16 physical cores.
Reproduce
# in-memory engine comparison (equal threads)
~/LZ4-ks/lz4 -b1 -i3 -T16 ~/LZ4-ks-bench/silesia.tar
zstd -b1 -i3 -T16 ~/LZ4-ks-bench/silesia.tar
# file-based wall clock (tmpfs)
cp ~/LZ4-ks-bench/silesia.tar /dev/shm/ && cd /dev/shm
time ~/LZ4-ks/lz4 -1 -T16 -c silesia.tar > /dev/null
time pigz -1 -p16 -c silesia.tar > /dev/null
time pzstd -1 -p16 -c silesia.tar > /dev/null
time zstd -1 -T16 -c silesia.tar > /dev/null
Raw data: ~/LZ4-ks/OPTIMIZATION-RESULTS.md (section “Competitive benchmark
vs reference parallel compressors”). Tools: pigz 2.8, pzstd/zstd 1.5.5, LZ4 v1.10.0-dev
(commit 0ad1b65 + PGO/LTO build). All measurements taken on an idle machine, August 2026.