为什么 GNU diff 占用这么多内存?

为什么 GNU diff 占用这么多内存?

有足够多的问题询问如何区分大文件,因为diff无法处理它们。

我想知道为什么GNU diff 无法处理它们。

我做了一个小实验。我比较了两个相同的数据集,如下所示

$ time /usr/bin/diff -u <(cat file1) <(cat file2) > /tmp/memoryhog
^C

real    5m6.478s
user    0m0.540s
sys     0m19.184s

这是我取消作业时 top 显示的内容:

差异内存使用情况

3  PID  %MEM    VIRT   SWAP    RES   CODE    DATA    SHR nMaj nDRT S  PR  NI  %CPU COMMAND
 19087  30.0   16.0g          9.4g   0.2m   16.0g   2.0m           R  20       0.5 /usr/bin/diff -u /dev/fd/63 /dev/fd/62

正如预期的那样,输出是空的:

$ stat -c '%s' /tmp/memoryhog 
0

(它们实际上不是文件而是数据库结果,我忘了追踪diff当时实际消耗了多少字节 - 估计每个管道文件消耗 30-60GiB。)

但那里到底发生什么事了?

diff当它甚至不需要跟踪单个字节的变化时,是否会分配大量内存?

我只能假设这部分是因为必须跟踪行数,但对于这项任务来说,分配 16GiB 虚拟内存似乎有点多!

diff认为为什么需要那么多内存?还是只是内存处理不好?

我已经尝试使 diff 尽可能保持“无状态”或无上下文,仅使用-u,但我找不到任何不跟踪行号或以其他方式进一步改善情况的选项。

(该选项--speed-large-files实际上是一个虚假参数,它没有在代码中实现,所以请不要建议那个。)

编辑:纠正我自己的代码检查的错误结果,我发现这个隐藏在bug-diffutils ML中:

当您传递时--speed-large-filessrc/diff.c设置speed_large_files为 true。然后,src/analyze.c设置ctxt.heuristic为 true。然后,gnulib/lib/diffseq.h函数diag()应用您的启发式方法。

答案1

我相信我找到了这种行为的原因。
似乎diff总是将整个文件读入内存。
老实说,我对此感到很惊讶。我没想到这是基于行的工具所必须的,但显然是的。

此信息基于此处的错误报告:https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21665

Unfortunately I have found that diff reads the entire input files into
memory, leading to "/usr/bin/diff: memory exhausted" messages [...]

回复如下:

> Would you be open to patches that enable diffing large files by using
> mmap?

I doubt whether that would help that much, as it still needs to construct 
information about each line, and that information consumes memory too.  Doing 
this in secondary storage would be a bear.  In practice when I've run into this 
problem, I've either gotten a bigger machine or made my input lines shorter. 
Preferably the former.

最后

As Paul responded [...], using mmap seems
unlikely to help much, but if you write the patch and demonstrate that
it does make a difference, we'll be very interested, and I will
happily reopen the issue.

For now, I'm marking this as notabug and closing it.

在这种情况下,GNU diff 似乎在大文件处理方面会受到限制,除非有人找到办法克服错误报告中指出的困难,或者实现一个不同工作的 diff 工具。

如果有人提出更好或更深入的答案,例如通过代码审查,我会很乐意接受它。


PS:到目前为止,我自己使用基于 Python 的逐行阅读器只取得了中等程度的成功 差异库,其目的是查找差异,而不是创建可修补的差异文件;它可以读取几个 GiB,但在某些时候似乎“不同步”,之后会报告实际上相同的行的差异。当然,它很慢。如果我能在某个时候建立一个可行的解决方案,我会发布源代码。

相关内容