Cygwin / MinGW的Git difftool可笑地变慢了

我注意到, 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和用于git-difftool--helper 。 后者是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 2.13(2017年第2季度), git difftool应该稍快一些,
    参见Jeff Hostetler( jeffhostetler )的提交d12a8cf(2017年4月14日)。
    (由Junio C gitster合并 - gitster - 2017年4月24日承诺8868ba1)

    unpack-trees :在结帐时避免重复的ODB查找

    (ODB:Object DataBase)

    当两个目录引用相同的OID时,教导traverse_trees_recursive()不要执行冗余的ODB查找。

    在诸如read-treecheckout ,当提交之间的差异相对较小时,可能会有许多具有相同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将“其他域”文件用户显示为“<域> + <用户名>”。 主域自我只是“<用户名>”。 没有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,使用Windows AuthZ API计算ACL中所有者和所有其他用户的POSIX权限。 这可能会在某些情况下显着减慢POSIX权限的计算 (我强调)。

    noacl选项减少了启动BeyondCompare(或者将字符串回显为25秒)到1的时间。完全无法理解为什么简单的git diff在同一个文件中的速度非常快,即使是acl,因为我会天真地认为所需的信息以及所需的FS操作是相同的。

    我现在会检查一下cygserver ,它可以通过缓存来改善事情。

    更新:不幸的是,cygserver并没有改善这种情况。


    (1)git的修复。 mkpasswd不受影响。

    (2)我还没有理解和测试git(以及我们通过cygwin访问的ClearCase视图)对文件权限和所有权的影响。 我的直觉是,人们希望尽可能地遵守Windows语义(意思是noacl可能会遇到问题)。

    (3)cygwin文档讨论了查询结果没有被缓存的场景。 一个由cygwin进程序列组成,这些进程不是由一个常见的cygwin祖先(如bash)产生的,而是从像cmd这样的windows程序产生的。 我必须假定Windows为本机程序提供了一个缓存机制,否则Windows系统将无法在此公司环境中使用。 出于某种原因,cygwin不使用它。

    链接地址: http://www.djcxy.com/p/89085.html

    上一篇: Git difftool ridiculously slow in Cygwin/MinGW

    下一篇: How to use @Configuration and @EnableScheduling together with Spring Batch