仓库变大之后,哪些命令会变慢、慢多少、跟什么有关?是提交数、文件数还是对象数?这篇分别把提交数、文件数各放大几十倍,测 status、log、rev-list、add、clone 的耗时,再对比松散与打包存储,以及 commit-graph 与 Bloom filter 的作用。
1,000 提交 10,000 提交 40,000 提交 git status 12 ms 12 ms 12 ms git log -1 11 ms 11 ms 12 ms git rev-list --count HEAD 14 ms 39 ms 124 ms git log --oneline(全部) 19 ms 65 ms 213 ms git log --stat -n 100 15 ms 15 ms 15 ms 1,000 提交:对象 count: 0 in-pack: 3000 .git 目录 396 KB 10,000 提交:对象 count: 0 in-pack: 30000 .git 目录 3060 KB 40,000 提交:对象 count: 0 in-pack: 120000 .git 目录 11992 KB
提交数增长 40 倍,git status、git log -1、git log --stat -n 100 的耗时没有变化;rev-list --count 与完整的 git log --oneline 要遍历所有提交,耗时随提交数增长。
1,000 文件 10,000 文件 50,000 文件 git status 16 ms 30 ms 88 ms git status -uno 16 ms 24 ms 61 ms git add -A(没有改动) 15 ms 29 ms 84 ms git ls-files | wc -l 13 ms 15 ms 20 ms git diff --stat HEAD 15 ms 24 ms 63 ms git commit --allow-empty 16 ms 28 ms 74 ms 1,000 文件:.git/index 82,970 字节(每个文件约 83 字节) 10,000 文件:.git/index 803,071 字节(每个文件约 80 字节) 50,000 文件:.git/index 4,003,071 字节(每个文件约 80 字节)
文件数增长 50 倍,status、add -A、commit 的耗时增长 4 到 6 倍(都要检查每个已跟踪文件),ls-files 只读 index,增长很小。index 文件约每个文件 80 字节。
1,000 10,000 40,000 (提交数) 对象总数 4,000 40,000 160,000 打包:.git 目录 KB 576 5,260 27,128 松散:.git 目录 KB 16,092 166,904 (未测) 松散:对象文件数 4,000 40,000 (未测) rev-list --objects --all(打包) 22 ms 131 ms 948 ms rev-list --objects --all(松散) 118 ms 1368 ms (未测) cat-file --batch-all-objects(打包) 16 ms 38 ms 116 ms cat-file --batch-all-objects(松散) 128 ms 1325 ms (未测) git status(打包) 17 ms 29 ms 74 ms (这一组里工作区文件数也随提交增长到 40,000 个,所以 status 的增长来自工作区而不是历史)
同一份历史,10,000 个提交的松散存储遍历全部对象比打包慢约 10 倍,磁盘占用约 32 倍(166,904 KB 对 5,260 KB)。40,000 个提交的松散存储没有测。
1,000 10,000 40,000 (提交数) 没有 commit-graph 18 ms 67 ms 246 ms 有 commit-graph(无 Bloom) 16 ms 37 ms 122 ms commit-graph + --changed-paths 16 ms 27 ms 76 ms 结果行数(三种相同) 20 200 800 commit-graph 文件大小 68 KB 656 KB 2,620 KB
git log -- 目录 要比较每个提交的树。commit-graph 把 246 ms 降到 122 ms,再加 --changed-paths 的 Bloom filter 降到 76 ms(40,000 提交)。
1,000 10,000 40,000 (提交数)
完整克隆 143 ms 1215 ms 6284 ms
.git 大小 600 KB 5,412 KB 28,224 KB
--depth 1 130 ms 858 ms 3296 ms
.git 大小 276 KB 1,652 KB 6,360 KB
--filter=blob:none 181 ms 1314 ms 7482 ms
.git 大小 692 KB 6,228 KB 31,272 KBfile:// 克隆的耗时随提交数增长;--depth 1 在这里也要 3.3 s,因为最新提交的树里有 40,000 个文件;--filter=blob:none 在这个带检出的场景里最慢。耗时里包含检出,没有拆开。
git status 12 ms → 12 ms 1.0 倍 (规模 ×40) git log -1 11 ms → 12 ms 1.1 倍 (规模 ×40) git rev-list --count HEAD 14 ms → 124 ms 8.8 倍 (规模 ×40) git log --oneline(全部) 19 ms → 213 ms 11.0 倍 (规模 ×40) git log --stat -n 100 15 ms → 15 ms 1.0 倍 (规模 ×40) git status 16 ms → 88 ms 5.6 倍 (规模 ×50) git status -uno 16 ms → 61 ms 3.7 倍 (规模 ×50) git add -A(没有改动) 15 ms → 84 ms 5.4 倍 (规模 ×50) git ls-files | wc -l 13 ms → 20 ms 1.5 倍 (规模 ×50) git diff --stat HEAD 15 ms → 63 ms 4.3 倍 (规模 ×50) git commit --allow-empty 16 ms → 74 ms 4.6 倍 (规模 ×50) rev-list --objects --all(打包) 22 ms → 948 ms 43.5 倍 (规模 ×40) cat-file --batch-all-objects(打包) 16 ms → 116 ms 7.3 倍 (规模 ×40) log -- 目录(无 graph) 18 ms → 246 ms 13.8 倍 (规模 ×40) log -- 目录(Bloom) 16 ms → 76 ms 4.7 倍 (规模 ×40) 完整克隆 143 ms → 6284 ms 44.0 倍 (规模 ×40) --depth 1 130 ms → 3296 ms 25.4 倍 (规模 ×40) --filter=blob:none 181 ms → 7482 ms 41.3 倍 (规模 ×40)
每条命令都有约 11 ms 的启动开销,所以小规模时倍数偏低。表里的“随历史”“随文件”“不变”三类划分来自上面的测量。