zstd 的压缩级别到底在换什么?每提高一级,体积小多少、慢多少、多吃多少内存?和 gzip、lz4、snappy、xz、brotli 比又在哪个位置?这篇把级别、内部参数、解压速度、内存和多线程一次量清楚。
--- products JSON Lines,6.9 MiB ---
级别 压缩后 压缩比 压缩速度 解压速度
-5 2436466 2.96× 824.4 1681
-1 1832929 3.94× 676.3 1768
1 1479666 4.88× 630.4 1788
3 1331194 5.42× 493.9 1819
5 1274145 5.66× 211.3 1782
7 1224789 5.89× 121.0 1062
9 1208529 5.97× 77.9 2028
12 1163953 6.20× 44.1 2036
15 1095849 6.58× 12.7 2209
17 1074433 6.71× 5.4 1846
19 1047098 6.89× 3.8 1737
22 1047069 6.89× 3.2 1739
--- pages(类 HTML),8.0 MiB ---
级别 压缩后 压缩比 压缩速度 解压速度
-5 2813285 2.98× 873.3 2271
-1 2368714 3.54× 735.4 2477
1 1812910 4.63× 655.3 2171
3 1670198 5.02× 419.7 1849
5 1635572 5.13× 208.1 1800
7 1593825 5.26× 122.4 1954
9 1559105 5.38× 90.1 2099
12 1528567 5.49× 38.3 2106
15 1512619 5.55× 8.6 1700
17 1429458 5.87× 4.5 1795
19 1401606 5.98× 3.3 1633
22 1401606 5.98× 3.3 1681级别越高,窗口越大、搜索越深、最小匹配越短、匹配策略越复杂,体积越小,压缩越慢。products 上级别 3 是 5.42×(494 MiB/s),级别 19 是 6.89×(3.8 MiB/s),级别 22 只比 19 再小 29 字节。
级别 strategy windowLog chainLog hashLog searchLog minMatch targetLength
1 1 19 13 14 1 7 0
3 2 21 16 17 1 5 0
5 3 21 18 19 3 5 2
7 4 21 19 20 4 5 8
9 5 22 20 21 4 5 16
12 5 22 22 23 6 5 32
15 6 22 23 23 6 5 32
17 7 23 23 22 5 4 64
19 9 23 24 22 7 3 256
22 9 27 27 25 9 3 999
(strategy 编号:1 fast、2 dfast、3 greedy、4 lazy、5 lazy2、6 btlazy2、7 btopt、8 btultra、9 btultra2——这个对应关系我是按 zstd 的头文件记忆写的,没有在本机核对)zstandard 库的 from_level 给出每个级别的参数:级别 1 的 windowLog 19、searchLog 1;级别 19 的 windowLog 23、searchLog 7、minMatch 3、targetLength 256;级别 22 的 windowLog 27(128 MiB)。第 11 篇里我自己的 LZ77 已经演示过窗口、搜索深度和懒匹配各自的作用。
压缩器 压缩后 压缩比 压缩速度 解压速度 zstd-1 1479666 4.88× 628.8 1611 zstd-3 1331194 5.42× 490.6 1783 zstd-9 1208529 5.97× 94.2 2016 zstd-19 1047098 6.89× 3.9 2088 gzip(zlib)-1 1803223 4.00× 200.1 1336 gzip(zlib)-6 1484671 4.86× 73.2 1346 gzip(zlib)-9 1460895 4.94× 43.1 1124 lz4 默认 2314121 3.12× 596.4 4155 lz4 HC-6 1666007 4.33× 87.5 4343 lz4 HC-12 1638242 4.40× 22.2 4159 snappy 2511526 2.87× 725.1 1693 xz 预设 0 1382936 5.22× 45.4 130 xz 预设 6 988040 7.30× 3.6 157 xz 预设 9 988040 7.30× 3.6 162 brotli -q1(命令行) 1588412 4.54× 232.1 344 brotli -q5(命令行) 1217062 5.93× 73.1 497 brotli -q9(命令行) 1120363 6.44× 28.7 534 brotli -q11(命令行) 1012833 7.12× 0.8 511 (brotli 命令行的速度包含启动进程和读写文件的时间,和库内调用不完全可比;xz 只测了 1 次)
同样的压缩比,zstd 比 gzip 快得多(zstd-1 压缩比 4.88× 且 629 MiB/s,zlib-6 是 4.86× 但 73 MiB/s);lz4 最快的是解压(约 4150 MiB/s),压缩比低;xz 与 brotli-q11 压缩比最高(7.30×、7.12×),但压缩很慢,xz 解压也只有 157 MiB/s。
zstd-1:压缩后 1479666 字节,解压 1788 MiB/s zstd-3:压缩后 1331194 字节,解压 1819 MiB/s zstd-9:压缩后 1208529 字节,解压 2028 MiB/s zstd-19:压缩后 1047098 字节,解压 1737 MiB/s
同一个解压器,解压速度在 1737–2028 MiB/s 之间,不随级别变化——多花的时间只花在压缩一侧。所以适合压缩一次、读取很多次的数据。
级别 1:窗口 2^19 = 0.5 MiB;压缩峰值内存增量 2.1 MiB(压缩后 1812910 字节);解压峰值内存增量 8.2 MiB 级别 3:窗口 2^21 = 2.0 MiB;压缩峰值内存增量 2.8 MiB(压缩后 1670198 字节);解压峰值内存增量 8.2 MiB 级别 9:窗口 2^22 = 4.0 MiB;压缩峰值内存增量 1.7 MiB(压缩后 1559105 字节);解压峰值内存增量 8.1 MiB 级别 15:窗口 2^22 = 4.0 MiB;压缩峰值内存增量 65.9 MiB(压缩后 1512619 字节);解压峰值内存增量 8.2 MiB 级别 19:窗口 2^23 = 8.0 MiB;压缩峰值内存增量 81.9 MiB(压缩后 1401606 字节);解压峰值内存增量 8.2 MiB 级别 22:窗口 2^27 = 128.0 MiB;压缩峰值内存增量 128.7 MiB(压缩后 1401606 字节);解压峰值内存增量 8.2 MiB (这是对 8 MiB 输入的测量:输入比窗口小时,实际用到的内存可能比窗口小;输入的 8 MiB 和输出本身也计在内)
高级别的压缩要更大的哈希表和搜索结构:级别 19 多用 81.9 MiB,级别 22 多用 128.7 MiB(窗口 128 MiB)。解压端的内存主要看输出和窗口。窗口大意味着解压端也要有足够内存(见第 16 篇)。
级别 3:threads=0 4151340 字节、379.0 MiB/s;threads=4 4147915 字节、756.7 MiB/s(2.0 倍速度,体积-0.08%) 级别 10:threads=0 3817719 字节、56.2 MiB/s;threads=4 3821356 字节、65.8 MiB/s(1.2 倍速度,体积+0.10%) 级别 19:threads=0 3411773 字节、2.9 MiB/s;threads=4 3411773 字节、2.5 MiB/s(0.9 倍速度,体积0.00%)
threads=4 在 19.9 MiB 的数据上:级别 3 快 2.0 倍,级别 10 快 1.2 倍,级别 19 没有加速。多线程要把输入切段,输出字节和单线程可能不同,体积差 ±0.1%。