What's the difference between `git diff --patience` and `git diff --histogram`?

This earlier question asked for the differences between 4 different Git diff strategies, but the only difference that was explained was the difference between myers and patience, which is pretty well explained elsewhere.

How does the histogram strategy work? What differentiates it from patience? The git-diff man page only says that it "extends the patience algorithm to "support low-occurrence common elements"." Other pages mention that it's faster, and that it comes from JGit, but they don't explain where or how its algorithm or results will differ from patience.

Where can I find a description of the histogram algorithm relative to the patience algorithm, with the same level of detail as Bram Cohen's original description of the patience algorithm?

(If it's just a matter of implementation performance with no case that will produce different results, why wasn't it just implemented as a new backend for patience?)

25837 次浏览

这个直方图策略是在 Git 1.7.7(2011年9月)中引入的,具有以下描述(如 OP 所提到的)

git diff”学会了一个“ --histogram”选项,使用从 JGIT偷来的不同的差异生成机器,这可能会提供更好的性能。

JGit 包括 src/org/eclipse/jgit/diff/HistogramDiff.javatst/org/eclipse/jgit/diff/HistogramDiffTest.java

书中的描述相当完整:

直方图 Diff

布拉姆 · 科恩耐心差分算法的扩展形式。

这个实现是通过使用 Bram Cohen 的博客中概述的4个规则得到的,然后进一步扩展以支持低出现率的公共元素。

该算法的基本思想是 为序列 A 的每个元素创建出现的直方图。然后依次考虑序列 B 的每个元素。如果元素也存在于序列 A 中,并且出现次数较少,则将这些位置视为最长公共子序列(LCS)的候选项。
在 B 扫描完成后,选择出现次数最少的 LCS 作为分割点。该区域围绕 LCS 进行分割,算法递归地应用于 LCS 之前和之后的区域。

通过总是选择出现次数最少的 LCS 位置,该算法的行为与 Bram Cohen 的耐心差异完全相同,只要两个序列之间有一个独特的公共元素可用。
当不存在唯一元素时,将选择最低出现元素,而不是
. < br/> 这比简单地依靠迈尔斯的 O(ND)算法产生的标准差异提供了更多的可读性。

为了防止算法具有 O(N^2)运行时间,直方图桶中唯一元素数的上限由 #setMaxChainLength(int)配置。
如果序列 A 有多于这个数量的元素散列到同一个散列桶中,则算法将该区域传递给 #setFallbackAlgorithm(DiffAlgorithm)
如果未配置回退算法,则该区域作为替换编辑发出。

在序列 B 的扫描过程中,任何出现次数超过 #setMaxChainLength(int)的 A 元素都不会被考虑用于 LCS 匹配位置,即使它在两个序列之间是常见的。这限制了序列 A 中必须考虑的位置数,以便找到 LCS,并有助于保持较低的运行时间限制。

只要 #setMaxChainLength(int)是一个小常数(比如64) ,算法就在 O(N * D)时间内运行,其中 N是输入长度的总和,而 D是结果 EditList中的编辑次数。
如果提供的 SequenceComparator具有良好的散列函数,那么这个实现的性能通常优于 MyersDiff,即使它的理论运行时间是相同的。

这个实现有一个内部限制,它不能处理包含超过268,435,456(2 ^ 28)个元素的序列


注意,这种算法是 在2006年已经用于 pack _ check (git 1.3),对于 git-verify-pack -v在 git1.7.7中重用于 index-pack


Commit 8c912ee 实际上引入了 --histogram来区分:

端口 JGit 的 HistogramDiff 算法转到 C。粗糙数(TODO)显示 它比它的 --patience表亲还要快,还有默认的 Meyers 算法。

实现被重新设计为 < strong > use structs 和指针, 取代了位掩码,从而消除了 JGit 的 2^28行限制 .

