往回看得越远,压缩得越小,代价是内存。这篇量窗口多大才找得到重复、窗口开大了为什么还可能找不到、解压端为什么会报“需要太多内存”,以及每个对象单独压缩要付出多少压缩比。
1 MiB 页面文本单独压缩(级别 3):209800 字节;下面每个数据理想情况(第二份完全被匹配掉)约为 单份 + 填充
填充 填充之外的字节 wLog 17 wLog 19 wLog 20 wLog 21 wLog 22 wLog 24 wLog 27
0.00MiB (压缩后减去填充) 466041 415169 417075 209905 209905 209905 209905
0.25MiB (压缩后减去填充) 466137 416400 417882 210059 210058 210058 210058
1.00MiB (压缩后减去填充) 466419 419030 419970 419315 210326 210326 210326
4.00MiB (压缩后减去填充) 467562 420173 421113 421113 421113 211459 211459
16.00MiB (压缩后减去填充) 472050 424661 425601 425601 425601 425601 425600
(表里的数字是 压缩后总字节 - 填充字节:约等于 209800 表示第二份被匹配掉了,约等于 419600 表示第二份没有找到(重复压了一遍))匹配只能指向窗口内的内容。重复的内容相隔 1 MiB,windowLog 20(1 MiB)找不到,windowLog 21(2 MiB)才行;相隔 5 MiB 要 windowLog 24。对 gzip 来说窗口固定是 32 KiB,更远的重复统统错过。
级别 3,windowLog 27,不开 LDM → 17202831 字节(425615 字节减去填充),22 ms 级别 3,windowLog 27 + enable_ldm → 16993311 字节(216095 字节减去填充),37 ms 级别 3,windowLog 20(1 MiB)+ enable_ldm → 17203150 字节(425934 字节减去填充),36 ms
相隔 16 MiB 的重复,级别 3 开了 windowLog 27 仍然没找到,因为搜索用的哈希表容量有限,老位置被覆盖。长距离匹配(enable_ldm)另建结构专门找远处的重复,开了才找到;但 LDM 仍受窗口限制。
windowLog 22 流式压成 17198867 字节,帧头声明的窗口 4 MiB windowLog 27 流式压成 16987969 字节,帧头声明的窗口 128 MiB windowLog 28 流式压成 16987969 字节,帧头声明的窗口 256 MiB 帧 windowLog 22,解压端 max_window_size = 4 MiB:成功,输出 18874368 字节(一致) 帧 windowLog 22,解压端 max_window_size = 64 MiB:成功,输出 18874368 字节(一致) 帧 windowLog 22,解压端 max_window_size = 128 MiB:成功,输出 18874368 字节(一致) 帧 windowLog 27,解压端 max_window_size = 4 MiB:失败:ZstdError: zstd decompressor error: Frame requires too much memory for decoding 帧 windowLog 27,解压端 max_window_size = 64 MiB:失败:ZstdError: zstd decompressor error: Frame requires too much memory for decoding 帧 windowLog 27,解压端 max_window_size = 128 MiB:成功,输出 18874368 字节(一致) 帧 windowLog 28,解压端 max_window_size = 4 MiB:失败:ZstdError: zstd decompressor error: Frame requires too much memory for decoding 帧 windowLog 28,解压端 max_window_size = 64 MiB:失败:ZstdError: zstd decompressor error: Frame requires too much memory for decoding 帧 windowLog 28,解压端 max_window_size = 128 MiB:失败:ZstdError: zstd decompressor error: Frame requires too much memory for decoding 命令行 zstd -d:windowLog 22 的帧:退出码 0,输出 18874368 字节,stderr:'' 命令行 zstd -d:windowLog 28 的帧:退出码 1,输出 0 字节,stderr:'/Users/w/Desktop/zen/tools/articles/ptmp/e16/w28.zst : Decoding error (36) : Frame requires too much memory for decoding \n/Users/w/Desktop/zen/tools/a' 命令行 zstd -d --long=28:windowLog 28 的帧:退出码 0,输出 18874368 字节
压缩端声明的窗口决定了解压端需要的内存。windowLog 27 的帧在解压端限制 64 MiB 时失败,windowLog 28 的帧命令行默认就解不了,要 --long=28。给别人分发压缩文件时,窗口不要开得比对方的内存大。
zlib-9(窗口 32 KiB):1822313 字节(压缩比 4.60×)
zstd 级别 wLog 15 wLog 17 wLog 20 wLog 23
3 2175415 1864670 1660885 1676095
9 1839849 1590804 1558895 1560280
19 1747221 1494076 1444525 1401606同样 32 KiB 窗口,zstd-19 比 zlib-9 只小 4%;放开窗口,zstd-3 在 1 MiB 窗口下就比 zlib-9 小 9%。zstd 比 gzip 小,窗口是原因之一。
windowLog 窗口 压缩后 压缩多用内存 流式解压多用内存
15 0.03M 5407003 13.8 MiB 0.2 MiB 解出长度一致
18 0.25M 4112979 11.2 MiB 0.5 MiB 解出长度一致
21 2.00M 4173277 12.9 MiB 2.5 MiB 解出长度一致
24 16.00M 4177740 27.2 MiB 16.5 MiB 解出长度一致
27 128.00M 4172915 30.7 MiB 20.4 MiB 解出长度一致窗口越大,压缩和解压需要的内存越多(流式解压 0.2 MiB 到 20.4 MiB),但窗口超过数据中重复的间隔后,压缩后的大小就不再改善(windowLog 18 之后基本不变)。
每块大小 块数 压缩后总字节 压缩比 比一整块大
4KiB 2048 2705807 3.10× 62.0%
16KiB 512 2376431 3.53× 42.3%
64KiB 128 2007345 4.18× 20.2%
256KiB 32 1759232 4.77× 5.3%
1024KiB 8 1679330 5.00× 0.5%
8192KiB 1 1670198 5.02× 0.0%每个对象单独压缩等于每次重置窗口。4 KiB 的块压缩比 3.10×,整块是 5.02×;块到 256 KiB 才接近(4.77×)。小对象要攒批或用字典(下一篇)。