This earlier question询问了4种不同的Git diff策略之间的差异,但唯一的区别是myers
和patience
之间的区别,这是很好的解释{{3 }}
histogram
策略如何运作?它与patience
的区别是什么? elsewhere仅表示它“将耐心算法扩展为”支持低发生的常见元素“。”其他页面提到它更快,它来自JGit,但它们没有解释其算法或结果在何处或如何与patience
不同。
在哪里可以找到histogram
算法相对于patience
算法的描述,其详细程度与git-diff man page相同?
(如果只是实现性能的问题而没有会产生不同结果的情况,那么为什么它不仅仅被实现为patience
的新后端?)
答案 0 :(得分:55)
此直方图策略在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
说明相当完整:
HistogramDiff
Bram Cohen耐心差异算法的扩展形式。
此实现是通过使用中列出的4条规则得出的 Bram Cohen's blog,然后进一步扩展到支持低发生的共同元素。
该算法的基本思想是为序列A 的每个元素创建出现的直方图。然后依次考虑序列B的每个元素。如果元素也存在于序列A中,并且具有较低的出现次数,则这些位置被视为最长公共子序列(LCS)的候选者。 在B的扫描完成之后,选择具有最低出现次数的LCS作为分割点。该区域围绕LCS分割,算法递归地应用于LCS之前和之后的部分。
通过始终选择具有最低出现次数的LCS位置,只要两个序列之间存在唯一的公共元素,此算法的行为与Bram Cohen的耐心差异完全相同。
如果不存在唯一元素,则选择最低出现元素 这提供了更多可读性差异,而不仅仅是简单地依靠标准迈尔斯'O(ND)
算法会产生。为防止算法具有
O(N^2)
运行时间,直方图存储桶中唯一元素数量的上限由#setMaxChainLength(int)
配置。
如果序列A具有多个散列到同一散列桶中的元素,则算法将该区域传递给#setFallbackAlgorithm(DiffAlgorithm)
。
如果未配置回退算法,则该区域将作为替换编辑发出。在扫描序列B期间,发生超过
#setMaxChainLength(int)
次的A的任何元素从不被认为是LCS匹配位置,即使它在两个序列之间是共同的。这限制了序列A中必须考虑查找LCS的位置数,并有助于保持较低的运行时间限制。只要
#setMaxChainLength(int)
是一个小常量(例如64),算法就会在O(N * D)
时间内运行,其中N
是输入长度和{{1}的总和}是结果D
中的修改次数 如果提供的SequenceComparator
具有良好的散列函数,则此实现通常优于MyersDiff
,即使其理论运行时间相同。此实现具有内部限制,可防止其处理超过268,435,456(2 ^ 28)个元素的序列
请注意,对于EditList
,此类算法为already used for pack_check, back in 2006 (git 1.3)。那是reused for index-pack in git 1.7.7
Commit 8c912ee实际上将git-verify-pack -v
引入了diff:
Port JGit的HistogramDiff算法到C.粗略数字(TODO)显示 它比它的
--histogram
堂兄快,以及默认的迈耶斯算法。该实现已经重新设计为使用结构和指针, 而不是位掩码,因此取消了JGit的
--patience
行限制。我们还使用
2^28
的默认哈希表实现(xdiff
使用xdl_hash_bits()
)以方便使用。
commit 8555123 (git 1.7.10, April 2012)补充道:
8c912ee(教导
XDL_HASHLONG()
至--histogram
,2011-07-12)声称直方图差异 比迈尔斯和耐心都快。我们已经合并了一个性能测试框架,所以添加一个 测试,比较在真实'
diff
'中执行的各种差异任务。 工作量。
这确实表明直方图差异略微超过迈尔斯,而耐心比其他人慢得多。
最后,commit 07ab4de (git 1.8.2, March 2013)添加
config:引入diff.algorithm变量
某些用户或项目偏好其他算法,例如对迈尔斯或类似人士的耐心 但是,每次使用diff时指定适当的参数是不切实际的。此外,创建别名并不能与基于diff(例如
log -p
)的其他工具很好地配合。因此,需要一个能够设置特定算法的配置变量 目前,接受这四个值:
- '
git-show
' (与完全不设置配置变量具有相同的效果),- '
myers
',- '
minimal
'和- '
patience
'
Commit 07924d4同时添加了histogram
命令行选项
由于OP Stuart P. Bentley提及in the comments:
您可以将Git配置为默认情况下使用直方图:
--diff-algorithm
更新:Git 2.12(2017年第一季度)将淘汰"快速哈希"在某些极端情况下,这会带来灾难性的性能问题。
commit 1f7c926见Jeff King (peff
)(2016年12月1日)。
(Junio C Hamano -- gitster
--合并于commit 731490b,2016年12月19日)
git config --global diff.algorithm histogram
:dropxdiff
XDL_FAST_HASH
代码散列差异两边的每一行,然后比较这些散列以查找重复。整体性能取决于我们计算哈希值的速度,以及我们看到的哈希冲突次数。
xdiff
的想法是加快哈希计算 但是生成的哈希值具有更差的碰撞行为。这意味着在某些情况下,它会加速差异(在XDL_FAST_HASH
上运行"git log -p
"使用git.git
提高~8%
,但在其他情况下,它会降低速度。 One pathological case saw over a 100x slowdown可能有更好的哈希函数覆盖这两个属性,但与此同时我们最好使用原始哈希。它在常见情况下稍微慢一点,但它的病理情况较少。
注意:" git diff --histogram
"有一个糟糕的内存使用模式,有
已重新安排以降低峰值使用率,使用Git 2.19(Q3 2018)。
commit 79cb2eb见commit 64c4e8b,commit c671d4b,commit 2820985,Stefan Beller (stefanbeller
)(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
,内存在我们递归之前是空闲的,这样在递归的下一步中重用内存而不是使用新内存这只解决了内存压力,而不是运行时复杂性, 对于上面提到的角落案例来说,这也很糟糕。