Skip to content

igzip: parse the level 3 match map with conditional moves on x86 - #454

Closed
ptweezy wants to merge 1 commit into
intel:masterfrom
ptweezy:igzip-lvl3-parse-asm
Closed

ptweezy wants to merge 1 commit into
intel:masterfrom
ptweezy:igzip-lvl3-parse-asm

Conversation

@ptweezy

@ptweezy ptweezy commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

compress_icf_map_g() parses the level 3 match map greedily: from each position it takes the match there, a literal and the match after it, or two literals. Whether a position starts a match is close to random, so the branches of the C loop mispredict often. This PR moves the parse into a dispatched parse_icf_map_g() and adds parse_icf_map_g_06, which computes the advance from each position, 16 at a time, then follows the advances in a loop that writes the codes and counts the histogram with conditional moves. The loop's only branches are its exits.

Which CPUs run it

x86 CPUs with AVX-512 run parse_icf_map_g_06, at the _06 dispatch level like the other level 3 kernels. Everything else, including aarch64, riscv64 and arch=noarch, runs parse_icf_map_g_base: the C loops, with the output pointer kept in a local. Only level 3 calls the parse.

Output

The output does not change.

Results

Level 3 MB/s, master → this PR, in-process A/B on GitHub runners (harness; runs 1, 2, 3) on the Silesia files and on 256 slices of silesia.tar per size. Each cell is the median over all runs (two per runner) of the best of 11 rounds and, in parentheses, the median over runs of the change in median-round throughput. The benchmarked build has this PR's loop code with two extra setup instructions (a dead store and a redundant xor).

CPU (runners) Whole files 64 KB 16 KB 4 KB 1 KB
AMD EPYC 9V45, Zen 5 (6) 432 → 478 (+11.9%) 400 → 446 (+11.7%) 338 → 369 (+9.6%) 230 → 240 (+4.3%) 116 → 116 (+0.1%)
AMD EPYC 9V74, Zen 4 (3) 302 → 310 (+2.4%) 289 → 296 (+2.3%) 250 → 253 (+1.3%) 182 → 181 (−0.9%) 96 → 95 (−1.4%)
Xeon Platinum 8370C, Ice Lake (3) 232 → 241 (+3.9%) 222 → 231 (+4.0%) 194 → 200 (+3.2%) 142 → 144 (+1.2%) 78 → 78 (−0.3%)
Xeon Platinum 8573C, Emerald Rapids (1) 402 → 445 (+10.9%) 381 → 419 (+9.4%) 311 → 335 (+7.9%) 206 → 211 (+2.0%) 95 → 96 (−0.3%)
Xeon 6973P-C, Granite Rapids (3) 431 → 464 (+7.5%) 413 → 444 (+7.1%) 337 → 357 (+5.4%) 219 → 223 (+1.4%) 103 → 103 (−0.3%)

The Zen 3 runners (EPYC 7763, no AVX-512) run the C version and are within 1% of master at every size.

Testing

  • make check passes on x86-64, with arch=noarch, on aarch64 under qemu, and natively on Windows (mingw-w64) on a Ryzen 7 9800X3D, which dispatches parse_icf_map_g_06. The igzip tests pass on riscv64 under qemu.
  • parse_icf_map_g_06 matches parse_icf_map_g_base (return value, codes, histogram) on random match maps, as SysV code and as win64 code called through a thunk, with guard pages after the output and 64 bytes past the end of the map. The vector pass reads up to 16 entries past the end, which hash_map.overflow[] makes room for.
  • Level 3 output is byte-identical to master in random stateless and stateful runs (all flush types, dictionaries, gzip/zlib, small level buffers, Silesia slices), on x86 with AVX-512 and with arch=noarch.
  • On native Windows, every call of parse_icf_map_g_06 in level 3 deflate preserves xmm6-xmm15, the callee-saved general purpose registers, rsp and DF.

compress_icf_map_g() parses the level 3 match map greedily: from each
position it takes the match there, a literal and the match after it, or
two literals. Whether a position starts a match is close to random, so
the branches of the C loop mispredict often. The compiled loop also
reloads level_buf->icf_buf_next after each store.

Move the parse loops to parse_icf_map_g(), dispatched like the other
level 3 functions. parse_icf_map_g_base keeps the C loops, with the
output pointer in a local. parse_icf_map_g_06, for x86 with AVX-512,
first finds how far the parse moves from each position, 16 positions at
a time. It then follows those advances in a loop that writes the codes
and counts the histogram with conditional moves, so the only branches
of the loop are its exits. The codes and the histogram do not change.

Level 3 in-process A/B against master on GitHub runners, whole Silesia
files: +11.9% on AMD EPYC 9V45 (Zen 5), +10.9% on Emerald Rapids, +7.5%
on Granite Rapids, +3.9% on Ice Lake, +2.4% on EPYC 9V74 (Zen 4).
The measured build had the same loop code, plus a dead store and a
redundant xor in the setup.

Signed-off-by: Parker Loflin <parker@cronstable.dev>
@ptweezy
ptweezy force-pushed the igzip-lvl3-parse-asm branch from ebda600 to 381b855 Compare October 21, 2026 21:27
@ptweezy ptweezy changed the title igzip: parse the level 3 match map without branches on x86 igzip: parse the level 3 match map with conditional moves on x86 Oct 21, 2026
@ptweezy
ptweezy marked this pull request as draft October 21, 2026 22:01
@ptweezy ptweezy closed this Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant