v5.0
live available
compute ranked 30 Sept 2026

Ten compressor settings on 128 MiB of logs, ranked by the bytes each CPU second bought

zstd at its default is smaller than gzip -6 and cheaper at the same time. Past that point one CPU second is worth 138 kB of AWS egress, and level 19 stops clearing the bar.

By Purvansh Parmar Checked 2026-09-30 Read 8 min Entries 10

You have 128 MiB of logs and a pipe to somewhere that charges for bytes. The habit is gzip. On this machine that habit costs 33 percent more bytes than zstd at its own default and six times the CPU to produce them, which is an unusual thing for a default to get wrong in both directions at once. The better question is where to stop climbing. A vCPU second on a c7i.large costs $0.0000124 and a gigabyte out of AWS costs $0.09, so one vCPU second is worth 138 kB of egress, and every level above the default has to clear that bar to be worth typing.

How this was checkedRun on 2026-09-30 on one 8-core i5-1135G7 under Ubuntu 22.04, with input and output both in tmpfs so no disk touched the timings. The corpus was 128 MiB of real GitHub Archive event JSON, 32,061 complete events, plus 128 MiB of ELF shared objects and 32 MiB of already-gzipped data. gzip -6 needed 2.33 CPU seconds to reach 13.5 percent of the input. zstd -3 reached 10.2 percent in 0.37. Climbing on to zstd -19 with a 128 MB window removed a further 5.05 MB and charged 44.13 CPU seconds for it, a rate of 114 kB per CPU second against a break-even of 138 kB. xz -9 landed on the same 6.4 percent and then read back at 151 MB/s instead of 674, holding 811 MB of RAM to compress 128 MB.

What I compared on

01 lz4 -1

The fast end of the trade: a byte-oriented LZ codec with no entropy coding stage at all.

Wins
The compressor is fighting the thing that produces the data for the same core. It took 0.24 CPU seconds for the whole 128 MiB and read it back at 985 MB/s, so it is close to free at both ends. Right for a pipe, or for a spool file that gets deleted within the hour.
Loses
It stops early. At 19.9 percent of the input it leaves nearly twice as many bytes on the wire as zstd -3, and zstd -3 asked for only 0.13 CPU seconds more to get there. On the binary corpus the gap widens to 47.5 percent against 31.9.
19.9% of input0.24 CPU s512 MB/s in, 985 MB/s out447 MB removed per CPU s

02 zstd -3

Zstandard at its shipped default, which is what you get when you type zstd with no flags.

Wins
Smaller than gzip -6 and cheaper than gzip -6, both at once: 10.2 percent of the input against 13.5, on 0.37 CPU seconds against 2.33. It read back at 1067 MB/s, the fastest figure in the table, and peak memory stayed at 36 MB writing and 4 MB reading.
Loses
It is not the smallest thing here and it is not going to become it. If the object will sit in a bucket for a year, the 6.4 percent that level 19 reaches is worth the CPU and -3 will never get you there. Runtimes without a zstd binding still need gzip.
10.2% of input0.37 CPU s1067 MB/s decompress36 MB peak RSS

03 brotli -q 5

Brotli at mid quality, the setting most HTTP servers use for compressing a response in flight.

Wins
The best ratio per CPU second above the very fast settings. It removed 1.84 MB more than zstd -3 for 1.46 extra CPU seconds, which is 1.26 MB per CPU second against a 138 kB break-even, a nine to one return. The decoder held 19 MB, the smallest of anything that got under 9 percent.
Loses
Single threaded, with no -T flag to reach for. At 70 MB/s in it runs at a fifth of zstd -3, and on the binary corpus it falls to 28 MB/s. Outside a browser or an HTTP stack nothing reads .br unless you tell it to.
8.8% of input1.83 CPU s70 MB/s in19 MB decoder RSS

04 gzip -6

DEFLATE at the default level, and the thing almost every script in your repository already calls.

Wins
Universality, and a memory footprint nothing else comes near. Every language runtime, every HTTP client and every tape format reads it with no dependency, and it compressed inside 1.9 MB of RAM, the smallest figure in the table by a factor of three. When the reader is unknown this is the only safe answer.
Loses
It is dominated. zstd -3 produced a file 25 percent smaller using a sixth of the CPU, so there is no workload on which gzip -6 is the better of the two on the numbers. Picking it is a compatibility decision and deserves to be argued as one.
13.5% of input2.33 CPU s55 MB/s in1.9 MB peak RSS

05 pigz -6

Parallel DEFLATE with byte-compatible output, running eight workers on this box.

Wins
Wall clock. The same gzip file took 0.45 seconds instead of 2.34, and the output differs from gzip's by 25 kB out of 18 MB, so anything that reads gzip reads this without being told. On a build machine you rent by the minute that is the entire argument.
Loses
It bought that with 3.16 CPU seconds against gzip's 2.33, so 36 percent more total CPU for the same bytes. On Lambda, on Cloud Run, or on any CI plan billed per core minute, you pay for the extra work and see none of the saving.
13.5% of input3.16 CPU s against gzip's 2.330.45s wall against 2.34

