【问题标题】:Git difftool ridiculously slow in Cygwin/MinGWCygwin/MinGW 中的 Git difftool 慢得离谱
【发布时间】:2015-12-01 19:28:12
【问题描述】:

我注意到git difftool 非常慢。每次差异调用之间会出现大约 1..2 秒的延迟。

为了对其进行基准测试,我编写了一个自定义 difftool 命令:

#!/bin/sh
echo $0 $1 $2

并在我的~/.gitconfig中配置Git使用这个工具

[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

当我将git difftool259b5e6d33 计时时,结果慢得离谱:

$ 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.8 秒和在git-difftool--helper 中花费的7.5 秒。后者有 98 行长。我不明白为什么这么慢。

【问题讨论】:

  • 嗨。我面临着同样的问题。你有没有得到关于为什么 git difftool 这么慢的答案?
  • 不,我还没有找到任何解决方案。
  • 嗨。我想你可能会对这个问题在 Git 2.8.1 for Windows 中得到修复感兴趣。请参阅github.com/git-for-windows/git/issues/711
  • 我在 mingw64 中看到与 Git 2.8.1 完全相同的行为。另外,@jeyoung 您链接的问题是 vim 集成。也许是不同的问题?

标签: git cygwin mingw benchmarking git-difftool


【解决方案1】:

使用on the 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 次!

现在,可能的补救措施:

  • 我通过将所有与差异相关的部分移动到我的.gitconfig 文件的顶部,将这些命令的执行时间减半。严重地。它仍然很明显,但现在大约是 2 秒而不是 4.5 秒。
  • 确保从实时病毒扫描中排除 Program Files 下的 Git 文件夹和用户配置文件(.gitconfig 所在的位置)。
  • 从根本上说,Git 需要更高效地解析和获取配置值。理想情况下,它会缓存这些,而不是每次循环从配置中重新请求(和重新解析......)。甚至可能为整个命令执行而缓存。

【讨论】:

    【解决方案2】:

    git difftool 使用 Git 2.13(2017 年第二季度)应该会稍微快一点
    请参阅 Jeff Hostetler (jeffhostetler)commit d12a8cf(2017 年 4 月 14 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit 8868ba1,2017 年 4 月 24 日)

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

    (ODB:对象数据库)

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

    read-treecheckout等操作中,当提交之间的差异相对较小时,可能会有许多具有相同OID的对等目录。
    在这些情况下,我们可以避免多次访问同一 OID 的 ODB。

    此补丁处理 n=2 和 n=3 的情况,并简单地复制数据,而不是重复 fill_tree_descriptor()。

    ================
    

    在 Windows 存储库(500K 树、3.1M 文件、450MB 索引)上,当在 2 次提交之间循环且单个文件不同时,这将总时间减少了 0.75 秒。

    (avg) before: 22.699
    (avg) after:  21.955
    ===============
    

    【讨论】:

      【解决方案3】:

      经过一些调查,我有证据表明性能不佳与来自不同域的用户拥有的文件有关。具体来说,我得出以下结论:

      • 我在一个拥有多个域和数千名用户的公司环境中工作。
      • 由于组织变化,每个用户可能仅在过渡阶段保留在两个域中,即他或她的主域以及第二个域。通过 Windows GUI 更改对象所有权时,每个用户会出现两次,并且必须转到扩展用户选择以识别分配给特定域的用户。
      • cygwin 启用 acl 将“其他域”文件用户显示为“+”。主域自身就是“”。 Cygwin without acl 在这两种情况下都只显示“”。这可能会造成相当混乱,因为 cygwin 识别的文件权限和所有权将指示写权限,而用户实际上没有。
      • 属于“其他域”自身的文件可由“本域”自身写入,因此域分配在很大程度上是透明的。
      • 我们的版本控制系统(也反映在 git 存储库中)的一个大型源代码树包含“其他域自身”拥有的数千个文件。这似乎导致文件操作缓慢。将所有权更改为“主域自身”解决了 git 和其他文件访问的速度问题。

      我必须假设为其他域中的用户获取文件权限很慢,并且由于某种原因没有缓存(它始终是同一个用户)。

      以下文章的其余部分是我最初发布的内容。我让它站着。


      对我(在一家拥有多个地理分布的 Windows 域的大公司工作)来说,罪魁祸首是 cygwin uses 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 一起引入的。来自release notes for 2.4:

      为了适应标准 Windows ACL,所有者和 ACL 中所有其他用户的 POSIX 权限是使用 Windows AuthZ API 计算的。这可能会在某些情况下显着减慢 POSIX 权限的计算 [...](我强调)。

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

      我现在会查看cygserver,它可能会通过缓存来改进。

      更新:很遗憾,cygserver 并没有改善这种情况。


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

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

      (3) cygwin documentation 讨论不缓存查询结果的场景。一个由一系列 cygwin 进程组成,这些进程不是从共同的 cygwin 祖先(如 bash)产生的,而是从像 cmd 这样的 Windows 程序产生的。我必须假设 Windows 为本地程序提供了缓存机制,否则 Windows 系统将无法在这种企业环境中使用。由于某种原因 cygwin 不使用它。

      【讨论】:

      • 很好的反馈,比我的回答更准确。 +1
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      • 2014-09-05
      • 1970-01-01
      • 1970-01-01
      • 2013-03-23
      • 1970-01-01
      相关资源
      最近更新 更多