对于高延迟磁盘,许多文件的Git提取速度很慢

时间:2017-10-24 21:23:13

标签: git

我对此感兴趣的是对git内部的一些见解 -

如果我在Bitbucket上远程托管了一个包含许多文件的repo(比如~25000,它们的大小都在2K左右),为什么在定位高延迟磁盘时第一次获取速度这么慢?

由于需要编写大量文件,我希望第一次检出的操作很慢,但是获取应该只接收少量元数据和打包文件并将其写入磁盘。磁盘是高延迟但吞吐量很好,因此编写少量大文件的性能通常很好。

1 个答案:

答案 0 :(得分:0)

  

获取仅应接收少量元数据和打包文件并将其写入磁盘。

仍然,Git 2.20(2018年第四季度)将提高获取速度。

这是因为,当创建一个精简包时,它允许将对象与另一个不在生成包中但已知存在于接收端的对象形成一个增量,该代码学会了利用可达性位图;这样,服务器就可以针对超出“边界”提交的基准发送增量。

请参见commit 6a1e32dcommit 30cdc33(2018年8月21日)和commit 198b349commit 22bec79commit 5a924a6commit 968e77a(2018年8月17日)由Jeff King (peff)
(由Junio C Hamano -- gitster --commit 3ebdef2中合并,2018年9月17日)

  

pack-objects:对精简的“拥有”对象重用磁盘增量

     

当我们为fetch提供服务时,我们将从获取协商中将“希望”和“拥有”传递给打包对象。这不仅告诉我们需要发送哪些对象,还使用了   边界提交为“首选基础”:它们的树和斑点是增量基础的候选对象,既可以重用磁盘上的增量也可以找到新的基础。

     

但是,这错过了一些机会。模制一些特殊情况,例如浅克隆或部分克隆,我们知道从“ haves”可访问的每个对象都可能是首选的基础。
  我们不使用所有这些有两个原因:

     
      
  1. 遍历整个历史并枚举另一边所有的物体是很昂贵的。
  2.   
  3. 增量搜索非常昂贵,因此我们希望保持候选碱基的数量合理。边界提交最有可能起作用。
  4.   
     

但是,当我们具有可到达性位图时,原因1不再适用。
  我们可以在另一侧高效地计算可到达对象的集合(事实上,这已经作为位图集差异的一部分进行了处理,以获取有趣对象的列表)。并且使用此集方便地涵盖了浅层和部分情况,因为无论如何我们都必须禁用位图。

     

第二个理由反对在寻找新的增量时使用这些基数。

     

但是在一种情况下,我们可以免费使用此信息:当我们要考虑重用现有的磁盘增量时,如果我们知道另一端具有基础对象,则可以这样做。实际上,这样可以节省增量搜索的时间,因为它减少了我们要计算的增量。

     

这正是此修补程序的作用:当我们考虑是否要重用磁盘增量时,如果位图告诉我们另一侧有对象(我们正在制作一个   薄包装),然后我们重新使用它。

     

以下是使用p5311linux.git上的结果,它模拟了自上次提取N天后的客户提取

 Test                         origin              HEAD
 --------------------------------------------------------------------------
 5311.3: server   (1 days)    0.27(0.27+0.04)     0.12(0.09+0.03) -55.6%
 5311.4: size     (1 days)               0.9M              237.0K -73.7%
 5311.5: client   (1 days)    0.04(0.05+0.00)     0.10(0.10+0.00) +150.0%
 5311.7: server   (2 days)    0.34(0.42+0.04)     0.13(0.10+0.03) -61.8%
 5311.8: size     (2 days)               1.5M              347.7K -76.5%
 5311.9: client   (2 days)    0.07(0.08+0.00)     0.16(0.15+0.01) +128.6%
 5311.11: server   (4 days)   0.56(0.77+0.08)     0.13(0.10+0.02) -76.8%
 5311.12: size     (4 days)              2.8M              566.6K -79.8%
 5311.13: client   (4 days)   0.13(0.15+0.00)     0.34(0.31+0.02) +161.5%
 5311.15: server   (8 days)   0.97(1.39+0.11)     0.30(0.25+0.05) -69.1%
 5311.16: size     (8 days)              4.3M                1.0M -76.0%
 5311.17: client   (8 days)   0.20(0.22+0.01)     0.53(0.52+0.01) +165.0%
 5311.19: server  (16 days)   1.52(2.51+0.12)     0.30(0.26+0.03) -80.3%
 5311.20: size    (16 days)              8.0M                2.0M -74.5%
 5311.21: client  (16 days)   0.40(0.47+0.03)     1.01(0.98+0.04) +152.5%
 5311.23: server  (32 days)   2.40(4.44+0.20)     0.31(0.26+0.04) -87.1%
 5311.24: size    (32 days)             14.1M                4.1M -70.9%
 5311.25: client  (32 days)   0.70(0.90+0.03)     1.81(1.75+0.06) +158.6%
 5311.27: server  (64 days)   11.76(26.57+0.29)   0.55(0.50+0.08) -95.3%
 5311.28: size    (64 days)             89.4M               47.4M -47.0%
 5311.29: client  (64 days)   5.71(9.31+0.27)     15.20(15.20+0.32) +166.2%
 5311.31: server (128 days)   16.15(36.87+0.40)   0.91(0.82+0.14) -94.4%
 5311.32: size   (128 days)            134.8M              100.4M -25.5%
 5311.33: client (128 days)   9.42(16.86+0.49)    25.34(25.80+0.46) +169.0%
     

