git status 在 Git 2.13(2017 年第二季度)中应该更快,因为:
关于最后一点,请参阅 Jeff Hostetler (jeffhostetler) 的 commit a33fc72(2017 年 4 月 14 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit cdfe138,2017 年 4 月 24 日)
read-cache: force_verify_index_checksum
教 git 在结束时跳过对 SHA1-1 校验和的验证
verify_hdr() 中的索引文件,从 read_index() 调用,除非设置了“force_verify_index_checksum”全局变量。
教fsck 强制执行此验证。
校验和验证用于检测磁盘损坏,对于小型项目,计算 SHA-1 所花费的时间并不那么重要,但对于巨大的存储库,此计算会为每个命令增加大量时间。
Git 2.14 通过更好地考虑“untracked cache”再次提高了 git status 性能,如果它们的stat 数据没有改变,它允许 Git 跳过读取未跟踪的目录,使用stat 结构的mtime 字段。
有关未跟踪缓存的更多信息,请参阅Documentation/technical/index-format.txt。
参见David Turner (dturner-tw)commit edf3b90(2017 年 5 月 8 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit fa0624f,2017 年 5 月 30 日)
当“git checkout”、“git merge”等操作内核索引时,索引扩展中的各种信息从原始状态被丢弃,因为它们通常不是这样的与主索引上的操作保持最新并保持同步。
现在跨这些操作复制未跟踪的缓存扩展,这将加速“git status”(只要缓存正确失效)。
更一般地说,使用 Git 2.14.x/2.15 写入缓存也会更快
参见Kevin Willford (``)commit ce012de、commit b50386c、commit 3921a0b(2017 年 8 月 21 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 030faf2,2017 年 8 月 27 日)
过去,我们在分配和释放方面花费了超出必要的周期
写出每个索引条目时的一块内存。
这已被优化。
当索引有超过一百万个条目并且在小型存储库上没有性能下降时,[That] 将节省 3-7%。
2017 年 12 月更新:Git 2.16(2018 年第一季度)将提出一项额外的改进,这次是针对 git log,因为迭代松散对象文件的代码刚刚得到优化。
参见Derrick Stolee (derrickstolee)commit 163ee5e(2017 年 12 月 4 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 97e1f85,2017 年 12 月 13 日)
sha1_file:使用strbuf_add() 而不是strbuf_addf()
枚举时将strbuf_addf() 的使用替换为strbuf_add()
for_each_file_in_obj_subdir() 中的松散对象。既然我们已经
在使用之前检查字符串的长度和十六进制值
路径,我们可以通过使用较低的
水平方法。
for_each_file_in_obj_subdir() 的一个消费者是缩写
代码。 OID (object identifiers) 缩写使用松散对象的缓存列表(每个对象子目录)来快速重复查询,但是有
当有许多松散对象时,缓存加载时间很长。
大多数存储库在重新打包之前没有很多松散对象,但在 GVFS 的情况下(请参阅“Announcing GVFS (Git Virtual File System)”),存储库可以增长到包含数百万个松散对象。
在具有约 250 万个松散对象的启用 GVFS 的 repo 上分析 Git For Windows 中的“git log”性能显示 12% 的 CPU 时间花费在 strbuf_addf()。
向p4211-line-log.sh 添加新的性能测试
对这种缓存加载敏感。
通过限制为 1000 次提交,我们在将历史记录读入寻呼机时更接近于用户等待时间。
对于包含两个 ~512 MB 包文件和 ~572K 松散对象的 Linux 存储库副本,运行“git log --oneline --parents --raw -1000”具有以下性能:
HEAD~1 HEAD
----------------------------------------
7.70(7.15+0.54) 7.44(7.09+0.29) -3.4%
2018 年 3 月更新:Git 2.17 将进一步改进 git status:请参阅 this answer。
更新:Git 2.20(2018 年第四季度)添加了 Index Entry Offset Table (IEOT),这允许 git status 更快地加载索引。
请参阅commit 77ff112、commit 3255089、commit abb4bb8、commit c780b9c、commit 3b1d9e0、commit 371ed0d(2018 年 10 月 10 日)Ben Peart (benpeart)。
请参阅 Nguyễn Thái Ngọc Duy (pclouds) 的 commit 252d079(2018 年 9 月 26 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit e27bfaa,2018 年 10 月 19 日)
read-cache:在工作线程上加载缓存条目
此补丁通过利用
Index Entry Offset Table (IEOT) 来划分加载和转换
跨多个线程并行缓存条目。
我使用p0002-read-cache.sh 生成了一些性能数据:
Test w/100,000 files reduced the time by 32.24%
Test w/1,000,000 files reduced the time by -4.77%
注意,在 1,000,000 个文件的情况下,多线程解析缓存条目
不会产生性能上的胜利。这是因为解析
此 repo 中的索引扩展,远远超过加载缓存的成本
条目。
这允许:
config:添加新的index.threads 配置设置
添加对新的 index.threads 配置设置的支持,该设置将用于
控制do_read_index()中的线程代码。
- 值 0 将告诉索引代码自动确定要使用的正确线程数。
值 1 将使代码成为单线程。
- 大于 1 的值将设置要使用的最大线程数。
出于测试目的,可以通过设置
GIT_TEST_INDEX_THREADS=<n> 环境变量的值大于 0。
Git 2.21(2019 年第一季度)引入了一项新的改进,更新了用于优化存在查找的 松散对象缓存。
参见commit 8be88db(2019 年 1 月 7 日)和commit 4cea1ce、commit d4e19e5、commit 0000d65(2019 年 1 月 6 日)René Scharfe (rscharfe)。
(由@987654367 中的Junio C Hamano -- gitster -- 合并@,2019 年 1 月 18 日)
object-store:每个子目录使用一个oid_array 用于松散缓存
松散对象缓存根据需要一次填充一个子目录。
它存储在oid_array 中,每次添加操作后都必须使用它。
所以在查询大范围的对象时,部分填充的数组需要最多255次,比一次排序要长100倍以上。
为每个子目录使用一个oid_array。
这可确保条目只需排序一次。
它还避免了每次缓存查找的八个二分搜索步骤。
缓存用于日志占位符%h、%t 和%p 的冲突检查,我们可以在存储库中看到更改速度加快了它们的速度。每个子目录 100 个对象:
$ git count-objects
26733 objects, 68808 kilobytes
Test HEAD^ HEAD
--------------------------------------------------------------------
4205.1: log with %H 0.51(0.47+0.04) 0.51(0.49+0.02) +0.0%
4205.2: log with %h 0.84(0.82+0.02) 0.60(0.57+0.03) -28.6%
4205.3: log with %T 0.53(0.49+0.04) 0.52(0.48+0.03) -1.9%
4205.4: log with %t 0.84(0.80+0.04) 0.60(0.59+0.01) -28.6%
4205.5: log with %P 0.52(0.48+0.03) 0.51(0.50+0.01) -1.9%
4205.6: log with %p 0.85(0.78+0.06) 0.61(0.56+0.05) -28.2%
4205.7: log with %h-%h-%h 0.96(0.92+0.03) 0.69(0.64+0.04) -28.1%
在 Git 2.26(2020 年第一季度)中,对象可达性位图机制和部分克隆机制无法很好地协同工作,因为部分克隆使用的某些对象过滤标准本质上依赖于对象遍历,但位图机制是绕过该对象遍历的优化。
然而,在某些情况下,他们可以一起工作,并且他们被教导过。
见commit 20a5fd8(2020 年 2 月 18 日)Junio C Hamano (gitster)。
见commit 3ab3185,commit 84243da,commit 4f3bd56,commit cc4aa28,commit 2aaeb9a,commit 6663ae0,commit 4eb707e,commit ea047a8,commit 608d9c9,commit 55cb10f,commit 55cb10f,commit 55cb10f,@98654 2020 年 2 月)和 commit e03f928、commit acac50d、commit 551cf8b(2020 年 2 月 13 日)Jeff King (peff)。
(由 Junio C Hamano -- gitster -- 合并到 commit 0df82d9,2020 年 3 月 2 日)
签字人:杰夫·金
我们可以轻松使用位图支持BLOB_NONE 过滤器。
由于我们知道所有对象的类型,我们只需要清除所有 blob 的结果位。
注意实现中的两个微妙之处(我也在 cmets 中提到过):
- 我们必须包含任何明确要求(并且未通过图形遍历到达)的 blob 以匹配非位图版本
- 我们必须分别处理 in-pack 和“ext_index”对象。
可以说 prepare_bitmap_walk() 可以将这些 ext_index 对象添加到类型位图中。
但现在还不行,所以让我们在这里匹配其余的位图代码(这样做可能不会提高效率,因为扩展这些位图的成本与我们这里的循环大致相同,但它可能让代码更简单)。
这是git.git 上新测试的性能结果:
Test HEAD^ HEAD
--------------------------------------------------------------------------------
5310.9: rev-list count with blob:none 1.67(1.62+0.05) 0.22(0.21+0.02) -86.8%
要了解有关 oid_array 的更多信息,请考虑 Git 2.27(2020 年第二季度)
参见commit 0740d0a、commit c79eddf、commit 7383b25、commit ed4b804、commit fe299ec、commit eccce52、commit 600bee4(2020 年 3 月 30 日)Jeff King (peff)。
(合并Junio C Hamano -- gitster --commit a768f86,2020 年 4 月 22 日)
签字人:杰夫·金
oid_array 对象使用“int”来存储项目数和分配的大小。
存储库中的对象不太可能超过 2^31 个(仅 sha1 就有 40GB!),但如果他们这样做,我们的 alloc 变量就会溢出。
您可以通过以下方式重现此案例:
git init repo
cd repo
# make a pack with 2^24 objects
perl -e '
my $nr = 2**24;
for (my $i = 0; $i < $nr; $i++) {
print "blob\n";
print "data 4\n";
print pack("N", $i);
}
| git fast-import
# now make 256 copies of it; most of these objects will be duplicates,
# but oid_array doesn't de-dup until all values are read and it can
# sort the result.
cd .git/objects/pack/
pack=$(echo *.pack)
idx=$(echo *.idx)
for i in $(seq 0 255); do
# no need to waste disk space
ln "$pack" "pack-extra-$i.pack"
ln "$idx" "pack-extra-$i.idx"
done
# and now force an oid_array to store all of it
git cat-file --batch-all-objects --batch-check
导致:
fatal: size_t overflow: 32 * 18446744071562067968
所以好消息是st_mult() 发现了问题(很大一部分是因为我们的 int 包装为负数,然后它被强制转换为 size_t),完成了它的本意工作:在疯狂的情况下保释而不是导致缓冲区过小。
但我们应该完全避免遇到这种情况,而是根据malloc() 愿意给我们的东西来限制自己。
我们可以通过切换到size_t 轻松做到这一点。
上面的cat-file 进程在整数溢出之前使其虚拟集大小达到约 120GB(我们的内部哈希存储现在为 32 字节,为 sha256 做准备,因此我们预计总共需要约 128GB,加上可能更多从一个重新分配的块复制到另一个))。
在这个补丁(以及大约 130GB 的 RAM+swap)之后,它最终会读入整个集合。没有测试,原因很明显。
请注意,此对象是在sha1-array.c 中定义的,它已重命名为oid-array.c:考虑到Git will be eventually transition from SHA1 to SHA2,这是一个更中性的名称。
另一个优化:
在 Git 2.31(2021 年第一季度)中,索引中围绕缓存树扩展的代码已经过优化。
见commit a4b6d20、commit 4bdde33、commit 22ad860、commit 845d15d(2021 年 1 月 7 日)和 commit 0e5c950、commit 4c3e187、commit fa7ca5d、commit c338898、commit da8be8c(1 月 4 日) Derrick Stolee (derrickstolee).
请参阅 René Scharfe (rscharfe) 的 commit 0b72536(2021 年 1 月 7 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit a0a2d75,2021 年 2 月 5 日)
签字人:Derrick Stolee
在比较verify_cache() 中的连续路径时,之前的更改减少了在strlen() 中花费的时间,但我们可以做得更好。
条件检查在正确位置是否存在目录分隔符,但仅在进行字符串比较之后。
交换顺序以在逻辑上等效但执行较少的字符串比较。
为了测试对性能的影响,我使用了一个在索引中包含超过 300 万条路径的存储库。
然后我重复运行以下命令:
git -c index.threads=1 commit --amend --allow-empty --no-edit
以下是 5 次运行预热后超过 10 次运行的测量结果:
Benchmark #1: v2.30.0
Time (mean ± σ): 854.5 ms ± 18.2 ms
Range (min … max): 825.0 ms … 892.8 ms
Benchmark #2: Previous change
Time (mean ± σ): 833.2 ms ± 10.3 ms
Range (min … max): 815.8 ms … 849.7 ms
Benchmark #3: This change
Time (mean ± σ): 815.5 ms ± 18.1 ms
Range (min … max): 795.4 ms … 849.5 ms
此更改比之前的更改快 2%,比 v2.30.0 快 5%。