9

不久前,我发布了一个问题,征求有关修复由于大量二进制文件而导致速度缓慢的 repo 的计划的反馈。那个问题(不是这个问题的必读问题):Fixing up a git repo that was slowing because of big binary files

我遵循了我的计划,并经历了意想不到的副作用。

我们 repo 的新克隆最初需要 2-3 个小时。发现服务器开始交换,并且在执行git config pack.windowMemory 100m && git config pack.packSizeLimit 200m之后,克隆时间下降到约 15 分钟。

我想我仍然会做剩下的计划,所以我为我们拥有的二进制类型禁用了 delta compresson,并在 repo 上运行了git repack -a -d -F 。

在此之后,回购的新克隆需要大约 20 分钟,所以实际上变得更糟了。但真正的问题是,每次已经克隆了存储库的人尝试推送提交时,他们都会得到“自动打包存储库以获得最佳性能。”。

关于可能出了什么问题以及如何/应该如何解决的任何想法?

4

3 回答 3

4

可能你的 repo 的大小和你的低价值pack.packSizeLimit使得包的数量总是高于gc.autopacklimit. 所以增加它们中的任何一个以确保 gc 不会运行每个提交。

我不确定会以何种方式packSizeLimit影响记忆,但我不相信它会产生任何重大影响。如果您的实验结果并非如此,请纠正我。直接影响内存使用的参数是pack.windowMemorypack.deltaCacheSize

于 2013-11-10T19:20:02.970 回答
2

您可能想重试相同的git repack,这次使用 Git 2.32(2021 年第二季度,将近 8 年后)及其新--geometric=<n>选项:

" git repack" ( man )到目前为止,只能将阳光下的所有东西重新包装成一个包装(或按大小拆分)。

引入了一种更聪明的策略来降低重新打包存储库的成本。

