【问题标题】:Why would gnu parallel chunking improve gzip's compression size?为什么 gnu 并行分块会提高 gzip 的压缩大小?
【发布时间】:2016-11-05 19:59:11
【问题描述】:

文件位于:“Unexpected Efficiency Dept.”

前 9000 万个数字占用大约 761MB,输出如下:

 seq 90000000

根据man parallel,它可以加速gzip的归档大文件,方法是把输入切碎,并使用不同的CPU来压缩块。因此,即使gzip单线程,这种技术也使其多线程

seq 90000000  | parallel --pipe --recend '' -k gzip -9 >bigfile.gz

在 Intel Core i3-2330M (4) @ 2.2GHz 上耗时 46 秒。

将它传递给普通的gzip

seq 90000000  | gzip -9 > bigfile2.gz

在同一个 CPU 上耗时 80 秒。现在是惊喜:

ls -log bigfile*.gz

输出:

-rw-rw-r-- 1 200016306 Jul  3 17:27 bigfile.gz
-rw-rw-r-- 1 200381681 Jul  3 17:30 bigfile2.gz

300K 更大?那看起来不太对劲。首先,我检查了zdiff 文件是否具有相同的内容——是的,相同。我本以为 any 压缩器在处理连续数据流时会比使用分块压缩器做得更好。为什么bigfile2.gz 不小于bigfile.gz

【问题讨论】:

  • 有趣的是,在我的 iMac 上,bigfile2.gz 变小了,并行调用和标准调用所用的时间几乎相同。
  • @MarkSetchell 出于某种原因,Mac OS X seq 不会产生相同的输出。你可以试试jot
  • 可能需要注意的是,pigzparallel+gzip 更小更快(此处为 198345773,来自gzip 的 200381681,以及 52s 用户和 6½s 真实,反对36½s 用户和真实)。
  • parallel --pipe 效率低下。如果可能,请使用parallel --pipepart(在这种情况下不是这样,因为您从管道读取,但您有一个文件,--pipepart 会更快)。

标签: linux shell gzip gnu-parallel chunking


【解决方案1】:

原因是对于这种特殊的、相当不寻常的输入,较小的放气块比较大的块更好。默认情况下,gzip 使用较大的 deflate 块,因为这对正常输入数据最有效。 parallel 命令通过每 1 MB 分解输入来强制执行一些较小的放气块,从而产生很小的增益。尽管大多数块的大小仍然相同。

您可以通过在deflateInit2() 中使用zlibmemLevel 参数为每个 块设置更小的块大小,从而做得更好。在这里,我每次在单个线程中压缩相同的输出,使用从 9 到 2 的 memLevel 值,其中较小的 memLevel 是较小的 deflate 块大小(请注意,zlib 比您的 gzip 在默认级别):

  • 9 - 199688429
  • 8 - 198554111(默认)
  • 7 - 191582070
  • 6 - 184880482
  • 5 - 181295029
  • 4 - 180137425(此输入的最佳值)
  • 3 - 181176610
  • 2 - 185759115

该数据的最佳memLevel 是4,其压缩数据比默认memLevel 的8 小12 MB (9%)。对于memLevel 8,deflate 块大小是 16383 个符号,而对于 memLevel 4,deflate 块大小为 1023 个符号。一个符号要么是文字字节,要么是匹配。

改进来自于输入的极其规则的性质,导致匹配和文字命令的规则序列。块大小越小,出现的此类不同命令就越少,从而对每个命令进行编码所需的位数就越少。 memLevel 3 仍然如此,但到那时,每个 deflate 块开头的代码描述开销抵消了更少不同代码的改进。

zopfli 是一个 deflate 压缩器,它优化了块大小和选择的命令,并设法将其压缩到 100,656,812 字节。虽然花了三个半小时! zopfli 使用压缩级别 11 使用 pigz 调用。

