我注意到git difftool
非常慢。每次diff调用之间会出现大约1秒.2秒的延迟。
为了对其进行基准测试,我编写了一个自定义的difftool
命令:
#!/bin/sh
echo $0 $1 $2
并将Git配置为在我的~/.gitconfig
中使用此工具
[diff]
tool = mydiff
[difftool "mydiff"]
prompt = false
cmd = "~/mydiff "$LOCAL" "$REMOTE""
我在Git上测试了它来源:
$ git clone https://github.com/git/git.git
$ cd git
$ git rev-parse HEAD
1bc8feaa7cc752fe3b902ccf83ae9332e40921db
$ git diff head~10 --stat --name-only | wc -l
23
当我用259b5e6d33
计时git difftool
时,结果慢得离谱:
$ time git difftool 259b5
mydiff /dev/null Documentation/RelNotes/2.6.3.txt
...
mydiff /tmp/mY2T6l_upload-pack.c upload-pack.c
real 0m10.381s
user 0m1.997s
sys 0m6.667s
通过尝试一个更简单的脚本,它会运行得更快:
$ time git diff --name-only --stat 259b5 | xargs -n1 -I{} sh -c 'git show 259b5:{} > {}.tmp && ~/mydiff {} {}.tmp'
mydiff Documentation/RelNotes/2.6.3.txt Documentation/RelNotes/2.6.3.txt.tmp
mydiff upload-pack.c upload-pack.c.tmp
real 0m1.149s
user 0m0.472s
sys 0m0.821s
我错过了什么?
这是我得到的结果
| Cygwin | Debian | Ubuntu | Method |
| ------ | ------ | ------ | -------- |
| 10.381 | 2.620 | 0.580 | difftool |
| 1.149 | 0.567 | 0.210 | custom |
对于Cygwin
的结果,我测量了在git-difftool
中花费的2.8s和在git-difftool--helper
中花费的7.5s。后者长达98行。我不明白为什么这么慢。
使用msysgit GitHub上的一些技术,我已经缩小了范围。
对于diff中的每个文件,git-difftool--helper
重新运行以下内部命令:
12:44:46.941239 git.c:351 trace: built-in: git 'config' 'diff.tool'
12:44:47.359239 git.c:351 trace: built-in: git 'config' 'difftool.bc.cmd'
12:44:47.933239 git.c:351 trace: built-in: git 'config' '--bool' 'mergetool.prompt'
12:44:48.797239 git.c:351 trace: built-in: git 'config' '--bool' 'difftool.prompt'
12:44:49.696239 git.c:351 trace: built-in: git 'config' 'difftool.bc.cmd'
12:44:50.135239 git.c:351 trace: built-in: git 'config' 'difftool.bc.path'
12:44:50.422239 git.c:351 trace: built-in: git 'config' 'mergetool.bc.path'
12:44:51.060239 git.c:351 trace: built-in: git 'config' 'difftool.bc.cmd'
12:44:51.452239 git.c:351 trace: built-in: git 'config' 'difftool.bc.cmd'
请注意,在这种特殊情况下,执行这些操作大约需要4.5秒。在我的日志中,这是一个非常一致的模式。
还要注意,其中一些是重复的——git config difftool.bc.cmd
被调用了4次!
现在,可能的补救措施:
- 通过将所有与diff相关的部分移到
.gitconfig
文件的顶部,我将这些命令的执行时间缩短了一半。认真地它仍然很明显,但现在大约是2秒,而不是4.5秒 - 确保您的程序文件下的Git文件夹和您的用户配置文件(
.gitconfig
居住的地方)都被排除在实时病毒扫描之外 - 从根本上讲,Git需要在解析和获取配置值方面更加高效。理想情况下,它会缓存这些内容,而不是每次在循环中从配置中重新请求(和重新分析…)。甚至可能为整个命令执行而缓存
git difftool
使用Git 2.13(2017年第二季度)应该会稍微快一点
参见Jeff Hostetler提交的d12a8cf(2017年4月14日)(jeffhostetler
)
(由Junio C Hamano合并——gitster
——提交8868ba1,2017年4月24日)
unpack-trees
:在签出过程中避免重复的ODB查找
(ODB:对象数据库)
教导
traverse_trees_recursive()
在两个目录都引用相同OID时不要执行冗余ODB查找。在像
read-tree
和checkout
这样的操作中,当提交之间的差异相对较小时,可能会有许多对等目录具有相同的OID
在这些情况下,我们可以避免对同一OID多次命中ODB。这个补丁处理n=2和n=3的情况,并简单地复制数据,而不是重复fill_tree_descriptor()。
================
在Windows repo(500K树、3.1M文件、450MB索引)上,当在具有单个文件差异的2个提交之间循环时,这将总时间减少了0.75秒。
(avg) before: 22.699
(avg) after: 21.955
===============
经过一些调查,我有证据表明,糟糕的性能与来自不同域的用户拥有的文件有关。具体来说,我得出了以下结论:
- 我在一个拥有多个域和数千名用户的公司环境中工作
- 由于组织的变化,每个用户可能只在过渡阶段被保留在两个域中,一个是他或她的主域,另一个是第二域。当通过Windows GUI更改对象所有权时,每个用户都会出现两次,其中一个用户必须转到扩展用户选择,以识别分配给特定域的用户
- 启用acl的cygwin将"其他域"文件用户显示为"<domain>+<username>"。主域本身只是"<用户名>"。不带acl的Cygwin在这两种情况下都只显示"<username>"。这可能相当令人困惑,因为cygwin识别的文件权限和所有权表示写权限,而用户实际上没有写权限
- 属于"其他域"自身的文件可由我的"此域"自身写入,因此域分配在很大程度上是透明的
- 我们版本控制系统中的一个大型源代码树(也镜像在git repo中)有数千个文件归"其他域自身"所有。这似乎是导致文件操作缓慢的原因。将所有权更改为"主域自身"解决了git和其他文件访问的速度问题
我必须假设,为其他域中的用户获取文件权限很慢,而且由于某些原因没有缓存(总是同一个用户)。
下面这篇文章的其余部分是我最初发布的内容。我听之任之。
对我来说(在一家拥有多个地理分布的Windows域的大公司工作),罪魁祸首是cygwin默认使用Windows acl。考虑域中所有已知用户的此请求:
$ time (mkpasswd -D | wc -l)
45183
real 27m55,340s
user 0m1,637s
sys 0m0,123s
修复(1)(2)只是安装带有noacl
的NTFS文件系统的一个简单问题,即我的/etc/fstab
包含行
none / cygdrive binary,posix=0,user,noacl 0 0
(同时消除恼人的cygdrive
前缀)。
我忍不住想象,cygwin/msys(与此行为相同,只是Windows git安装默认安装noacl
,可能是因为这个原因)会对它所接触的每个文件执行域服务器查询,而不会缓存结果。
这一变化是在2015年左右推出的,cygwin为2.4或2.5。来自2.4的发行说明:
为了适应标准的Windows ACL,ACL中所有者和所有其他用户的POSIX权限是使用Windows AuthZ API计算的。在某些情况下,这可能会显著减慢POSIX权限的计算速度[…](我强调)。
noacl
选项将启动BeyondCompre(或回显字符串)的时间从25秒减少到1秒。完全无法理解为什么即使使用acl,同一文件上的简单git diff
也非常快,因为我天真地认为所需的信息和所需的FS操作是相同的。
我现在来看看cygserver
,它可能会通过缓存来改进。
更新:不幸的是,cygserver并没有改善这种情况。
(1) git的修复程序。mkpasswd
不受影响。
(2) 我还没有理解和测试git(以及我们也通过cygwin访问的ClearCase视图)对文件权限和所有权的影响。我的直觉是,人们希望尽可能地忠于Windows语义(这意味着noacl
可能会遇到问题)。
(3) cygwin文档讨论了不缓存查询结果的场景。其中一个由一系列cygwin进程组成,这些进程不是从常见的cygwin祖先(如bash)派生的,而是从cmd
等windows程序派生的。我必须假设Windows为本机程序提供了缓存机制,否则Windows系统在这种公司环境中将不可用。由于某些原因,cygwin没有使用它。