请参阅Taylor Blau 的提交 14e7b83(2021 年 3 月 19 日)、提交 2a15964提交 13d746a提交 dab3247提交 f25e33c(2021 年 3 月 5 日)和提交 0fabafd提交 339bce2提交 c9fff00提交 f62312e ttaylorr 2021 年 2 月 22 日
请参阅Junio C Hamano ( ) 的提交 ccae01c(2021 年 3 月 5 日。 请参阅Jeff King的提交 20b031f提交 6325da1提交 fbf20ae提交 60bb5f2(2021 年 2 月 22 日) (gitster
peff) .
(由Junio C Hamano 合并 -- gitster--提交 2744383中,2021 年 3 月 24 日)


第一的:find_kept_pack_entry()

packfile:介绍' find_kept_pack_entry()'

合着:Jeff King
署名:Jeff King
署名:Taylor Blau
审核人:Jeff King

未来的调用者将需要一个函数来填充pack_entry给定对象 id 的“结构”,但只能从它在任何保留包中的位置开始。

特别是,一个新的 ' git repack' ( man )模式可以确保生成的包通过对象计数形成几何进度,它将把不想重新打包的包标记为“保持在核心”,并且它会想要停止可达性遍历一旦它访问任何保留包中的对象。
但是,它不想在非保留包或 .keep 包处停止遍历。

显而易见的替代方案是 ' find_pack_entry()',但这还不够,因为它只返回它找到的第一个包,可能会或可能不会保留(并且 mru 缓存使其无法预测如果有选项你会得到哪个包) .

除此之外,您可以遍历所有包以查找每个包中的对象,但它会随着包的数量而缩放,这可能会令人望而却步。

引入' find_kept_pack_entry()',一个类似于' find_pack_entry()'的函数,但只填充保留包中的对象。


然后:git pack-objects --stdin-packs

builtin/pack-objects.c: 添加 '--stdin-packs' 选项

建议人:Jeff King
签字人:Taylor Blau
审核人:Jeff King

在即将到来的提交中,' git repack' ( man )将希望创建一个包含某些包(包含的包)中的所有对象的包,不包括某些其他包(排除的包)中的任何对象。

这个调用者可以自己迭代这些包并将它找到的对象直接通过标准输入提供给' git pack-objects' man,但这种方法有一些缺点:

  • 它要求每个想要以git pack-objects这种方式驱动“”的调用者自己实现包迭代。
    这迫使调用者考虑诸如将什么订单对象提供给打包对象之类的细节,而调用者可能宁愿不这样做。
  • 如果包含的包中的对象集很大,则需要通过管道发送大量数据,这是低效的。
  • 调用者也被迫跟踪排除的对象,并确保它不发送任何出现在包含和排除包中的对象。

但最大的缺点是缺乏可达性遍历。
因为调用者直接传入一个对象列表,所以这些对象不会获得分配给它们的 namehash,这会对 delta 选择过程产生负面影响,导致“ git pack-objects”即使存在也无法找到好的 delta。

调用者可以自己制定可达性遍历,但以git pack-objects这种方式驱动''的唯一方法是进行完全遍历,然后在遍历完成后删除排除包中的对象。
这可能对关心性能的调用者不利,尤其是在具有许多对象的存储库中。

引入 ' git pack-objects --stdin-packs' ( man )来解决这四个问题。

' git pack-objects --stdin-packs' 需要标准输入上的包名称列表,其中 ' pack-xyz.pack' 表示包含该包,而 ' ^pack-xyz.pack' 表示排除它。
生成的包包括存在于至少一个包含包中且不存在于任何排除包中的所有对象。

git pack-objects现在在其手册页中包含:

--stdin-packs

从标准输入读取包文件的基本名称(例如,pack-1234abcd.pack),而不是对象名称或修订参数。

生成的包包含包含包中列出的所有对象(不以 开头的^对象),不包括排除包中列出的任何对象(以 开头^)。

不兼容--revs,或暗示的选项--revs(例如 --all),但兼容的 除外--unpacked


最后:git repack --geometric=<n>

builtin/repack.c: 添加 '--geometric' 选项

建议人:Derrick Stolee
签字人:Taylor Blau
审核人:Jeff King

通常对两者都有用:

  • 存储库中的包文件相对较少,并且
  • 避免存储库中的打包文件太少,以至于我们会定期重新打包其全部内容

git repack这个补丁在 ' ' ( man )中实现了一个 '--geometric=' 选项。
这允许调用者指定他们希望每个包至少是前一个最大包的倍数(按对象计数)。

具体来说,假设一个存储库有“n”个包文件,标记为 P1、P2、...,直到 Pn。
每个包文件的对象计数等于“ objects(Pn)”。
几何因子为 ' r',应该是:

objects(Pi) > r*objects(P(i-1))

对于所有 i in [1, n],包按以下顺序排序

objects(P1) <= objects(P2) <= ... <= objects(Pn).

由于找到一个真正的最优重新包装是 NP 难的,我们沿着两个方向对其进行近似:

  1. 我们假设在开始重新打包之前有一个包的截止点,该截止点右侧的所有内容都已经形成了几何级数(或者不存在截止点并且所有内容都必须重新打包)。

  2. 我们假设所有小于截止计数的东西都必须重新打包。
    这构成了我们的基本假设,但它也可能导致甚至“重”包被重新打包,例如,如果我们有 6 个包含以下对象数量的包:

    1, 1, 1, 2, 4, 32

然后我们将在 '1, 1' 和 '1, 2, 4, 32' 之间放置截止点,将前两个包卷成一个包含 2 个对象的包。
这打破了我们的进步,让我们:

2, 1, 2, 4, 32
  ^

(其中 ' ^' 表示我们拆分的位置)。
为了恢复进度,我们将拆分向前(朝向更大的包)将每个包连接到我们的新包中,直到恢复几何级数。
在这里,看起来像:

2, 1, 2, 4, 32  ~>  3, 2, 4, 32  ~>  5, 4, 32  ~> ... ~> 9, 32
  ^                   ^                ^                   ^

这样做的好处是不会过于频繁地重新包装沉重的包装,同时也一次只创建一个新包装。
另一个问题是我们假设松散的、索引的和重新记录的对象是微不足道的,并将它们集中到我们创建的任何新包中。
这可能导致非幂等结果。

git repack现在在其手册页中包含:

-g=<factor>

--geometric=<factor>

排列生成的包结构,以便每个连续包包含的对象数量至少<factor>是次大包的数量。

git repack通过确定需要重新打包成一个以确保几何级数的包文件的“剪切”来确保这一点。它选择最小的包文件集,以便尽可能多的较大的包文件(按该包中包含的对象数)可以保持不变。

与其他重新打包模式不同,要打包的对象集由“卷起”的包集唯一确定;换句话说,确定需要组合的包才能恢复几何级数。

指定时--unpacked,松散对象隐式包含在此“汇总”中,而不考虑它们的可达性。这在未来可能会发生变化。此选项(意味着完全不同的重新打包模式)不能保证与选项的所有其他组合一起使用git repack)。


结果

  Test                                        HEAD^                   HEAD
  -----------------------------------------------------------------------------------------------
  5303.5: repack (1)                          57.34(54.66+10.88)      56.98(54.36+10.98) -0.6%
  5303.6: repack with kept (1)                57.38(54.83+10.49)      57.17(54.97+10.26) -0.4%
  5303.11: repack (50)                        71.70(88.99+4.74)       71.62(88.48+5.08) -0.1%
  5303.12: repack with kept (50)              72.58(89.61+4.78)       71.56(88.80+4.59) -1.4%
  5303.17: repack (1000)                      217.19(491.72+14.25)    217.31(490.82+14.53) +0.1%
  5303.18: repack with kept (1000)            246.12(520.07+14.93)    217.08(490.37+15.10) -11.8%

还有这种--stdin-packs情况,它的扩展性更好一些(尽管即使在 1,000 包时也没有那么多):

  5303.7: repack with --stdin-packs (1)       0.00(0.00+0.00)         0.00(0.00+0.00) =
  5303.13: repack with --stdin-packs (50)     3.43(11.75+0.24)        3.43(11.69+0.30) +0.0%
  5303.19: repack with --stdin-packs (1000)   130.50(307.15+7.66)     125.13(301.36+8.04) -4.1%

