compression: cap the default zstd MT workers at 4 for chunks - #10115
Merged
ThomasWaldmann merged 1 commit intoAug 14, 2026
Merged
Conversation
The BORG_ZSTD_MT_WORKERS default was the cpu count, but a chunk only yields ceil(size / 512KiB) compression jobs - 4 for the 2MiB chunks the default chunker aims at - so most of a big thread pool gets no work, while the pool is still created and torn down again for every chunk. Measured at the chunk level on a 12-core machine (fastcdc 512KiB..8MiB chunks, real data), 4 workers beat 12 on every corpus at the default zstd,-4: +13% (source code) .. +37% (VM image) big-chunk throughput. A sweep over 1/2/4/6/8/12 workers, bucketed by jobs-per-chunk, shows the throughput peak tracking the job count (2-job chunks peak at 2 workers, 4-job chunks at 4) and 12 workers winning nowhere: beyond the job count, extra threads only add startup overhead. export-tar compresses one long stream through a single pool using libzstd's default (large) job size, so it keeps the cpu count as its default via the new stream=True parameter. An explicitly set BORG_ZSTD_MT_WORKERS still overrides both defaults, e.g. for chunker configurations producing much bigger chunks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #10115 +/- ##
==========================================
- Coverage 87.03% 87.02% -0.02%
==========================================
Files 101 101
Lines 17730 17730
Branches 2678 2678
==========================================
- Hits 15431 15429 -2
- Misses 1598 1599 +1
- Partials 701 702 +1 ☔ View full report in Codecov by Harness. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
BORG_ZSTD_MT_WORKERSdefault was the cpu count. But a chunk only yieldsceil(size / 512KiB)compression jobs - 4 for the 2MiB chunks the default chunker aims at - so most of a big thread pool gets no work, while the whole pool is still created and torn down again for every single chunk.The per-chunk default is now
min(cpu count, 4).Chunk-level worker sweep on a 12-core machine (M3 Pro), default fastcdc chunker (512KiB..8MiB chunks), real corpora, big chunks only (>= 768KiB; smaller ones are always compressed single-threaded). Throughput in MB/s at the default
zstd,-4:4 workers beat 12 on every corpus at
zstd,-4(+13% .. +37%). Atzstd,3the sweep peaks at 4-6 workers with 12 always below the peak, atzstd,10it is flat from 4 workers up - no clear win for 12 anywhere. Bucketing the timings by jobs-per-chunk shows the peak tracking the job count: 2-job chunks (768KiB..1MiB) peak at 2 workers, the dominant 4-job (~2MiB) chunks at 4, so beyond the job count extra threads only add startup overhead.export-taris different: it compresses one long stream through a singleZstdFile, using libzstd's default (large) job size, so there are always enough jobs and the pool is created only once. It keeps the cpu count as its default via the newstream=Trueparameter. An explicitly setBORG_ZSTD_MT_WORKERSstill overrides both defaults, e.g. for chunker configurations producing much bigger chunks.The sweep also showed that on data zstd is very fast on anyway, single-threaded compression can beat any multi-threaded configuration at fast levels (at
zstd,-4: VM image 6510 MB/s single-threaded vs 5217 at 4 workers, pure entropy 5072 vs 4298) - while typical file-tree data gains far more from multi-threading than such data loses (code +78%, binaries +83% for 4 workers vs 1). The default stays multi-threaded; theborg help environmenttext now mentions settingBORG_ZSTD_MT_WORKERS=1as a speed option for such data, in addition to its existing ratio/cpu-sharing advice.Follow-up to #10100.
🤖 Generated with Claude Code