尽管CPU和硬盘驱动器的性能没有达到极限,为什么gzip仍然很慢?


14

我有一些JSON文件,每个文件20 GB,我想使用以下文件进行压缩gzip

gzip file1.json

这样就占用了一个完整的CPU内核。

它的处理速度约为25 MB / s(已签入atop),我的硬盘驱动器可以读取125 MB / s,并且我拥有3个免费的处理器内核,因此我希望在并行压缩多个文件时能够加快速度。所以我在其他终端上运行:

gzip file2.json
gzip file3.json
gzip file4.json

令人惊讶的是,我的吞吐量没有增加。每个核心上的CPU大约占25%,而我的高清仍然只能以25 MB / s的速度读取。

为什么以及如何解决?

Answers:


17

我发现了:

原因是gzip(根据目前的CPU速度与HD搜索速度),缓冲区大小非常低

它从输入文件中读取几个KB,对其进行压缩,然后将其刷新到输出文件。考虑到这需要进行硬盘驱动器搜索,因此每秒只能执行少量操作。

我的表现无法扩展的原因是因为已经有人gzip在疯狂地寻求。


我通过使用unix buffer实用程序解决了这个问题:

buffer -s 100000 -m 10000000 -p 100 < file1.json | gzip > file1.json.gz

通过在将大量输入发送到gzip之前对其进行缓冲,可以大大减少小查找的数量。选项:

  • -s-m指定缓冲区的大小(我相信它以KB为单位,但不确定)
  • -p 100 确保仅在缓冲区100%填充后才将数据传递到gzip

并行运行其中的四个,可以达到预期的4 * 25 MB / s吞吐量。


我仍然想知道为什么gzip不允许增加缓冲区大小-这样,如果在旋转的磁盘上运行,它几乎没有用。

编辑:我尝试了更多的压缩程序行为:

  • bzip2 由于其更强大/更多的CPU密集压缩,仅处理2 MB / s
  • lzop 似乎允许更大的缓冲区:每个内核70 MB / s,2个内核可以最大程度地增加我的HD,而不会出现过多的请求

可以dd一样吗?
Simon Kuang

@SimonKuang我怀疑dd它的bs=选项可以做到这一点,是的。
nh2 2014年

听起来这是一个有趣的巧合,对于单个文件,块大小恰好完全利用了单个CPU内核和驱动器的IOPS。
Dave L.

3

看了MIT OpenCourseware中关于6.172:“软件系统的性能工程”的前五讲后,我在一个中等大的测试文件上运行了Linux性能分析器“ perf”。结果似乎显示流水线停滞,其中一条指令必须等待上一条指令的结果。

       │         while (lookahead != 0) {                                                                
       │             /* Insert the string window[strstart .. strstart+2] in the                          
       │              * dictionary, and set hash_head to the head of the hash chain:                     
       │              */                                                                                 
       │             INSERT_STRING(strstart, hash_head);                                                 
  2.07 │       movzbl 0x8096d82(%edx),%eax                                                               
  3.99 │       mov    %edx,%ebp                                                                          
       │       shl    $0x5,%ecx                                                                          
  0.03 │       and    $0x7fff,%ebp                                                                       
  1.94 │       xor    %ecx,%eax                                                                          
  1.43 │       and    $0x7fff,%eax                                                                       
  2.01 │       mov    %eax,0x805e588                                                                     
  2.40 │       add    $0x8000,%eax                                                                      
  0.88 │       movzwl 0x8062140(%eax,%eax,1),%ecx                                                        
 23.79 │       movzwl %cx,%edi                                                                           
       │             /* Find the longest match, discarding those <= prev_length.  

最后一条指令要复制到第二条指令%ecx,最后一条指令必须等待(暂存管道),直到%cx寄存器中的数据准备就绪为止。该管道停顿阻止了包含循环。

这是某些真正晦涩的“老式” C编程风格的结果。


1

提示可能会使它在多核/超线程CPU上达到另一个速度:(
假设Ubuntu)

sudo apt-get install moreutils

moreutils除其他外还包含“ gnu parallel”-它具有许多选项来帮助使用更多的CPU。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.