在所有情况下,我们都可以节省服务器上的CPU时间(有时很节省),并且结果包更小。
  我们确实在客户端上花费了更多的CPU时间,因为它必须重建更多的增量。

     

但这是正确的权衡,因为客户端倾向于数量超过服务器。
  这只是意味着瘦包机制正在发挥作用。

     

从用户的角度来看,操作的端到端时间通常会更快。例如,在128天的情况下,我们在服务器上节省了15秒,而在服务器上节省了16秒   客户。
  由于生成的数据包小34MB,因此,如果网络速度低于270Mbit / s,这将是一次净赢。那实际上是最坏的情况。
  这个为期64天的案例可以节省11秒钟以上,而成本却不到11秒钟。所以这是一个小小的胜利   在任何网络速度下,节省的40MB都是纯粹的奖励。
  对于较小的获取量,这种趋势继续存在。


在Git 2.22(2019年第二季度)中,另一个选项将在重新打包方面有所帮助,现在默认情况下会创建路径名哈希缓存,以避免在重新打包时产生cr脚的delta。

请参见commit 36eba03Eric Wong (ele828)(2019年3月14日)。
请参见commit d431660commit 90ca149Jeff King (peff)(2019年3月15日)。
(由Junio C Hamano -- gitster --commit 2bfb182中合并,2019年5月13日)

  

pack-objects:默认为写入位图哈希缓存

启用 pack.writebitmaphashcache 应该始终是性能上的胜利。
磁盘上每个对象仅花费4个字节,ae4f07fpack-bitmap:实现可选的name_hash缓存,2013-12-21,Git v2.0.0-rc0)中的时间显示 将获取和部分位图的克隆时间缩短40-50%。

  

我们当时未默认启用它的唯一原因是   版本的JGit的位图阅读器抱怨存在   不了解的可选标头位。
  但这在JGit's d2fa3987a中进行了更改(使用bitcheck来检查OPT_FULL选项的存在,2013-10-30),使其在2014年末成为JGit v3.5.0。

     

因此,让我们默认启用此选项。
  它与所有版本的Git向后兼容,如果您还在同一存储库上使用JGit,则使用将近5年的版本只会遇到问题。

     

我们将从所有测试脚本中删除手动设置,包括   性能测试。这不是严格必要的,但有两个优点:

     
      
  1. 如果默认情况下永远停止启用哈希缓存,则我们的性能    回归测试会引起注意。

  2.   
  3. 我们可以使用修改后的perf测试来展示    否则未配置的存储库,如下所示。

  4.   
     

这些是针对linux.git的一些性能测试的结果,   显示出有趣的结果。
  您可以在5310.4中看到预期的加速,这在ae4f07f(2013年12月,Git v2.0.0-rc0)中有所说明。
  奇怪的是,尽管在ae4f07f中看到了相反的说法,但5310.8却没有改善(实际上变慢了)。我对此没有解释。

     

p5311的测试当时还不存在,但确实有改进   (由于具有更好的增量,因此可以减少包装数量,我们可以在更短的时间内找到它。)

  Test                                    HEAD^                HEAD
  -------------------------------------------------------------------------------------
  5310.4: simulated fetch                 7.39(22.70+0.25)     5.64(11.43+0.22) -23.7%
  5310.8: clone (partial bitmap)          18.45(24.83+1.19)    19.94(28.40+1.36) +8.1%
  5311.31: server (128 days)              0.41(1.13+0.05)      0.34(0.72+0.02) -17.1%
  5311.32: size   (128 days)                         7.4M                 7.0M -4.8%
  5311.33: client (128 days)              1.33(1.49+0.06)      1.29(1.37+0.12) -3.0%