经典 vs 现代
CompressKit 的四种算法诞生于 1977-1987 年,是压缩领域的"教科书"。 这里把它们与今天的主流压缩器放在同一组语料上对比——不是为了证明谁更 好,而是为了看清经典算法在现代生态中的真实位置:压缩率不输,速度差 一个数量级,且对跨符号结构无能为力。
测量方法
- 语料:
tests/data/(make test-data生成),与本站基准测试一致 - 现代压缩器全部使用默认级别,并附
zstd -3(快速档)作参考 - 本机实测(Linux x86-64, 2026-09-02),单线程;你的机器上数字会变,量级不变
- 复现:见文末命令
textlike_10MiB(熵 5.944 bits/byte)
| 工具 | 输出大小 | 压缩率 | 编码时间 | 编码吞吐 |
|---|---|---|---|---|
| gzip -6 | 7,864,920 | 0.750 | 0.37 s | 27 MiB/s |
| zstd -3 | 7,799,207 | 0.744 | 0.06 s | 156 MiB/s |
| zstd -19 | 7,804,829 | 0.744 | 3.9 s | 2.6 MiB/s |
| brotli -5 | 7,798,550 | 0.744 | 0.14 s | 69 MiB/s |
| brotli -11 | 7,797,834 | 0.744 | 19 s | 0.5 MiB/s |
| xz -6 | 7,957,448 | 0.759 | 4.7 s | 2.1 MiB/s |
| bzip2 -9 | 7,870,775 | 0.751 | 0.73 s | 14 MiB/s |
| ck-arithmetic | 7,791,384 | 0.743 | 0.46 s | 22 MiB/s |
| ck-huffman | 7,819,095 | 0.746 | 0.11 s | 87 MiB/s |
| ck-range | 8,285,633 | 0.790 | 0.11 s | 88 MiB/s |
| ck-lzss | 11,569,829 | 1.103 | 0.13 s | 79 MiB/s |
| ck-rle | 51,567,663 | 4.918 | 0.36 s | 27 MiB/s |
观察 1:静态熵模型的压缩率并不落后。 ck-arithmetic(0.743)是这张表里 压缩率最好的——比 zstd -19 和 brotli -11 还略好。全量扫描频率表的静态模型 在单文件上有天然优势:它"看过"整个文件。现代压缩器的压缩率优势主要来自 上下文建模(LZ 匹配 + 熵后端),而不是熵编码本身。
观察 2:速度差距是数量级。 zstd -3 以 156 MiB/s 达到与 ck-arithmetic 几乎相同的压缩率(0.744),快 7 倍;gzip 也比最快的 ck 算法快 3 倍。 教学实现的价值在可读性,不在速度——这正是本仓库不追逐吞吐优化的原因。
repetitive_10MiB(长重复数据)
| 工具 | 输出大小 | 压缩率 |
|---|---|---|
| zstd -3 | 15,384 | 0.0015 |
| xz -6 | 17,288 | 0.0016 |
| brotli -5 | 18,829 | 0.0018 |
| bzip2 -9 | 21,038 | 0.0020 |
| gzip -6 | 23,730 | 0.0023 |
| ck-rle | 25,618 | 0.0024 |
| ck-lzss | 1,248,010 | 0.119 |
| ck-arithmetic | 10,423,990 | 0.994 |
| ck-huffman | 10,463,709 | 0.998 |
| ck-range | 10,869,461 | 1.037 |
观察 3:为什么需要字典编码。 ck-rle 与通用压缩器同一量级(0.0024 vs 0.0015),ck-lzss 也把 run 结构压到 0.119,而 ck-huffman/arithmetic/range 全部接近 1.0——逐字节频率模型看不到跨符号的结构("这段是那段的重现")。 zstd/gzip 的 LZ77 字典能捕获这种结构,这正是 1977 年 Lempel-Ziv 论文的 动机;仓库第五种算法 LZSS 就是这个思想的 4 KiB 窗口 教学实现。注意 LZSS 在 textlike 上反而膨胀(1.103)——字典编码不是 "更好的熵编码",而是另一种工具。
random_1MiB(不可压缩)
| 工具 | 输出大小 | 压缩率 |
|---|---|---|
| zstd -3 | 1,048,613 | 1.0000 |
| brotli -5 | 1,048,581 | 1.0000 |
| xz -6 | 1,048,688 | 1.0001 |
| gzip -6 | 1,048,770 | 1.0002 |
| ck-arithmetic | 1,049,601 | 1.0010 |
| ck-huffman | 1,050,109 | 1.0015 |
| bzip2 -9 | 1,053,432 | 1.0046 |
| ck-range | 1,061,679 | 1.0125 |
| ck-lzss | 1,179,393 | 1.125 |
| ck-rle | 5,222,243 | 4.9803 |
观察 4:没有算法能压缩随机数据。 全部熵编码器都停在 1.00 附近(只有 表头开销),bzip2 因 BWT 块头略膨胀,LZSS 因 flag 字节开销停在 1.125。 任何声称能压缩随机数据的工具都是在骗你——RLE 的 4.98× 膨胀是格式 契约,不是 bug。
fastq_10MiB(FASTQ 风格测序数据)
| 工具 | 输出大小 | 压缩率 |
|---|---|---|
| bzip2 -9 | 3,856,165 | 0.368 |
| xz -6 | 4,143,732 | 0.395 |
| zstd -19 | 4,175,687 | 0.398 |
| gzip -6 | 4,658,594 | 0.444 |
| brotli -5 | 4,688,118 | 0.447 |
| zstd -3 | 4,742,679 | 0.452 |
| ck-arithmetic | 4,750,049 | 0.453 |
| ck-huffman | 4,812,963 | 0.459 |
| ck-range | 5,200,936 | 0.496 |
| ck-lzss | 5,796,411 | 0.553 |
| ck-rle | 43,830,476 | 4.180 |
观察 5:结构 vs 统计的差距在基因组数据上被放大。 默认档的现代压缩器 与 ck-arithmetic 几乎同级(0.44-0.45),但 bzip2/xz/zstd -19 能压到 0.37-0.40——它们的 LZ/BWT 结构捕获了 FASTQ 中 @read_N 行与质量字符串 的重复模式。ck-lzss(0.553)能捕获部分结构但仍落后于纯熵编码(0.453): 这个语料的 read 间冗余有限,flag 开销超过匹配收益——真实测序数据(如 fq-compressor 面向的 FASTQ)有更强的 read 间冗余,现代压缩器的优势只会更大,这也是为什么 生信压缩需要专用算法(参考序列 + 熵编码),而不是裸 LZ。
复现
make build && make test-data
python3 tests/bench.py # 本站基准(仅 ck 算法)
# 现代压缩器对比(Linux):
for ds in textlike_10MiB repetitive_10MiB random_1MiB; do
for t in "gzip -6" "zstd -3" "zstd -19" "brotli -5" "xz -6" "bzip2 -9"; do
read -r name args <<< "$t"
$name $args < tests/data/$ds.bin > /tmp/$name-$ds.bin
printf '%-12s %s: %s bytes\n' "$name" "$ds" "$(stat -c%s /tmp/$name-$ds.bin)"
done
done2
3
4
5
6
7
8
9
10
结论
| 维度 | 经典算法(本仓库) | 现代压缩器 |
|---|---|---|
| 压缩率 | 单文件上与 zstd/brotli 同级 | 上下文建模,多文件/长距离更强 |
| 速度 | 20-90 MiB/s(LZSS 解码 150+) | zstd 150+ MiB/s,gzip 30 MiB/s |
| 格式透明 | 完全透明,可读可验证 | 专有格式,难以教学 |
| 结构性数据 | RLE 捕获连续重复,LZSS 捕获任意距离重复 | LZ 字典 + 熵后端,参数更大 |