Git索引和提交非常慢

qojgxg4l  于 2023-01-24  发布在  Git
关注(0)|答案(1)|浏览(228)

我有一个本地git仓库,使用git add file1 file2 file3...将我的修改添加到git索引中,然后我使用普通的git commit,每个命令大约需要3到6秒,我的仓库有大约150.000次提交。
我已经执行了git gc,因为我认为它会执行一些垃圾收集。SSD是相当快的。我想知道我可以在git中拧哪些螺丝来加速这两个命令的执行。有什么建议吗?

git version 2.21.0 (Apple Git-122.2)

我的系统是MacOS 10.14.6的Mac Pro。我使用APFS的SSD。没有安装杀毒软件(或任何其他干扰扫描软件)。

2w3rbyxf

2w3rbyxf1#

首先,更新到最新的Git 2.25:每个新版本都解决了性能问题。
要调查性能问题,请将GIT_TRACE2_PERF环境变量设置为1并运行git命令。See this SO answer for details about the trace2 feature and how to interpret the output table
(In撞击you can set a variable and run a command in the same line

GIT_TRACE2_PERF=1 git commit -m "test"

在Windows命令提示符中,您需要使用SET

SET GIT_TRACE2_PERF=1
git commit -m "test"

或者,在CMD in one line中:

cmd /V /C "SET "GIT_TRACE2_PERF=1" && git commit -m "test""

例如,在Windows上,您将看到如下所示的输出:

C:\git\me\foobar>SET GIT_TRACE2_PERF=1

C:\git\me\foobar>git status
17:23:13.056175 common-main.c:48             | d0 | main                     | version      |     |           |           |              | 2.31.1.windows.1
17:23:13.056175 common-main.c:49             | d0 | main                     | start        |     |  0.003356 |           |              | git.exe status
17:23:13.065174 ..._win32_process_info.c:118 | d0 | main                     | data_json    | r0  |  0.012053 |  0.012053 | process      | windows/ancestry:["git.exe","cmd.exe","explorer.exe"]
17:23:13.066174 repository.c:130             | d0 | main                     | def_repo     | r1  |           |           |              | worktree:C:/git/me/foobar
17:23:13.067174 git.c:448                    | d0 | main                     | cmd_name     |     |           |           |              | status (status)
17:23:13.068174 read-cache.c:2324            | d0 | main                     | region_enter | r1  |  0.015462 |           | index        | label:do_read_index .git/index
17:23:13.069175 cache-tree.c:598             | d0 | main                     | region_enter | r1  |  0.015809 |           | cache_tree   | ..label:read
17:23:13.069175 cache-tree.c:600             | d0 | main                     | region_leave | r1  |  0.016021 |  0.000212 | cache_tree   | ..label:read
17:23:13.069175 read-cache.c:2284            | d0 | main                     | data         | r1  |  0.016056 |  0.000594 | index        | ..read/version:2
17:23:13.069175 read-cache.c:2286            | d0 | main                     | data         | r1  |  0.016065 |  0.000603 | index        | ..read/cache_nr:3808
17:23:13.069175 read-cache.c:2329            | d0 | main                     | region_leave | r1  |  0.016072 |  0.000610 | index        | label:do_read_index .git/index

请注意最左列中的挂钟时间、第7列中的自开始以来的总时间以及第8列中的每个子操作的总时间。
请注意,使用Git 2.40(Q1 2023),您可以加快写入索引的速度!
Git 2.40引入了一个可选配置,允许 * 跳过 * 保护索引文件不被比特翻转的尾部哈希。
参见Derrick Stolee ( derrickstolee )commit 17194b1commit da9acdecommit ee1f0c2commit 1687150(2023年1月6日)。
(由Junio C Hamano -- gitster --合并到commit ffd9238,2023年1月16日)

hashfile:允许跳过散列函数

合著人:凯文·威尔福德
签署人:凯文·威尔福德
签署人:德里克·斯托利
hashfile API对于生成包含文件内容到该点为止的尾部哈希的文件非常有用。
使用这样的散列有助于验证文件是否存在静态损坏,例如驱动器故障导致翻转位。
Git的索引文件包含这个尾部哈希,所以它使用一个'struct hashfile'来处理文件的I/O。
这对于允许在这些操作期间使用hashfile方法是非常方便的。
但是,在写入过程中对文件内容进行散列会降低性能。
在字节到达磁盘的过程中对其进行散列比不进行散列要慢。
将硬件加速的SHA1计算替换为基于软件的sha1dc计算会使该问题变得更糟。
这种写入成本是相当大的,对于这样一个生存期很短的文件,校验和功能可能不值得这样的成本。
索引会频繁重写,并且唯一一次检查校验和是在"git fsck"(man)期间。
因此,允许用户选择退出散列计算将是有益的。
我们首先需要允许Git在哈希文件API中选择退出哈希计算。
API的缓冲写入仍然很有帮助,因此在这里进行更改是有意义的。
在"结构哈希文件"中引入新的"skip_hash"选项。
设置后,将跳过the_hash_algoupdate_fnfinal_fn成员。
当最终确定哈希文件时,末尾哈希将替换为空哈希。
在任何一种情况下,使用尾随空哈希都是可取的,因为我们不希望根据文件格式是否经过哈希处理而使其具有不同的长度。
当文件的最后一个字节都是零时,我们可以推断它是在没有哈希的情况下写入的,因此验证不能用作文件一致性检查。
这也意味着我们可以很容易地为任何我们想要的文件格式切换散列。
此补丁的一个版本已存在于microsoft/git fork since 2017中(链接提交于2018年重定基,但原始版本可追溯到2017年1月)。
在这里,使索引使用此快速路径的更改被延迟到以后的更改。
以及:

read-cache:添加索引。skipHash配置选项

签署人:德里克·斯托利
之前的更改允许跳过hashwrite API的哈希部分,而将其用作缓冲写入API。
当写操作处于关键路径时,禁用hashwrite特别有用。
一个这样的关键路径是索引的写入。
此操作非常关键,因此创建稀疏索引的目的就是为了减小索引的大小,从而加快这些写入(和读取)的速度。
文件静态稳定性和写入时性能之间的这种权衡并不容易平衡。
这个索引是一个有趣的例子,原因有两个:
1.* * 写入块用户**。
在许多用户阻塞前台操作中都会写入索引。速度的提高直接影响到它们的使用。其他文件格式通常在后台写入(commit-graph,multi-pack-index)或对正确性非常关键(pack-files)。
1.* * 索引文件的寿命很短**。
用户很长时间保留索引并进行许多阶段性更改的情况很少见。在阶段性更改之外,可以完全销毁和重写索引,而对用户的影响极小。
按照与microsoft/git fork中使用的方法类似的方法,添加一个新的配置选项(index.skipHash),允许在索引写入期间禁用此散列。
代价是我们不能再使用尾部哈希来验证内容是否存在静态损坏。
我们从istate->repo,提供的存储库配置加载此配置,如果未设置,则回退到the_repository
虽然旧版本的Git不会将空哈希值识别为特殊情况,但文件格式本身在结构上仍然符合。
使用这个空哈希仍然允许Git操作跨旧版本运行。
一个例外是"git fsck"(man),它检查索引文件的散列。

这曾经是对每个索引读取的检查,但后来被拆分为仅检查a33fc72read-cache:强制验证索引校验和,2017年4月14日,Git v2.13.0-rc 1--merge)(读缓存:force_verify_index_checksum, 2017-04-14),并在Git 2.13.0中首次发布。
记录下放宽这些限制的版本,乐观地期待这个变化会包含在Git 2.40.0中。
在这里,如果尾部哈希值全为零,则禁用此检查。
我们在config选项中添加了一个警告,指出这可能会导致较旧版本的Git出现不良行为。
作为一个快速比较,我在Linux内核存储库的副本上测试了“git update-index”(man--force-write(带index.skipHash=true和不带index.skipHash=true)。

Benchmark 1: with hash
    Time (mean ± σ):      46.3 ms ±  13.8 ms    [User: 34.3 ms, System: 11.9 ms]
    Range (min … max):    34.3 ms …  79.1 ms    82 runs

Benchmark 2: without hash
    Time (mean ± σ):      26.0 ms ±   7.9 ms    [User: 11.8 ms, System: 14.2 ms]
    Range (min … max):    16.3 ms …  42.0 ms    69 runs

Summary
    'without hash' ran
      1.78 ± 0.76 times faster than 'with hash'

这些性能上的好处足够让用户选择加入这个特性,即使可能会与旧的“git fsck”版本混淆。
git config现在在其手册页中包括:

index.skipHash

启用后,不计算索引文件的尾部哈希值。这会加速Git命令操作索引的速度,例如git addgit commitgit status
不存储校验和,而是写入一组值为零的尾随字节,表示跳过了计算。
如果您启用index.skipHash,那么2.13.0之前的Git客户端将拒绝解析索引,2.40.0之前的Git客户端将在git fsck期间报告错误。

相关问题