Repository navigation
Conversation
|
Dear Maxpaj, Would you mind explaining what in your view is bogus? Did you test the code performance? |
|
It might be a good idea to split your PR into multiple functional commits explaining each change in detail. This allows each change to be performance tested individually. |
|
I appreciate your constructive comments. The changes are intermingled, so it is difficult to split them into multiple commits in a clear-cut way. How about I amend a document to contrast each of the critical changes and explain the underlying reasons? |
|
I am waiting for the proposer's to-do list to be completed, and I will then test the commit for speed and inspect for correctness and portability. I will then consider whether I should merge it or some rewrite of it.I do not require that this be split into multiple commits. |
|
I have created inflateBackWrap() to validate inflateBack(). Moreover, inflateBackWrap() exhibits identical functionality as inflate(). This allows for an apple-to-apple comparison of performance between inflate() and inflateBack() under different setups. @madler, could you take over and review the code? Feel free to make changes in your manner. |
|
Hi @madler, may I ask what the current status of this work. we'd like to enable Intel DEFLATE IAA/QAT in-kernel support for EROFS, but we'd also like to have a faster DEFLATE software decompression fallback support anyway. I'm not sure if zlib official codebase could address that (otherwise zlib-ng codebase has to be considered instead) since DEFLATE-family is currently a de-facto standard for various common use cases/formats, and better optimized (de)compression for those modern processors could benefit all of us. And I guess old 16-bit platforms are unimportant now since they could still be supported by old stable zlib versions. |
|
This is on my to-do list. |
|
It has been 18 months. Is there anything I can do to expedite the process? |
No, you've done all the good things. It's on me now. |
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
Increases the literal/length root table size from 2^10 (1024) to 2^11 (2048) entries. This eliminates secondary table lookups for 11-bit Huffman codes, which are common in data with flat byte distributions. Benchmarked on the Silesia corpus, this shows ~10% improvement on osdb and ~5% on mr, with no regressions on other files. The tradeoff is a larger ENOUGH_LENS (1332 to 2340), increasing the inflate_state codes array by 4032 bytes. inflate_table() already adapts root downward via root = MIN(root, max) when codes are shorter, so the larger initial value only takes effect for blocks that actually have codes exceeding 10 bits. Based on madler/zlib#793 and zlib-ng#1332.
cfa12c8 to
ec19b74
Compare
|
@madler I have replaced this PR's branch with a new version, rebased on |
Where a 64-bit type is available (Z_U8), inflate_fast() first runs a loop that refills a 64-bit bit buffer eight bytes at a time, once per length/distance pair or per up to three literals. It copies matches sixteen bytes at a time for distances of 16 or more and eight at a time for 8 to 15; for distances 1 to 7 it writes eight bytes per step and advances by the distance. It runs while sixteen bytes of input remain and a match plus fifteen bytes fits in the output; the existing loop then continues as before, so the entry conditions of inflate_fast() and everything outside inffast.c stay the same, except the root table sizes: 10 bits for literal/length codes and 8 for distance codes (were 9 and 6), so that fewer codes need a second lookup. ENOUGH is from examples/enough.c (1332 and 400), and the inflate state grows by 1152 bytes. inflate() is 57% to 74% faster with the whole stream in one call and 47% to 63% faster with 16K input and 64K output buffers, on Silesia, Canterbury plus Calgary, and enwik8 at levels 1, 6 and 9 (AMD EPYC 9334, one core, GCC 14.2 -O2 and -O3). On Silesia level 6 in one call, GCC 11 gives +61% and clang 18 +55%. 32-bit x86 is 9% faster in one call and 4% faster in pieces. test/infcover.c gains cases for the new loop and for second-level codes with the larger roots: every line of inffast.c runs except the byte-wise load used on big-endian machines, and infback.c and inftrees.c stay at 100% line coverage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ec19b74 to
217f080
Compare
This branch now holds a new and much smaller version of this change, rebased on
develop(1.3.2.1). (Updated on 2026-10-12 with sixteen-byte match copies and eight-byte steps for short distances; the numbers below are for this version.)Why it changed. The first version rewrote the decoding tables and parts of
inflate(). Testing it under ASAN/UBSAN and with damaged streams found real problems in it: distances before the start of the output were not checked until a window existed, length symbols 286/287 were not rejected, the code-length code's table was a local ofinflate()and did not survive a return in the middle of a dynamic header, and some copies read before the output buffer. It also did not build with GCC or clang, and it changedinflateCodesUsed(). Please disregard it. This version leaves the table format,inflate()andinflateBack()alone.What it does.
inffast.c: where a 64-bit type is available (Z_U8),inflate_fast()first runs a loop that refills a 64-bit bit buffer eight bytes at a time (one load on little-endian machines, the bytes one by one otherwise), once per length/distance pair or per up to three literals. It copies matches sixteen bytes at a time for distances of 16 or more and eight at a time for 8 to 15; for distances 1 to 7 each step writes eight bytes and advances by the distance; window parts go withzmemcpy(). The loop runs while sixteen bytes of input remain and a match plus fifteen bytes fits in the output; the existing loop then continues unchanged, so the entry conditions ofinflate_fast()are the same.inflate.c,infback.c,inftrees.h: root tables of 10 bits for literal/length codes and 8 for distance codes (were 9 and 6), so fewer codes need a second lookup.ENOUGHis fromexamples/enough.c: 1332 and 400 (1334 and 402 withPKZIP_BUG_WORKAROUND). The inflate state grows by 1152 bytes.test/infcover.c:cover_fast64()covers the new loop and second-level codes with the larger roots. On x86, every line ofinffast.cruns except the byte-wise load for big-endian machines (which runs in the s390x test below);infback.candinftrees.cstay at 100% line coverage (inflate.cat 96%, as before).Only C89, no new API, no change in behavior: for every input, valid or not, the output and return codes are those of
develop.Speed.
inflate()throughput, stockdevelop-> this branch, AMD EPYC 9334 (Zen 4, 2.7 GHz), one core, GCC 14.2 -O2, streams from zlib's own deflate:-O3 gives the same gains (+58% to +76% in one call). On Silesia level 6, GCC 11 gives +61% / +54% and clang 18 +55% / +47%. 32-bit x86 (
-m32), where the 64-bit buffer is emulated: +9% in one call, +4% in pieces. Windows, Visual Studio 2022 (cl /O2, x64) on an Intel Core i7-8850H, level 6: +55% to +64% in one call, +46% to +48% in pieces.Testing.
make test;infcoveras above;-std=c89 -pedanticwithout new warnings;ZLIB_DEBUGbuilds.inflateBack(); plus 60 damaged copies of each stream, whose return codes, output lengths and CRCs must equal those of unpatcheddevelop(34,860 streams). Clean under ASAN and UBSAN, also withINFLATE_STRICT,INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRRandPKZIP_BUG_WORKAROUND. Identical results on x86-64, i386, arm64, ARMv5, ppc64le and s390x (big-endian; QEMU), and with MSVC 2022 (x64).Z_SYNC_FLUSH: every byte before a flush point is delivered once its compressed bytes are in (43,815 messages).develop(valid and damaged streams, random input and output pieces): 65 million runs under ASAN and 47 million under UBSAN, no difference and no report.🤖 Generated with Claude Code