有足够多的问题询问如何区分大文件,因为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-files
,src/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,但在某些时候似乎“不同步”,之后会报告实际上相同的行的差异。当然,它很慢。如果我能在某个时候建立一个可行的解决方案,我会发布源代码。