尽管 CPU 和硬盘驱动器的性能没有达到最大值,为什么 gzip 很慢?

nh2*_*nh2 16 performance hard-drive gzip buffer

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

gzip file1.json
Run Code Online (Sandbox Code Playgroud)

这占用了一个完整的 CPU 内核,一切正常。

它处理大约 25 MB/s(签入atop),我的硬盘驱动器可以读取 125 MB/s 并且我有 3 个空闲处理器内核,所以我希望在并行压缩多个文件时获得加速。所以我在其他终端运行:

gzip file2.json
gzip file3.json
gzip file4.json
Run Code Online (Sandbox Code Playgroud)

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

为什么以及如何解决?

nh2*_*nh2 19

我发现了:

原因是gzip操作(就 CPU 速度与这些天的高清搜索速度而言)极低的缓冲区大小

它从输入文件中读取几 KB,对其进行压缩,然后将其刷新到输出文件中。鉴于这需要硬盘驱动器寻道,每秒只能完成几个操作。

我的表现没有扩大的原因是因为已经有人gzip在疯狂地寻找。


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

buffer -s 100000 -m 10000000 -p 100 < file1.json | gzip > file1.json.gz
Run Code Online (Sandbox Code Playgroud)

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

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

并行运行其中的四个,我可以获得 4 * 25 MB/s 的吞吐量,正如预期的那样。


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

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

  • bzip2 由于其更强/更多 CPU 密集型压缩,仅处理 2 MB/s
  • lzop 似乎允许更大的缓冲区:每个内核 70 MB/s,2 个内核可以在不过度搜索的情况下最大化我的 HD


小智 5

在阅读了 MIT OpenCourseware for 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.  
Run Code Online (Sandbox Code Playgroud)

倒数第二条指令正在复制,%ecx最后一条指令必须等待(暂停流水线),直到%cx寄存器准备好使用数据。这个管道停顿阻止了包含循环。

这是一些非常晦涩的“老派”C 编程风格的结果。