代码之家  ›  专栏  ›  技术社区  ›  manumoomoo

对于100000个对象来说,Git真的很慢。有什么修正吗?

  •  52
  • manumoomoo  · 技术社区  · 14 年前

    我有一个“新鲜”的GitSvnRepo(11.13GB),其中包含超过100000个对象。

    我已经做好了

    git fsck
    git gc
    

    首次结算后的回购。

    然后我试着做了一个

    git status
    

    执行Git状态所需的时间为2 m25.578秒和2 m53.901秒。

    我通过发出命令测试了git状态

    time git status
    

    5次,所有时间都在上述两次之间。

    我在Mac OS X上执行此操作,而不是通过虚拟机本地执行。

    不可能花这么长时间。

    有什么想法吗?帮助?

    谢谢。

    编辑

    我旁边坐着一个同事,旁边有一个类似的盒子。更少的RAM,运行带有JFS文件系统的Debian。伊斯 GIT状态 在同一个repo上运行在.3中(它也是Git SVN签出)。

    另外,我最近更改了这个文件夹的文件权限(到777),大大减少了时间(为什么,我不知道)。我现在可以在3到6秒之间在任何地方完成。这是可以控制的,但仍然是一种痛苦。

    12 回复  |  直到 5 年前
        1
  •  28
  •   willeM_ Van Onsem    10 年前

    我现在能看到一些东西。

    1. git gc --aggressive
    2. 打开文件权限 777

    一定还有别的事情发生,但这显然是影响最大的事情。

        2
  •  17
  •   masonk    14 年前

    每次Git状态都必须查看存储库中的每个文件。你可以告诉它不要再看那些你不在研究的树了。

    git update-index --assume-unchanged <trees to skip>
    

    source

    从手册页:

    当指定这些标志时, 为路径记录的对象名 未更新。相反,这些 选项设置和取消设置“假设 路径的“位不变”。当 “假设不变”位是开的,git 停止检查工作树文件 为了可能的修改,所以你 需要手动复位位才能显示 当你改变工作树的时候 文件。这有时会有帮助,当 与一个大项目合作 具有非常慢的lstat的文件系统(2) 系统调用(例如CIFS)。

    此选项还可以用作 要忽略的粗文件级机制 跟踪文件中未提交的更改 (类似于.gitignore所做的 未跟踪的文件)。Git会失败 (优雅地)以防它需要 在索引中修改此文件,例如 在提交中合并时;因此,在 假设的未跟踪文件是 改变上游,你需要 手动处理情况。

    在Git的许多操作依赖于 文件系统要有一个高效的 lstat(2)实现,以便 工作树的ST时间信息 可以便宜地检查文件以查看 文件内容已从更改为 索引中记录的版本 文件。不幸的是,一些文件系统 低效率LSTAT(2)。如果你 文件系统就是其中之一,您可以设置 “假定不变”位指向您的路径 未更改以使Git不 做这个检查。注意设置这个 路径上的位并不意味着Git会 检查文件内容以查看 如果它改变了,它会使git变为 省略任何检查并假定它 没有改变。当您对 工作树文件,你必须 直接告诉Git 删除“假设不变”位, 修改之前或之后 他们。

    为了设置“假设不变” 位,使用——假设选项不变。到 不设置,使用——不假设不变。

    命令查看core.ignorestat 配置变量。当这是 是的,路径用git更新 更新索引路径和更新的路径 使用其他更新的git命令 索引树和工作树(例如git apply——索引,git checkout index-u, 和git read tree-u)是 自动标记为“假设 不变。注意“假设 如果git,则不设置“未更改”位 更新索引--refresh查找 工作树文件与索引匹配 (使用git update index——真正刷新 如果你想把它们标记为“假设 不变的“”。


    现在,很明显,这个解决方案只有在回购协议的某些部分您可以方便地忽略的情况下才能工作。我在一个类似规模的项目上工作,那里肯定有很大的树,我不需要定期检查。Git状态的语义使它成为一个普遍的O(N)问题(文件数量为N)。您需要特定于域的优化来做得更好。

    请注意,如果您在缝合模式下工作,也就是说,如果您通过合并而不是重新平衡来集成来自上游的更改,那么这个解决方案就变得不太方便了,因为对——假设从上游合并的未更改的对象变成了合并冲突。通过重新调整工作流程可以避免此问题。

        3
  •  5
  •   Chris Kline    11 年前

    一个长期的解决方案是增加git以在内部缓存文件系统状态。

    KarstenBlees已经为MSysgit做了这些,这大大提高了Windows的性能。在我的实验中,我的Win7机器在虚拟机中运行时,他的更改使“Git状态”从25秒变为1-2秒。

    卡斯滕的变化: https://github.com/msysgit/git/pull/94

    讨论缓存方法: https://groups.google.com/forum/#!topic/msysgit/fL_jykUmUNE/discussion

        4
  •  5
  •   VonC    5 年前

    git status 在Git 2.13(2017年第2季度)中应该更快,因为:

    关于最后一点,见 commit a33fc72 (2017年4月14日) Jeff Hostetler ( jeffhostetler ) .
    (合并) Junio C Hamano -- gitster -- 在里面 commit cdfe138 24 APR 2017

    read-cache : force_verify_index_checksum

    教Git跳过在结束时对sha1-1校验和的验证 中的索引文件 verify_hdr() 从哪来的 read_index() 除非“ 强制验证索引校验和 “已设置全局变量。

    fsck 强制进行验证。

    校验和验证用于检测磁盘损坏,对于小型项目,计算SHA-1所需的时间并不重要,但对于大型存储库,此计算会为每个命令增加大量时间。


    Git2.14通过更好地考虑 untracked cache “,这允许Git跳过读取未跟踪目录 stat 数据未更改,使用 mtime 领域 斯达 结构。

    Documentation/technical/index-format.txt 有关未跟踪缓存的更多信息。

    commit edf3b90 (2017年5月8日) David Turner ( dturner-tw ) .
    (合并) 哈马诺郡-- 吉斯特 —— 在里面 commit fa0624f ,2017年5月30日)

    何时 git checkout “,” git merge “,等等。操作核心索引时,索引扩展中的各种信息将从原始状态中丢弃,因为它们通常不是保持最新并与主索引上的操作同步的情况。

    现在可以跨这些操作复制未跟踪的缓存扩展,这将加快“Git状态”(只要缓存正确失效)。


    一般来说,使用git 2.14.x/2.15,写入缓存的速度也会更快。

    commit ce012de , commit b50386c , commit 3921a0b (2017年8月21日) Kevin Willford (``) .
    (合并) 哈马诺郡-- 吉斯特 —— 在里面 commit 030faf2 ,27八月2017日)

    我们过去常常花费超过必要的周期来分配和释放 写下每个索引项时的一段内存。
    这已经过优化。

    如果索引中有超过100万个条目,并且在小型回购上没有性能下降,那么这将节省3-7%的开销。


    更新日期:2017年12月:Git 2.16(2018年第1季度)将提出额外的增强,这次是为了 git log ,因为在松散对象文件上迭代的代码刚刚得到优化。

    commit 163ee5e (2017年12月4日) Derrick Stolee ( derrickstolee ) .
    (合并) 哈马诺·朱尼奥-- 吉斯特 —— 在里面 commit 97e1f85 ,13十二月2017日)

    sha1_file 使用 strbuf_add() 而不是 strbuf_addf()

    替代使用 StrubfAdff-() 具有 StrubfAdAd() 枚举时 中的松散对象 for_each_file_in_obj_subdir() . 因为我们已经 在使用之前检查字符串的长度和十六进制值 路径,我们可以使用- 水平法。

    一个消费者 对于_obj_subdir()中的每个_文件 是缩写 代码。类(类) object identifiers )缩写使用松散对象的缓存列表(每个对象子目录)快速进行重复查询,但是 当存在许多松散对象时,缓存加载时间很长。

    大多数存储库在重新打包之前没有很多松散对象,但是 GVFS 案例(见) Announcing GVFS (Git Virtual File System) “)回购协议可以发展成拥有数以百万计的松散对象。
    分析中的“git log”性能 Git For Windows 在一个支持gvfs的repo上,大约250万个松散对象显示12%的CPU时间花费在 StrubfAdff-() .

    向添加新的性能测试 p4211-line-log.sh 那就是更多 对此缓存加载敏感。
    通过限制为1000次提交,我们更接近于将历史记录读取到寻呼机时的用户等待时间。

    对于带有两个~512 MB packfiles和~572K loose对象的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%
    

    更新日期:2018年3月:Git 2.17将改进 GIT状态 更多:参见 this answer .


    更新:Git 2.20(2018年第4季度)新增 Index Entry Offset Table (IEOT) ,允许 GIT状态 以更快地加载索引。

    commit 77ff112 , commit 3255089 , commit abb4bb8 , commit c780b9c , commit 3b1d9e0 , commit 371ed0d (2018年10月10日) Ben Peart ( benpeart ) .
    commit 252d079 (2018年9月26日) Nguyễn Thái Ngọc Duy ( pclouds ) .
    (合并) 哈马诺·朱尼奥-- 吉斯特 —— 在里面 commit e27bfaa ,10月19日2018日)

    读缓存:在工作线程上加载缓存项

    此修补程序通过使用 这个 索引项偏移量表(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%
    

    注意,在1000000个文件的情况下,多线程的缓存条目解析 不产生性能胜利。这是因为分析 此repo中的索引扩展远远超过加载缓存的成本。 条目。

    允许:

    config 添加新的 index.threads 配置设置

    添加新的支持 索引线程 配置设置,用于 控制线程代码 do_read_index() .

    • 值0将告诉索引代码自动确定要使用的正确线程数。
      值为1将使代码成为单线程的。
    • 大于1的值将设置要使用的最大线程数。

    出于测试目的,可以通过设置 GIT_TEST_INDEX_THREADS=<n> 环境变量的值大于0。


    Git 2.21(2019年第1季度)引入了新的改进,更新了 松散对象缓存 ,用于优化存在性查找,已更新。

    commit 8be88db (2019年1月7日),以及 commit 4cea1ce , commit d4e19e5 , commit 0000d65 (2019年1月6日) René Scharfe ( rscharfe ) .
    (合并) 哈马诺·朱尼奥-- 吉斯特 —— 在里面 commit eb8638a ,18月2019日)

    object-store 使用一 oid_array 松散缓存的每个子目录

    松散对象缓存根据需要一次填充一个子目录。
    它存储在 OIDGA阵列 ,在每次添加操作之后必须重新使用。
    因此,当查询范围广泛的对象时,部分填充的数组需要重设为255次,这比排序一次要长100倍以上。

    使用一 OIDGA阵列 对于每个子目录。
    这样可以确保只对条目进行一次排序。
    它还避免了每次缓存查找的八个二进制搜索步骤,作为一个小的奖励。

    缓存用于日志占位符的冲突检查。 %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%
    
        5
  •  4
  •   slobobaby    10 年前

    一般来说,我的Mac可以使用Git,但是如果有很多松散的对象,那么它会变得非常慢。似乎hfs在一个目录中处理大量文件并不是很好。

    git repack -ad
    

    然后

    git gc --prune=now
    

    将生成一个单独的包文件并删除任何遗留的松散对象。运行这些程序可能需要一些时间。

        6
  •  3
  •   David Underhill    14 年前

    你可以试着通过 --aggressive 切换到 git gc 看看这是否有帮助:

    # this will take a while ...
    git gc --aggressive
    

    另外,你可以使用 git filter-branch 删除旧的提交和/或文件(如果您的历史记录中有您不需要的东西)(例如,旧的二进制文件)。

        7
  •  2
  •   Brendon McLean    13 年前

    就其价值而言,我最近发现 git status 我的主分支和开发分支之间的命令。

    为了缩短长话短说,我在项目根目录中找到了一个280MB文件。这是数据库转储的意外签入,因此可以删除它。

    以下是之前和之后:

    ⚡ time git status
    # On branch master
    nothing to commit (working directory clean)
    git status  1.35s user 0.25s system 98% cpu 1.615 total
    
    ⚡ rm savedev.sql
    
    ⚡ time git status
    # On branch master
    # Changes not staged for commit:
    #   (use "git add/rm <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #   deleted:    savedev.sql
    #
    no changes added to commit (use "git add" and/or "git commit -a")
    git status  0.07s user 0.08s system 98% cpu 0.157 total
    

    我有105000件物品在商店里,但似乎大文件比许多小文件更具威胁性。

        8
  •  1
  •   baudtack    14 年前

    你也可以试试 git repack

        9
  •  0
  •   Andreas Rehm    14 年前

    也许你用病毒扫描器? 我已经在Windows和Linux上测试了一些大项目——真是太快了!

    我认为您不需要在克隆的repo中执行git gc(应该是干净的)。

    你的硬盘可以吗?每秒IOPS和R/W?可能是坏了?

        10
  •  0
  •   neoneye    14 年前

    也许Spotlight正在尝试索引这些文件。可能禁用代码目录的聚光灯。检查活动监视器并查看正在运行的进程。

        11
  •  0
  •   srparish    14 年前

    我将使用不同的文件系统创建一个分区。与在其他文件系统上执行类似操作相比,HFT+对我来说总是比较迟缓。

        12
  •  0
  •   Devnegikec    8 年前

    试着运行prune命令,它会去掉松散的对象。

    Git远程修剪原点