我们还使用 xdiff的默认哈希表实现(xdl_hash_bits() (XDL_HASHLONG()).

提交8555123(git 1.7.10,2012年4月) 补充道:

8c912ee (教 --histogramdiff,2011-07-12)声称直方图差异 比迈尔斯和耐心都要快。

我们已经合并了一个性能测试框架,所以添加一个 比较在真正的“ log -p”中执行的各种不同任务的测试 工作量。
这确实表明,直方图差异略胜迈尔斯,而耐心是远远慢于其他人。

最后,提交07ab4de (git 1.8.2,March 2013)加上

引入 diff.algorithm变量

一些用户或项目喜欢不同的算法胜过其他的,例如耐心胜过迈尔斯或类似的。
但是,在每次使用 diff 时指定适当的参数是不切实际的。此外,创建别名并不能很好地与其他基于 diff 的工具(例如 git-show)一起工作。

因此,需要一个能够设置特定算法的配置变量。
目前,这四个价值观被接受:

  • myers’(与根本不设置配置变量具有相同的效果) ,
  • minimal
  • patience」及「
  • histogram”。

提交07924d4 并发添加 --diff-algorithm命令行选项。
正如 Stuart P. Bentley提到的 在评论中:

您可以将 Git 配置为默认情况下使用柱状图 ,具体如下:

git config --global diff.algorithm histogram

更新: Git 2.12(2017年第一季度)将淘汰在某些角落情况下出现灾难性性能问题的“快速散列”。

提交1f7c926(2016年12月1日) by 杰夫 · 金(peff)(由 朱尼奥 · C · 哈马诺 gitster于2016年12月19日在 提交731490b合并)

放下 XDL_FAST_HASH

xdiff代码对 diff 两边的每一行进行散列,然后比较这些散列找到重复的 。总体性能既取决于我们计算散列的速度,也取决于我们看到的散列冲突的数量。

XDL_FAST_HASH的思想是加速哈希计算。
但是生成的散列具有更差的碰撞行为。这意味着在某些情况下,它的速度会有所不同(在 git.git上运行“ git log -p”时,~8%会提高速度) ,但是在其他情况下,它会降低速度。一个病理病例出现了超过100倍的减速.

可能有一个更好的散列函数,涵盖了这两个属性,但同时,我们最好使用原始的散列。在普通病例中,这个过程稍微慢一些,但是它有较少的令人惊讶的病理病例。


注意: “ git diff --histogram”有一个糟糕的内存使用模式 Git 2.19(2018年第三季度)重新安排,以减少高峰使用。

提交79cb2eb第六季,第8集提交 c671d4b2820985(2018年7月19日) by 斯蒂芬 · 贝勒(stefanbeller)
(由 朱尼奥 · C · 哈马诺 gitster于2018年8月15日在 犯下57fbd8e合并)

将索引分配移动到 find_lcs

这修复了大量递归时的内存问题,这个问题可以复制为

seq 1   100000 >one
seq 1 4 100000 >two
git diff --no-index --histogram one two

在这个补丁之前,histogram_diff会递归地调用它自己 调用 free_index,这意味着在 递归,然后才释放。

通过将内存分配(及其空闲调用)移动到 find_lcs,在递归之前内存被释放,这样在递归的下一步中内存被重用,而不是使用新内存。

这只解决了内存压力,而不是运行时复杂性, 对于上面提到的角落案例来说,这也是很糟糕的。


注意: 耐心和直方图算法有内存泄漏,固定与 Git 2.36(Q22022)

承诺43ad3af犯下4a37b80提交61f8839提交9df0fc3(2022年2月16日) by 菲利普 · 伍德(phillipwood)
(由 朱尼奥 · C · 哈马诺 gitster犯罪合并,2022年3月9日)

修复内存泄漏

报道: Junio C Hamano
签名: 菲利普 · 伍德

虽然耐心和直方图算法初始化的环境,他们不释放它,如果有一个错误。
与 Myers 算法相比,环境是在 xdl_do_diff()中初始化的,如果出现错误,它将被释放。
通过始终在 xdl_do_diff()中初始化环境并在发生错误时将其释放来解决这个问题。