【讨论】:

  • 明确一点,zlib memlevel 2-9 选项 与 @ 987654340@的压缩速度-#1-9)选项,对吗?
  • 正确。 1-9 是压缩级别,它控制压缩器搜索匹配字符串的难度。事实上,对于这个输入,默认级别 6 比 9 压缩得更好!但这是另一个故事。
  • 这种类型的数据使 1023 个符号更好。更细粒度的设置(比如 1013 个符号等)会压缩到更小的最优值吗? 1023 也是数据集 size 所特有的,也就是说,如果有 900 万或 9 亿个数字,1023 个符号是否仍然是最优的?答:测试一些小于 90 mil.、9mil.、900K、90K 的值:parallel 通常似乎比gzip 好一点。 900 万还给了parallel 小胜。
  • 如果使用较少的不同命令,您可以使用较小的块大小做得更好。我正在想象为这些数据手动构建一个放气流,它会有一个非常小的块,其中包含一个数字来引入 1000 个数字的每个新序列,然后是一个块,它只与其他 999 个匹配。请参阅我关于 zopfli 的说明, 对此进行了优化。稍后我会检查它使用的块大小。
  • 原来parallel 有一个-block <size> 选项,用于设置块大小。对 90000(半兆数据)的列表进行测试,压缩的最佳块大小约为 1024 字节,但 parallel 的拆分开销和诸如此类的开销使其花费了 40 倍的时间。
【解决方案2】:

我认为是词典制作的频率不同。 这是速度和压缩效率之间的平衡,例如 gzip vs lzma

我猜它在拆分情况下更频繁。 所以字典的数字和下面的比较相似。

YouTube 上有一个 20 分钟的讲座,Raul Fraile: How GZIP compression works | JSConf EU 2014

【讨论】:

  • 回复:“以下。” following 表示什么名词宾语并不太清楚。抱歉,劳尔·弗莱尔的演讲,由一位自称非压缩专家,用一种浓重的西班牙口音和胆怯柔和的单调进行,对于我习惯于快速说话的美国耳朵来说太慢了——最好只引用您认为相关的部分,或仅链接到视频中最相关的部分。
【解决方案3】:

该效果可能是由于压缩块大小造成的。使用这样的一系列设置压缩相同的输入流:

for i in {1..9}; do seq 90000000 | gzip -$i >$i.gz; done

给出在gzip -5 处达到最小值的文件大小:

-rw-r--r-- 1 203473375 Jul  4 16:39 1.gz
-rw-r--r-- 1 201160853 Jul  4 16:40 2.gz
-rw-r--r-- 1 200181562 Jul  4 16:40 3.gz
-rw-r--r-- 1 204266147 Jul  4 16:40 4.gz
-rw-r--r-- 1 199144028 Jul  4 16:40 5.gz
-rw-r--r-- 1 199688429 Jul  4 16:40 6.gz
-rw-r--r-- 1 199689546 Jul  4 16:41 7.gz
-rw-r--r-- 1 200376213 Jul  4 16:41 8.gz
-rw-r--r-- 1 200381681 Jul  4 16:42 9.gz

这与gzip 的默认-6 相差不远。

【讨论】:

  • 不,不是这里的效果。压缩级别未更改。此外,压缩级别不会改变块大小。您会看到另一种效果,即更高的压缩级别可以找到更长的匹配项,但这种改进会被更多不同的长度和距离所抵消,每个匹配项需要更多的位来编码。
  • 我认为 gzip 程序在设置压缩级别时会更改块大小,但现在我已经纠正了。感谢@Mark 纠正我!
  • 琐事:浪费 15 分钟的 CPU 时间来比较 parallel 与普通 gzip 表,time for f in {1..9} ; do echo $f" " $(seq 90000000 | gzip -$f | wc -c) " " $(seq 90000000 | parallel --pipe --recend '' -k gzip -$f | wc -c) ; done,表明普通 gzip 对于 -1-3 来说要小一些, 之后变大。 parallel198735045 字节处达到其最小值,gzip -5
  • 更多琐事:将pigz 添加到该循环$(seq 90000000 | pigz -$f | wc -c),表明它的最佳位置也是-5,位于197271587 字节。 pigz 每次都是最小的,除了-2gzip 之后排在第二位。
猜你喜欢
  • 2012-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 1970-01-01
  • 2013-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多