`git diff --patience` 和 `git diff --histogram` 有啥区别?
Posted
技术标签:
【中文标题】`git diff --patience` 和 `git diff --histogram` 有啥区别?【英文标题】:What's the difference between `git diff --patience` and `git diff --histogram`?`git diff --patience` 和 `git diff --histogram` 有什么区别? 【发布时间】:2015-11-28 16:14:50 【问题描述】:This earlier question 询问了 4 种不同 Git diff 策略之间的区别,但唯一解释的区别是 myers
和 patience
之间的区别,elsewhere 解释得很好。
histogram
策略如何运作?它与patience
有什么区别? git-diff man page 只是说它“将耐心算法扩展为“支持低出现的常见元素”。其他页面提到它更快,并且来自 JGit,但他们没有解释 它的算法或结果在哪里或如何不同于 patience
。
我在哪里可以找到与patience
算法相关的histogram
算法的描述,其详细程度与Bram Cohen's original description of the patience
algorithm 相同?
(如果只是实现性能的问题,没有任何情况会产生不同的结果,那为什么不直接作为patience
的新后端实现?)
【问题讨论】:
虽然this paper 只比较了两种算法(Myers 和 Histogram),但我认为它可以提供帮助。 【参考方案1】:这个直方图策略是在git 1.7.7 (Sept 2011)中引入的,描述如下(如OP所述)
“
git diff
”学习了一个“--histogram
”选项来使用从jgit窃取的不同差异生成机制,这可能会提供更好的性能。
JGit 包括src/org/eclipse/jgit/diff/HistogramDiff.java
和tst/org/eclipse/jgit/diff/HistogramDiffTest.java
那里的描述相当完整:
直方图差异
Bram Cohen 的耐心差异算法的扩展形式。
此实现是通过使用 4 条规则得出的 Bram Cohen's blog,然后进一步扩展以支持低出现的常见元素。
该算法的基本思想是为序列 A 的每个元素创建一个出现次数的直方图。然后依次考虑序列 B 的每个元素。如果该元素也存在于序列 A 中,并且出现次数较少,则将这些位置视为最长公共子序列 (LCS) 的候选位置。 在 B 的扫描完成后,选择出现次数最少的 LCS 作为分割点。区域围绕 LCS 进行分割,并将算法递归应用于 LCS 之前和之后的部分。
通过始终选择出现次数最少的 LCS 位置,只要两个序列之间存在唯一的公共元素,此算法的行为就与 Bram Cohen 的耐心差异完全一样。 当不存在唯一元素时,将选择出现次数最少的元素。 这提供了比简单地使用标准 Myers 的
O(ND)
算法产生的更具可读性的差异。为防止算法出现
O(N^2)
的运行时间,直方图桶中唯一元素的数量上限由#setMaxChainLength(int)
配置。 如果序列 A 有多个元素散列到同一个散列桶中,则算法将该区域传递给#setFallbackAlgorithm(DiffAlgorithm)
。 如果未配置回退算法,则该区域将作为替换编辑发出。在扫描序列 B 期间,A 中出现超过
#setMaxChainLength(int)
次的任何元素都不会被视为 LCS 匹配位置,即使它在两个序列之间是常见的。这限制了序列 A 中必须考虑找到 LCS 的位置数量,并有助于保持较低的运行时间限制。只要
#setMaxChainLength(int)
是一个很小的常数(比如64),算法在O(N * D)
的时间内运行,其中N
是输入长度的总和,D
是编辑的数量结果EditList
. 如果提供的SequenceComparator
具有良好的哈希函数,则此实现通常优于MyersDiff
,即使其理论运行时间相同。此实现有一个内部限制,阻止它处理超过 268,435,456 (2^28) 个元素的序列
请注意,这种算法是already used for pack_check, back in 2006 (git 1.3),对应于git-verify-pack -v
。是reused for index-pack in git 1.7.7
Commit 8c912ee实际上是把--histogram
引入到diff中:
将 JGit 的 HistogramDiff 算法移植到 C. 粗略数字 (TODO) 显示 它比它的
--patience
表亲以及默认的 Meyers 算法更快。实现已被重新设计为使用结构和指针, 而不是位掩码,从而消除了 JGit 的
2^28
行限制。我们还使用
xdiff
的默认哈希表实现(xdl_hash_bits()
为方便起见,使用XDL_HASHLONG()
)。
commit 8555123 (git 1.7.10, April 2012) 已添加:
8c912ee (teach
--histogram
todiff
, 2011-07-12) 声称直方图差异 比迈尔斯和耐心都快。我们已经合并了一个性能测试框架,所以添加一个 比较在真实“
log -p
”中执行的各种差异任务的测试 工作量。 这确实表明直方图 diff 略优于 Myers,而耐心比其他方法慢得多。
最后commit 07ab4de (git 1.8.2, March 2013)添加
config:引入 diff.algorithm 变量
一些用户或项目比其他用户更喜欢不同的算法,例如对迈尔斯或类似的耐心。 但是,每次使用 diff 时都指定适当的参数是不切实际的。此外,创建别名不能很好地与其他基于 diff 的工具配合使用(例如
git-show
)。因此,需要一个能够设置特定算法的配置变量。 目前,这四个值被接受:
'myers
'(相当于完全不设置config变量), 'minimal
', 'patience
'和 'histogram
'.
Commit 07924d4 同时添加了--diff-algorithm
命令行选项。
正如 OP Stuart P. Bentley 提到的 in the comments:
您可以将 Git 配置为默认使用直方图:
git config --global diff.algorithm histogram
更新:Git 2.12(2017 年第一季度)将淘汰在某些极端情况下会导致灾难性性能问题的“快速哈希”。
参见Jeff King (peff
) 的commit 1f7c926(2016 年 12 月 1 日)。
(由 Junio C Hamano -- gitster
-- 合并于 commit 731490b,2016 年 12 月 19 日)
xdiff
:掉线XDL_FAST_HASH
xdiff
代码对差异两边的每一行进行哈希处理,然后比较这些哈希值以查找重复项。整体性能既取决于我们计算哈希的速度,也取决于我们看到多少哈希冲突。
XDL_FAST_HASH
的想法是加快哈希计算。 但是生成的散列具有更差的碰撞行为。这意味着在某些情况下它会加快 diff(在git.git
上运行“git log -p
”会提高~8%
),但在其他情况下它会减慢速度。 One pathological case saw over a 100x slowdown.可能有一个更好的散列函数涵盖这两个属性,但同时我们最好使用原始散列。普通情况下速度稍慢,但意外的病理情况较少。
注意:“git diff --histogram
”的内存使用模式不好,它有
在 Git 2.19(2018 年第三季度)中重新安排以减少峰值使用量。
参见Stefan Beller (stefanbeller
)commit 79cb2eb、commit 64c4e8b、commit c671d4b、commit 2820985(2018 年 7 月 19 日)。(由 Junio C Hamano -- gitster
-- 合并于 commit 57fbd8e,2018 年 8 月 15 日)
xdiff/xhistogram
:将索引分配移动到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 config --global diff.algorithm histogram
是使用最后一次提交的命令,用于将 Git 配置为默认使用直方图。
@StuartP.Bentley 好点。我已将您的评论包含在答案中以提高知名度。
XDL_FAST_HASH
与这些有什么关系?
@StuartP.Bentley 它被用来尝试和优化 diff historigram 和耐心,如 github.com/git/git/commit/… 中所述。但事与愿违,最近被撤了。以上是关于`git diff --patience` 和 `git diff --histogram` 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章