Compress or not: the rate comparison
Measure your machineNeeds Python with lz4, zstandard
Problem
Compression is worth it only if the time it saves on the wire exceeds the time it costs in CPU: compress + transfer(compressed) + decompress must beat transfer(raw). On a slow link, the transfer dominates and the strongest codec wins. On a fast link, transfer is nearly free and the codec's own time dominates, so a fast codec or none at all wins. The right choice changes with the link, not with the data alone.
Measure compress_time + transfer(compressed) + decompress_time against transfer(uncompressed) for a 500 MB file of text or JSON, using LZ4 (pip install lz4), Zstd levels 1, 3 and 10, and gzip. Time each codec on your laptop. Then compute the totals for three link speeds (10 Mb/s, 1 Gb/s, 100 Gb/s) with arithmetic, not with a real network.
The question. Which codec is fastest end to end at 10 Mb/s, at 1 Gb/s and at 100 Gb/s?
Predict
Expect the ranking to change with the link: gzip and Zstd-10 may win at 10 Mb/s, LZ4 or Zstd-1 at 1 Gb/s, and no compression at 100 Gb/s.
Run it
1Install uv once. It fetches Python and the lab's packages for you.
Terminal curl -LsSf https://astral.sh/uv/install.sh | sh2Get the script. It is the whole lab: read it, then download it.
View the codeHide the code
compress-or-not-the-rate-comparison.py # /// script # requires-python = ">=3.10" # dependencies = ["lz4", "zstandard"] # /// # Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT # https://thedataengineering.com/lab-code/LICENSE.txt """Compress or not: the rate comparison. compress + transfer(compressed) + decompress against transfer(uncompressed), for a JSON file, with LZ4, Zstd levels 1, 3 and 10, and gzip. Codecs are timed on this machine; transfers are computed for 10 Mb/s, 1 Gb/s and 100 Gb/s links. """ import argparse, gzip, json, random, time import lz4.frame, zstandard as zstd p = argparse.ArgumentParser(description=__doc__.splitlines()[0]) p.add_argument("--mb", type=int, default=500, help="Size of the generated JSON text, in MB.") p.add_argument("--links", type=lambda s: [float(v) for v in s.split(",")], default=[0.01, 1, 100], help="Link speeds in Gb/s.") p.add_argument("--json", help="Also write the results to this JSON file.") args = p.parse_args() random.seed(0) line = lambda i: json.dumps({"id": i, "user": f"u{random.randint(1, 50_000)}", "event": random.choice(["view", "click", "buy"]), "path": f"/p/{random.randint(1, 2000)}", "ms": random.randint(1, 900)}) + "\n" chunk = "".join(line(i) for i in range(200_000)).encode() data = (chunk * (args.mb * 1_000_000 // len(chunk) + 1))[: args.mb * 1_000_000] codecs = { "none": (lambda b: b, lambda b: b), "LZ4": (lz4.frame.compress, lz4.frame.decompress), "Zstd 1": (zstd.ZstdCompressor(level=1).compress, zstd.ZstdDecompressor().decompress), "Zstd 3": (zstd.ZstdCompressor(level=3).compress, zstd.ZstdDecompressor().decompress), "Zstd 10": (zstd.ZstdCompressor(level=10).compress, zstd.ZstdDecompressor().decompress), "gzip 6": (lambda b: gzip.compress(b, 6), gzip.decompress), } rows, totals = [], [] for name, (comp, decomp) in codecs.items(): t = time.perf_counter(); z = comp(data); tc = time.perf_counter() - t t = time.perf_counter(); decomp(z); td = time.perf_counter() - t rows.append({"codec": name, "ratio": len(data) / len(z), "compress_s": tc, "decompress_s": td}) for gbps in args.links: total = tc + len(z) * 8 / (gbps * 1e9) + td totals.append({"codec": name, "gbps": gbps, "total_s": total}) print(f"{name:<8} ratio {len(data) / len(z):5.2f}x compress {tc:6.2f} s decompress {td:5.2f} s " + " ".join(f"{gbps:g} Gb/s: {next(x['total_s'] for x in totals if x['codec'] == name and x['gbps'] == gbps):8.2f} s" for gbps in args.links)) best = {gbps: min((x for x in totals if x["gbps"] == gbps), key=lambda x: x["total_s"])["codec"] for gbps in args.links} print("fastest end to end: " + ", ".join(f"{g:g} Gb/s -> {c}" for g, c in best.items())) if args.json: with open(args.json, "w") as f: json.dump({"tables": {"codecs": rows, "totals": totals, "best": [{"gbps": g, "codec": c} for g, c in best.items()]}}, f)3Run it in the folder you saved it to, with the defaults first. Then change a knob from the table below and run it again.
Terminal uv run compress-or-not-the-rate-comparison.py uv run compress-or-not-the-rate-comparison.py --help # every knob
| Knob · default | What it changes |
|---|---|
| --mb500 | Size of the generated JSON text, in MB. |
| --links0.01, 1, 100 | Link speeds in Gb/s. |
Our run
500 MB of JSON lines, one core
| Codec | Ratio | Compress | Decompress |
|---|---|---|---|
| none | 1× | 3.58 µs | 0.334 µs |
| LZ4 | 3.53× | 797 ms | 293 ms |
| Zstd 1 | 5.59× | 921 ms | 515 ms |
| Zstd 3 | 5.8× | 1.12 s | 397 ms |
| Zstd 10 | 7.03× | 7.5 s | 297 ms |
| gzip 6 | 6.55× | 6.38 s | 830 ms |
Fastest end to end
| Link (Gb/s) | Codec |
|---|---|
| 0.01 | Zstd 10 |
| 1 | Zstd 1 |
| 100 | none |
none ratio 1.00x compress 0.00 s decompress 0.00 s 0.01 Gb/s: 400.00 s 1 Gb/s: 4.00 s 100 Gb/s: 0.04 s
LZ4 ratio 3.53x compress 0.80 s decompress 0.29 s 0.01 Gb/s: 114.54 s 1 Gb/s: 2.22 s 100 Gb/s: 1.10 s
Zstd 1 ratio 5.59x compress 0.92 s decompress 0.51 s 0.01 Gb/s: 72.98 s 1 Gb/s: 2.15 s 100 Gb/s: 1.44 s
Zstd 3 ratio 5.80x compress 1.12 s decompress 0.40 s 0.01 Gb/s: 70.44 s 1 Gb/s: 2.20 s 100 Gb/s: 1.52 s
Zstd 10 ratio 7.03x compress 7.50 s decompress 0.30 s 0.01 Gb/s: 64.70 s 1 Gb/s: 8.37 s 100 Gb/s: 7.80 s
gzip 6 ratio 6.55x compress 6.38 s decompress 0.83 s 0.01 Gb/s: 68.26 s 1 Gb/s: 7.82 s 100 Gb/s: 7.21 s
fastest end to end: 0.01 Gb/s -> Zstd 10, 1 Gb/s -> Zstd 1, 100 Gb/s -> noneWhat we found
The ranking changed with the link, as expected. At 10 Mb/s the strongest codec won: Zstd 10. At 1 Gb/s a fast one did: Zstd 1. At 100 Gb/s sending raw beat every codec (none), because even LZ4's 797 ms to compress took longer than sending the uncompressed file.
Read it
The compression decision is the fixed-cost idea with its migration clause: when the network gets faster, the break-even moves. Decide per link and per data: measure the ratio on your own data and the codec speed on your own CPU, then do the arithmetic for the link the data actually crosses.