06 zstd -12

Zstandard near the top of its normal band, run with -T0 across eight threads.

Wins
It reaches 8.6 percent, which is brotli territory, and still reads back at 914 MB/s. Measured against zstd -3, the extra 4.45 CPU seconds removed 2.11 MB, a rate of 475 kB per CPU second, so it clears the egress break-even three and a half times over.
Loses
218 MB of peak RSS while compressing, against 36 MB at level 3, which is the kind of number that ends a 512 MB function. And it is thirteen times the CPU of -3 for 1.6 percentage points, so on a stream you never store it is close to pointless.
8.6% of input4.82 CPU s218 MB peak RSS914 MB/s decompress

07 bzip2 -9

Burrows-Wheeler block sorting, the format behind every .tar.bz2 still on an FTP mirror.

Wins
On the binary corpus it did something the headline ranking hides. It landed at 29.9 percent for 10.88 CPU seconds, within 1.1 points of zstd -12 while using less CPU than it. Block sorting suits repetitive machine code better than it suits prose.
Loses
On the logs it is a rout. 15.36 CPU seconds to reach 9.9 percent, a figure zstd -3 beat at 10.2 percent for 0.37 seconds, so 41 times the CPU bought three tenths of a point. It also reads back at 43 MB/s, slower than everything else here by a factor of three.
9.9% of logs, 29.9% of binaries15.36 CPU s43 MB/s decompress

08 xz -9 -T0

LZMA2 at the maximum preset with all eight threads, the setting behind most distribution tarballs.

Wins
On binaries it found bytes nothing else did: 20.7 percent of the input, 2.4 points better than the next best entry, at 234 kB per CPU second over zstd -3. That still clears the egress bar, so pushing a release tarball once to a mirror is exactly the case this setting is for.
Loses
On the logs it tied zstd -19 at 6.4 percent and then read back at 151 MB/s against 674, a factor of four and a half on the side that happens more often than the write. Compressing needed 811 MB of peak RSS to process 128 MB of input, which will take out a small container with no warning at all.
6.4% of logs, 20.7% of binaries39.25 CPU s811 MB peak RSS151 MB/s decompress

09 zstd -19 --long=27

Zstandard at the top of its normal range with a 128 MB match window, across eight threads.

Wins
The archive setting. 6.4 percent on the logs, the same as xz, and it still opens at 674 MB/s inside 130 MB of RAM. If the object is going to cold storage and will be read back in a hurry during an incident, this is the only entry that is both small and quick to open.
Loses
44.5 CPU seconds. Against zstd -3 that works out at 114 kB removed per CPU second, under the 138 kB a CPU second is worth in egress, so a ship-once artifact loses money on it. Held in S3 Standard instead, it takes about five months to pay the CPU back.
6.4% of input44.5 CPU s114 kB per CPU s against a 138 kB break-even130 MB decoder RSS

10 brotli -q 11

Brotli at maximum quality, the setting a build step uses to write .br files once and stop.

Wins
It won the ratio outright on text at 6.3 percent, and it reads back at 492 MB/s holding 19 MB. For a bundle compressed once in CI and served from a CDN a million times, the CPU amortises to nothing and the decoder memory is the only figure that still matters.
Loses
183 CPU seconds on the logs and 389 on the binaries, where it managed 0.3 MB/s and still came out larger than zstd -19. Single threaded the whole way. As a rate that is 28 kB per CPU second, five times worse than what a CPU second buys in egress.
6.3% of logs183 CPU s0.3 MB/s on binaries492 MB/s decompress

Side by side

% of logsCPU skB per CPU sDecompress
lz4 -119.9%0.24447,809985 MB/s
zstd -310.2%0.37325,8481067 MB/s
brotli -q 58.8%1.8366,888582 MB/s
gzip -613.5%2.3349,821200 MB/s
pigz -613.5%3.1636,743512 MB/s
zstd -128.6%4.8225,452914 MB/s
bzip2 -99.9%15.367,87243 MB/s
xz -9 -T06.4%39.253,201151 MB/s
zstd -19 --long=276.4%44.502,823674 MB/s
brotli -q 116.3%183.18686492 MB/s

Verdict

Type zstd and take the default. Level 3 is 25 percent smaller than gzip -6 for a sixth of the CPU, and nothing else in this table is free in both directions at the same time. If the artifact is stored rather than streamed, take one step up, to brotli -q 5 on text or zstd -12 on anything mixed, and stop: both clear the 138 kB break-even three times over or better. Level 19 and xz -9 -T0 only pay when the bytes sit still for about five months, and brotli -q 11 belongs in a build step that runs once. Drop bzip2 -9 from your scripts. One more measured thing worth knowing: run bzip2 -9 over data that is already gzipped and it spends 4.12 CPU seconds making the file 81 kB bigger.

Sources