请参阅Taylor Blau的“ Git 2.33:几何重新打包” :

从历史上看,git repack做了两件事之一:

  • 它要么将所有松散的对象重新打包到一个新包中(可选地删除每个对象的松散副本),
  • 或者它将所有包重新打包成一个新包(可选地删除冗余包)。

一般来说,当包较少时,Git 具有更好的性能,因为许多操作会随着存储库中包的数量而扩展。
因此,将所有东西打包成一个包通常是个好主意。> 但是从历史上看,繁忙的存储库通常需要将所有内容打包到一个巨大的包中。
这是因为可达性位图是服务器端 Git 性能的关键优化,只能描述单个包中的对象。
因此,如果您想使用位图有效地覆盖存储库中的许多对象,则这些对象必须一起存储在同一个包中。

我们正在努力消除这一限制(您可以阅读更多关于我们如何做到这一点的信息),但其中一个重要步骤是实施一种新的重新包装方案,该方案在包装数量相对较少和包装之间进行权衡一起最近添加的对象(换句话说,近似于自上次重新打包以来添加的新对象)。

为此,Git 学习了一种新的“几何”重新打包策略

这个想法是确定一组(小)可以组合在一起的包,以便剩余的包根据对象大小形成几何级数。
换句话说,如果最小的包有 N 个对象,那么次大的包将至少有 2N 个对象,依此类推,在此过程中的每一步都会加倍(或增长任意常数)。

https://github.blog/wp-content/uploads/2021/04/Scaling-monorepo-maintenance-fig-12.png?w=1958


git pack-objects --stdin-packs“ ” ( man )的输入验证已在 Git 2.34(2021 年第四季度)中更正。

请参阅提交 561fa03(2021 年 7 月 9 日)和提交 fb20d4b(2021 年 6 月 21 日),作者 Ævar Arnfjörð Bjarmason ( avar)
(由Junio C Hamano 合并 -- gitster--提交 5c933f0中,2021 年 8 月 24 日)

pack-objects:修复--stdin-packs选项中的段错误

签字人:Ævar Arnfjörð Bjarmason

修复 339bce2 中添加--stdin-packs的选项中的段错误(“ :添加 '--stdin-packs' 选项”,2021-02-22,Git v2.32.0-rc0 --批次 #4中列出的合并)。builtin/pack-objects.c

read_packs_list_from_stdin()函数没有检查它正在读取的行是否是有效的包,因此在使用 QSORT() 时pack_mtime_cmp()我们会有一个NULL“util”字段。
“util”字段用于将包含/排除包的名称与packed_git它们对应的结构相关联。

逻辑错误在于假设我们可以迭代所有包并注释我们得到的排除和包含的包,而不是检查我们在标准输入上得到的行。
对排除的包进行了检查,但包含的包被简单地假定为有效。

正如测试中所指出的,我们不会报告第一个坏行,而是根据string-list.cAPI 首先排序的任何行。
在这种情况下,我认为这很好。
对替代方法进行了进一步讨论

即使我们很懒,让我们断言我们确实希望在测试中获得的行,因为将来更改此代码的人可能会错过这种情况,并且希望更新测试和注释。


另一种加快重新打包的方法:--quiet,它实际上适用于 Git 2.35(2022 年第一季度)。

请参阅Derrick Stolee ( ) 的提交 47ca93d提交 e4d0c11(2021 年 12 月 20 日(由Junio C Hamano 合并——提交 88a516a中,2022 年 1 月 5 日)derrickstolee
gitster

repack: 使 '--quiet' 禁用进度

帮助者:Jeff King
签字者:Derrick Stolee

在 ' git repack' ( man )中测试一些想法时,我用 ' --quiet' 运行它,发现仍然显示了一些进度输出。
具体来说,编写多包索引的输出显示了进度。

' show_progress' 变量 in用 ' ' 标志cmd_repack()初始化isatty(2)并且根本不被 ' --quiet' 标志修改。
'--quiet' 标志修改了po_args.quiet 选项,该选项被转换为 ' git pack-objects' ( man )子进程的 '--quiet' 标志。
但是,“ show_progress”用于将进度信息直接发送到不使用子进程的多包索引编写逻辑。

此处的解决方法是,如果为真,则将 ' show_progress' 修改为假,否则。 这种新的期望简化了后来检查两者的条件。po_opts.quietisatty(2)

更新文档以明确“-q”将禁用所有进度,并确保“ git pack-objects”子进程将收到标志。

git repack现在在其手册页中包含:

--quiet

在标准错误流上没有显示任何进展,并将-q 选项传递给“git pack-objects”。见git pack-objects

于 2021-03-27T14:07:07.890 回答
0

正在重新包装什么?本地克隆,远程?请记住,本地和远程是分开的,这可能意味着您正在获取解包的东西,然后在本地打包......需要在本地复制远程的配置(再试一次,我可能完全不在基地)。

于 2013-03-16T16:06:41.710 回答