我有一些 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%,而我的 HD 仍然只能以 25 MB/s 的速度读取。
为什么以及怎样解决?
答案1
我发现了:
原因是gzip
(就当今的 CPU 速度与 HD 寻道速度而言)极低的缓冲区大小。
它从输入文件中读取几 KB,压缩它,然后刷新到输出文件。考虑到这需要硬盘寻道,每秒只能执行几个操作。
我的表现没能扩大的原因是因为已经有一个人gzip
在疯狂地寻求。
我通过使用 unix 解决了这个问题buffer
公用事业:
buffer -s 100000 -m 10000000 -p 100 < file1.json | gzip > file1.json.gz
通过在将大量输入发送到 gzip 之前对其进行缓冲,可以显著减少小搜索的数量。选项如下:
-s
并-m
指定缓冲区的大小(I相信单位是 KB,但不确定)-p 100
确保缓冲区 100% 填满后,数据才会传递给 gzip
并行运行四个,我可以获得预期的 4 * 25 MB/s 吞吐量。
我仍然想知道为什么 gzip 不允许增加缓冲区大小——这样,如果在旋转磁盘上运行它就没用了。
编辑:我尝试了更多压缩程序的行为:
bzip2
由于压缩能力更强/更耗 CPU,因此处理速度仅为 2 MB/slzop
似乎允许更大的缓冲区:每核 70 MB/s,2 个核心可以最大化我的 HD 而不会过度寻道
答案2
在查看了 MIT 开放式课程 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 编程风格的结果。
答案3
一个可能使其在多核/超线程 CPU 上的速度再上一个台阶的技巧:
(假设是 Ubuntu)
sudo apt-get 安装 moreutils
moreutils 包含“gnu parallel”等 - 它有很多选项可以帮助您更多地利用 CPU。