man*_*moo 54 git performance git-svn
我有一个"新鲜的"git-svn repo(11.13 GB),其中有超过100,000个对象.
我已经做好了
git fsck
git gc
Run Code Online (Sandbox Code Playgroud)
在初步结账后的回购.
然后我试着去做
git status
Run Code Online (Sandbox Code Playgroud)
执行git状态所需的时间是2m25.578s和2m53.901s
我通过发出命令测试了git status
time git status
Run Code Online (Sandbox Code Playgroud)
5次,所有时间都在上面列出的两次之间.
我在Mac OS X上执行此操作,本地而非通过VM.
它不应该花这么长时间.
有任何想法吗?救命?
谢谢.
编辑
我有一个同事坐在我旁边,有一个类似的盒子.减少RAM并使用jfs文件系统运行Debian.他的git状态在同一个回购中运行.3(它也是一个git-svn checkout).
另外,我最近在这个文件夹上更改了我的文件权限(到777),它大大缩短了时间(为什么,我没有线索).我现在可以在3到6秒之间完成任务.这是可以控制的,但仍然很痛苦.
man*_*moo 30
它归结为我现在能看到的几件物品.
git gc --aggressive
777
必须有其他事情发生,但这显然是影响最大的事情.
mas*_*onk 17
git status必须每次查看存储库中的每个文件.您可以告诉它停止查看您没有使用的树木
git update-index --assume-unchanged <trees to skip>
Run Code Online (Sandbox Code Playgroud)
从联机帮助页:
指定这些标志时,不会更新为路径记录的对象名称.相反,这些选项设置和取消设置路径的"假定未更改"位.当"假定未更改"位打开时,git停止检查工作树文件是否有可能的修改,因此您需要手动取消设置该位以在更改工作树文件时告诉git.当在具有非常慢的lstat(2)系统调用(例如cifs)的文件系统上处理大项目时,这有时是有用的.
此选项还可以用作粗略的文件级机制,以忽略跟踪文件中未提交的更改(类似于.gitignore对未跟踪文件的更改).如果需要在索引中修改此文件,例如在提交中合并时,Git将失败(优雅); 因此,如果上游更改了假定未跟踪文件,则需要手动处理该情况.
git中的许多操作依赖于您的文件系统以实现高效的lstat(2)实现,因此可以便宜地检查工作树文件的st_mtime信息,以查看文件内容是否已从索引文件中记录的版本更改.不幸的是,一些文件系统的效率低下lstat(2).如果您的文件系统是其中之一,则可以将"假设未更改"位设置为未更改的路径,以使git不执行此检查.请注意,在路径上设置此位并不意味着git将检查文件的内容以查看它是否已更改 - 它使git省略任何检查并假设它未更改.当您对工作树文件进行更改时,必须通过在修改它们之前或之后删除"假定未更改"位来明确告诉git.
...
要设置"假定未更改"位,请使用--assume-unchanged选项.要取消设置,请使用--no-assume-unchanged.
该命令查看core.ignorestat配置变量.如果这是真的,使用git update-index路径更新路径...并使用更新索引和工作树的其他git命令更新路径(例如git apply --index,git checkout-index -u和git read-tree -u )自动标记为"假设不变".注意,如果git update-index --refresh发现工作树文件与索引匹配,则不设置"假定未更改"位(如果要将它们标记为"假定未更改",请使用git update-index --really-refresh).
现在,很明显,如果您可以方便地忽略回购的某些部分,此解决方案才会起作用.我在一个类似规模的项目上工作,肯定有大树,我不需要定期检查.git-status的语义使其成为一般的O(n)问题(文件数量为n).您需要特定于域的优化才能做得更好.
请注意,如果您使用拼接模式,即如果您通过合并而不是rebase从上游集成更改,则此解决方案变得不太方便,因为从上游合并的--assume-unchanged对象的更改将变为合并冲突.您可以使用基础工作流程来避免此问题.
git status
应该在Git 2.13(2017年第二季度)中更快,因为:
git status
性能的方法 ")关于最后一点,请参阅Jeff Hostetler()提交a33fc72(2017年4月14日).(由Junio C Hamano合并- -在提交cdfe138,2017年4月24日)jeffhostetler
gitster
read-cache
:force_verify_index_checksum
教导git跳过在
verify_hdr()
调用索引文件末尾的SHA1-1校验和的验证,read_index()
除非设置了"force_verify_index_checksum
"全局变量.教会
fsck
强制进行此验证.校验和验证用于检测磁盘损坏,对于小型项目,计算SHA-1所需的时间并不重要,但对于巨大的存储库,此计算会为每个命令增加大量时间.
Git 2.14通过更好地考虑" 未跟踪的缓存 "来改善git状态性能,这允许Git stat
使用结构的mtime
字段跳过读取未跟踪的目录(如果他们的数据没有改变)stat
.
有关Documentation/technical/index-format.txt
未跟踪缓存的更多信息,请参阅.
请参阅David Turner()提交的edf3b90(2017年5月8日).(由Junio C Hamano合并- -在fa0624f,2017年5月30日)dturner-tw
gitster
当"
git checkout
","git merge
"等操纵核心内索引时,索引扩展中的各种信息将从原始状态中丢弃,因为通常情况下它们不会保持最新并且在 -与主索引上的操作同步.现在,在这些操作中复制未跟踪的缓存扩展,这将加速"git status"(只要缓存被正确无效).
更一般地说,使用Git 2.14.x/2.15写入缓存也会更快
请参阅提交ce012de,提交b50386c,提交3921a0b(2017年8月21日)作者:Kevin Willford(``).
(由Junio C gitster
Hamano合并- -在提交030faf2,2017年8月27日)
在编写每个索引条目时,我们过去花费的时间超过必要的周期来分配和释放内存.
这已经过优化.当索引超过一百万个条目且小回购没有性能下降时,[那]将节省3-7%.
2017年12月更新:Git 2.16(2018年第一季度)将提出一个额外的增强功能,这一次git log
,因为迭代松散的目标文件的代码刚刚得到优化.
见Derrick Stolee()提交163ee5e(2017年12月4日).(由Junio C Hamano合并- -在97e1f85承诺中,2017年12月13日)derrickstolee
gitster
sha1_file
:用strbuf_add()
而不是strbuf_addf()
在枚举松散对象时替换使用
strbuf_addf()
with .由于我们在使用路径之前已经检查了字符串的长度和十六进制值,因此我们可以通过使用较低级别的方法来防止额外的计算.strbuf_add()
for_each_file_in_obj_subdir()
一个消费者
for_each_file_in_obj_subdir()
是缩写代码.OID(对象标识符)缩写使用缓存对象的缓存列表(每个对象子目录)来快速重复查询,但是当存在许多松散对象时,存在显着的缓存加载时间.大多数存储库在重新打包之前没有很多松散的对象,但在GVFS情况下(参见" 宣布GVFS(Git虚拟文件系统) "),存储库可以增长到拥有数百万个松散对象.在一个支持GVFS的repo上使用~250万个松散对象
分析Git For Windows中的 'git log'性能,显示占用了12%的CPU时间strbuf_addf()
.添加一个新的性能测试
p4211-line-log.sh
,对此缓存加载更敏感.
通过限制为1000次提交,我们更接近于将历史记录读入寻呼机时的用户等待时间.对于具有两个~512 MB包文件和~572K松散对象的Linux repo的副本,运行'git log --oneline --parents --raw -1000'具有以下性能:
HEAD~1 HEAD
----------------------------------------
7.70(7.15+0.54) 7.44(7.09+0.29) -3.4%
Run Code Online (Sandbox Code Playgroud)
更新2018年3月:Git 2.17将进一步改进git status
:看到这个答案.
更新:Git 2.20(Q8 2018)添加了索引条目偏移表(IEOT),允许git status
更快地加载索引.
请参阅Ben Peart()提交77ff112,提交3255089,提交abb4bb8,提交c780b9c,提交3b1d9e0,提交371ed0d(2018年10月10日).
见提交252d079通过(2018年9月26日)阮泰玉维战().(由Junio C Hamano合并- -在提交e27bfaa,2018年10月19日)benpeart
pclouds
gitster
read-cache:在工作线程上加载缓存条目
此补丁通过利用索引条目偏移表(IEOT)来并行地分割多个线程中的缓存条目的加载和转换,从而有助于解决加载索引的CPU成本问题.
我曾经
p0002-read-cache.sh
生成一些性能数据:Run Code Online (Sandbox Code Playgroud)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年第一季度)引入了一项新的改进,更新了松散的对象缓存,用于优化已更新的存在查找.
见提交8be88db(2019年1月7日),并提交4cea1ce,提交d4e19e5,提交0000d65(2019年1月6日),由勒内Scharfe( )rscharfe
.
(由Junio C gitster
Hamano合并- -在提交eb8638a,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%
Run Code Online (Sandbox Code Playgroud)
一个长期解决方案是增加git以在内部缓存文件系统状态.
Karsten Blees已经为msysgit做了这样的事情,它大大提高了Windows的性能.在我的实验中,他的更改花费了我在Win7机器上运行的"git status"从25秒到1-2秒的时间.
Karsten的变化:https://github.com/msysgit/git/pull/94
讨论缓存方法:https://groups.google.com/forum/#!topic / msysgit/fL_jykUmUNE/discussion