compression: add compression-threads option - #7128
Open
ernetas wants to merge 1 commit into
Open
Conversation
Layers exported as zstd are not compressed in parallel. The encoder from klauspost/compress is created with its default concurrency, but in streaming mode that only pipelines match finding with entropy coding and writing: the blocks themselves are encoded sequentially because they share the match history. Compressing a large layer at a high level can take minutes while the remaining cores sit idle. The encoder can instead split the input into large independently compressed jobs (zstd.WithConcurrentBlocks), the same way the zstd CLI implements -T<n>. Job boundaries are fixed and each job depends only on its own input plus an overlap prefix taken from the previous job, so the output is deterministic: it does not depend on the number of threads or on how the stream is written, only on whether parallel mode is used at all. The compression ratio is practically unchanged. Expose this as a compression-threads=<n> attribute next to compression-level, parsed by the shared compression attribute parser so the image, OCI, registry cache and local cache exporters all accept it. 0 uses all CPUs, 1 selects the sequential encoder and larger values select the number of parallel jobs. Like compression-level for uncompressed layers, the option is ignored by compression types that have no use for it; only zstd honours it for now. The option is opt-in and the default output is unchanged. Enabling parallel jobs changes the produced blob digests and buffers up to a few 16-32MB jobs per thread, so the memory for speed trade-off is left to the user. Signed-off-by: Ernestas Lukoševičius <ernetas@gmail.com>
Member
|
I don't see how a user is ever supposed to know what value provides good performance and doesn't OOM the machine at the same time. I support making sure we use all cores, but for that we need controls that allow using X cores to compress Y layers in parallel. If it is X cores for one layer, where all layers still run in parallel, then it is unpredictable. See also #6841 |
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.
Layers exported as zstd are not compressed in parallel today.
zstd.NewWriteris created with klauspost's default concurrency (GOMAXPROCS), but in streaming mode that only pipelines match finding with entropy coding and writing; the blocks themselves are encoded sequentially because they share the match history. A large layer at a high compression level takes minutes while the other cores sit idle.klauspost/compress (already vendored at v1.19.2) supports real parallel stream compression with
WithConcurrentBlocks(true): the input is split into large jobs (4× window size, 16–32MB) that are compressed simultaneously, each non-first job receives an overlap prefix from the previous job, and the output is flushed in order as a valid single-frame stream — the same design aszstd -T<n>.This PR exposes that as a
compression-threads=<n>attribute next tocompression-level. It is parsed by the sharedcompression.ParseAttributes, so the image, OCI, registry cache and local cache exporters all accept it:0: use all CPUs (GOMAXPROCS), likezstd -T01: the sequential encodern ≥ 2:nparallel jobsOnly zstd honours it for now; like
compression-levelwithcompression=uncompressed, other types ignore it. Negative or non-integer values are rejected when the attributes are parsed.Determinism
Job boundaries are fixed by size and each job depends only on its own input and its overlap prefix, so the output does not depend on the number of threads or on how the tar stream is chunked into writes — only on whether parallel mode is used at all.
TestZstdCompressThreadsasserts this on a 40MB input spanning several jobs:threads=2≡threads=4≡threads=0, one write ≡ 4KB writes, andthreads=1≡ unset.The option is opt-in and the default output is byte-for-byte unchanged, because enabling parallel jobs changes the produced blob digests.
Numbers
256MB of synthetic text-like data, Apple M1 Pro (10 cores),
BenchmarkZstdCompressThreads:threads=1threads=2threads=4threads=8Memory grows with the thread count: each in-flight job buffers 16–32MB of input and up to ~2n jobs can be in flight per layer writer, on top of buildkit already compressing the layers of a chain concurrently. That is why this is opt-in rather than the new default.
Not in this PR