如何排除异常缓慢的git差异?
我最近克隆了一个远程repo,其中一些如何排除异常缓慢的git差异?,git,git-diff,Git,Git Diff,我最近克隆了一个远程repo,其中一些git命令运行极其缓慢。例如,跑步 git diff --quiet …需要约40秒。(值得一提的是,回购协议是干净的。我使用的是git2.20.1版。) 在试图找出导致这种迟钝的原因时,我遇到了一些取消它的程序,尽管我不知道为什么 在这些过程中,我发现的最简单/最快的一个是:(从一个新克隆的回购实例开始)创建一个分支master,并将其签出。在此之后,如果我再次签出master,现在git diff--quiet会很快完成(低于50ms) 下面是一个交互
git
命令运行极其缓慢。例如,跑步
git diff --quiet
…需要约40秒。(值得一提的是,回购协议是干净的。我使用的是git
2.20.1版。)
在试图找出导致这种迟钝的原因时,我遇到了一些取消它的程序,尽管我不知道为什么
在这些过程中,我发现的最简单/最快的一个是:(从一个新克隆的回购实例开始)创建一个分支master
,并将其签出。在此之后,如果我再次签出master
,现在git diff--quiet
会很快完成(低于50ms)
下面是一个交互示例,显示了各种操作的计时信息1:
正如我已经强调的,这只是“修复”回购协议的几种可能程序之一,它们对我来说都同样神秘。这只是我发现的最简单/最快的一个
上述计时顺序是非常可重复的(即,每次运行特定顺序时,我得到的计时大致相同,如图所示)
然而,它对看似微小的变化非常敏感。例如,如果我替换git分支伏都教;git checkout VOODOO
使用git checkout-b VOODOO
,随后的计时模式会发生根本性的变化:
rm -rf ./"$REPONAME" # 0.015 s
git clone "$URL" # 45.312 s
cd ./"$REPONAME" # 0.007 s
git diff --quiet # 46.145 s
git checkout -b VOODOO # 42.363 s
git diff --quiet # 41.180 s
git checkout master # 47.345 s
git diff --quiet # 0.018 s
我想知道发生了什么事。我怎样才能进一步解决这个问题
是否有一种永久(“可承诺”)的方式来“确定”回购协议?(我所说的“修复”是指:摆脱git diff--quiet,git checkout…
等的长时间延迟。)
(顺便说一句,git gc
无法修复回购协议,即使是暂时的;我试过了。)
我认为最终“修复”回购协议的是,git
开始构建和缓存一些辅助数据结构,使其能够高效地执行某些操作。如果这个假设是正确的,那么我的问题可以重新表述为:什么是导致git
构建这种辅助数据结构的最直接的方法
编辑:可能有助于说明上述情况的另外一点信息是,该回购协议包含一个超大(1GB)文件。(这解释了
git clone
步骤的慢度。我不知道这是否与git diff--quiet
等的慢度有关,如果是,怎么做。)
1不用说,我将分支命名为
VOODOO
,以反映我对正在发生的事情的无知。首先检查Git 2.27,甚至即将推出的2.28(2020年第3季度)是否存在问题
我会用它来衡量任何绩效。(as)
使用Git2.28(2020年第3季度),在具有太多stat不匹配路径的工作树中“diff--quiet
”期间的内存使用量已经大大减少
它的补丁描述说明了一个用例,其中“diff--quiet
”可能很慢:
参见(2020年6月1日)作者。(于2020年6月18日合并) :放弃统计不匹配对中的blob数据 报告人:Jan Christoph Uhde
签字人:杰夫·金 在对工作树执行树级差异时,我们可能会发现我们的索引统计信息是脏的,因此我们将文件对排队等待稍后检查。
如果实际内容没有改变,我们称之为统计不匹配;统计信息已经过时,但没有实际的差异 通常
diffcore\u std()
会通过diffcore\u skip\u stat\u unmatch()
检测并删除这些相同的文件对
但是,当使用“--quiet
”时,我们希望在看到任何更改时立即停止差异,因此我们会立即在diff_change()
中检查统计不匹配
该检查可能要求我们实际将文件内容加载到一对diff\u filespec
如果我们发现这一对不是统计数据不匹配,那就没什么大不了的;我们可能会在以后加载内容以生成补丁、进行重命名检测等,因此我们希望保留它。
但如果这是一个统计数据不匹配,那么我们将不再使用该数据;关键是我们要放弃这一对。但是,我们从未释放分配的
diff_filespec
数据
在大多数情况下,保存这些数据不是问题。我们不希望有太多的统计数据不匹配条目,因为我们使用的是--quiet
,不管怎样,只要我们看到这样一个真正的变化,我们就会立即退出
然而,在一些极端情况下,这会产生很大的影响:
而且,由于操作系统可能会限制地图的总数,因此我们可以在大型存储库中与之相冲突。例如:
$ cd linux
$ git ls-files | wc -l
67959
$ sysctl vm.max_map_count
vm.max_map_count = 65530
$ git ls-files | xargs touch ;# everything is stat-dirty!
$ git diff --quiet
fatal: mmap failed: Cannot allocate memory
sed-i
”或类似的脚本,这是可能的
此修补程序之后,上述程序将正确退出,代码为0
再次在
linux.git
的克隆中运行:
$ git ls-files | head -n 10000 | xargs touch
$ git diff --quiet
--quiet
”stat unmatch check indiff\u changes
期间拾取的所有diff\u filespec
数据来解决此问题
以后没有人会需要这些数据,所以保留这些数据毫无意义。有几件事需要注意:
- 我们可以完全跳过排队,这在理论上可以节省一点工作。但是没有什么可以保存的,因为我们需要一个
来馈送到diff\u文件对
。diff\u filespec\u check\u stat\u unmatch()
由于我们缓存了
检查的结果,因此稍后调用stat unmatch
diffcore\u skip\u sta
$ git ls-files | head -n 10000 | xargs touch $ git diff --quiet
Test v2.29.0-rc1 this tree ----------------------------------------------------------------------------------------------------------------- 7519.2: status (fsmonitor=.git/hooks/fsmonitor-watchman) 1.46(0.82+0.64) 1.47(0.83+0.62) +0.7% 7519.3: status -uno (fsmonitor=.git/hooks/fsmonitor-watchman) 0.16(0.12+0.04) 0.17(0.12+0.05) +6.3% 7519.4: status -uall (fsmonitor=.git/hooks/fsmonitor-watchman) 1.36(0.73+0.62) 1.37(0.76+0.60) +0.7% 7519.5: diff (fsmonitor=.git/hooks/fsmonitor-watchman) 0.85(0.22+0.63) 0.14(0.10+0.05) -83.5% 7519.6: diff -- 0_files (fsmonitor=.git/hooks/fsmonitor-watchman) 0.12(0.08+0.05) 0.13(0.11+0.02) +8.3% 7519.7: diff -- 10_files (fsmonitor=.git/hooks/fsmonitor-watchman) 0.12(0.08+0.04) 0.13(0.09+0.04) +8.3% 7519.8: diff -- 100_files (fsmonitor=.git/hooks/fsmonitor-watchman) 0.12(0.07+0.05) 0.13(0.07+0.06) +8.3% 7519.9: diff -- 1000_files (fsmonitor=.git/hooks/fsmonitor-watchman) 0.12(0.09+0.04) 0.13(0.08+0.05) +8.3% 7519.10: diff -- 10000_files (fsmonitor=.git/hooks/fsmonitor-watchman) 0.14(0.09+0.05) 0.13(0.10+0.03) -7.1% 7519.12: status (fsmonitor=) 1.67(0.93+1.49) 1.67(0.99+1.42) +0.0% 7519.13: status -uno (fsmonitor=) 0.37(0.30+0.82) 0.37(0.33+0.79) +0.0% 7519.14: status -uall (fsmonitor=) 1.58(0.97+1.35) 1.57(0.86+1.45) -0.6% 7519.15: diff (fsmonitor=) 0.34(0.28+0.83) 0.34(0.27+0.83) +0.0% 7519.16: diff -- 0_files (fsmonitor=) 0.09(0.06+0.04) 0.09(0.08+0.02) +0.0% 7519.17: diff -- 10_files (fsmonitor=) 0.09(0.07+0.03) 0.09(0.06+0.05) +0.0% 7519.18: diff -- 100_files (fsmonitor=) 0.09(0.06+0.04) 0.09(0.06+0.04) +0.0% 7519.19: diff -- 1000_files (fsmonitor=) 0.09(0.06+0.04) 0.09(0.05+0.05) +0.0% 7519.20: diff -- 10000_files (fsmonitor=) 0.10(0.08+0.04) 0.10(0.06+0.05) +0.0%
(gdb) bt [simplified]