Erki Der Loonyの回答で提案されているgit cat-file --batch-check --batch-all-objects
コマンドは、新しいGit 2.19(2018年第3四半期)オプションを使用して高速化できます。--unordered
学習したすべてのオブジェクトを反復処理して、パックファイルに表示される順序でオブジェクトをオプションで一覧表示します。これにより、オブジェクトが列挙されているときに呼び出し元がこれらのオブジェクトにアクセスした場合に、アクセスの場所を特定できます。
コミット0889aae、コミット79ed0a5、コミット54d2f0d、コミットced9fff(2018年8月14日)、およびコミット0750bb5、コミットb1adb38、コミットaa2f5ef、コミット736eb88、コミット8b36155、コミットa7ff6f5、コミット202e7f1 (peff
2018年8月10日)を参照してください。
( Junio C Hamanoによってマージされました---コミット0c54cda、2018年8月20日)gitster
cat-file
:サポート" unordered
"出力--batch-all-objects
パックファイル内のすべてのオブジェクトのコンテンツにアクセスする場合は、通常、ハッシュ順ではなくパック順でアクセスする方がはるかに効率的です。これにより、パックファイル内のアクセスの局所性が高まり、パックファイルは関連するデルタを隣り合わせに配置するため、デルタベースキャッシュに適しています。対照的に、sha1はコンテンツと識別可能な関係がないため、ハッシュの順序は事実上ランダムです。
このパッチでは、内部でパック順にパックを繰り返す「 --unordered
」オプションが導入されています。cat-file
すべてのファイルコンテンツをダンプすると、結果を確認できます。
$ time ./git cat-file --batch-all-objects --buffer --batch | wc -c
6883195596
real 0m44.491s
user 0m42.902s
sys 0m5.230s
$ time ./git cat-file --unordered \
--batch-all-objects --buffer --batch | wc -c
6883195596
real 0m6.075s
user 0m4.774s
sys 0m3.548s
同じ出力、異なる順序、はるかに高速です。次のような別のプロセスでオブジェクトコンテンツにアクセスすることになった場合でも、同じスピードアップが適用されます。
git cat-file --batch-all-objects --buffer --batch-check |
grep blob |
git cat-file --batch='%(objectname) %(rest)' |
wc -c
最初のコマンドに「」を追加--unordered
すると、ランタイムがgit.git
24秒から3.5秒に短縮されます。
補足:現在、すべての処理を実行するために、実際にはさらに高速化が可能です。実際のパックの反復中にオブジェクトのコンテンツを出力しているので、オブジェクトの場所がわかっており、によって行われる余分なルックアップをスキップできますoid_object_info()
。基盤となるAPIは、この種の直接リクエストを行う準備ができていないため、このパッチはその最適化の手前で停止します。
それで、もし--unordered
もっと良いのなら、それをデフォルトにしてみませんか?2つの理由:
--batch-all-objects
ドキュメントでは、ハッシュ順に出力することを約束しました。は配管工事であるためcat-file
、人々はそのデフォルトに依存している可能性があり、変更することはできません。
場合によっては実際には遅くなります。パック順に歩くには、パックのrevindexを計算する必要があります。また、重複排除の手順では、並べ替えと重複排除ではなく、oidsetを使用します。これにより、コストが高くなる可能性があります。
たとえば、各オブジェクトのタイプとサイズにアクセスするだけの場合は、次のようになります。
git cat-file --batch-all-objects --buffer --batch-check
私のベスト5のウォームキャッシュのタイミングは、を使用して900ミリ秒から1100ミリ秒になり--unordered
ます。コールドキャッシュまたはメモリ不足の場合は、パックファイル内のローカリティが向上するため、より適切に実行できる可能性があります。
そして最後の質問です。なぜそれは「--unordered
」ではなく「--pack-order
」なのですか?答えは再び2つあります。
「パックの順序」は、オブジェクトのセット全体で明確に定義されたものではありません。私たちはルーズなオブジェクトと複数のパックのオブジェクトをヒットしており、私たちが約束している唯一の注文は単一のパック内です。残りは明らかにランダムです。
ここでのポイントは最適化です。したがって、特定の順序を約束する必要はありませんが、オブジェクトのコンテンツにアクセスするのに効率的である可能性が高い順序を選択すると言うだけです。これにより、別の互換性オプションを追加することなく、将来のさらなる変更への扉が開かれたままになります。
Git 2.20(2018年第4四半期)では、次のようにさらに高速になります。
RenéScharfe ()によるcommit 8c84ae6、 commit 8b2f8cb、commit 9249ca2、commit 22a1646、commit bf73282 (rscharfe
2018年10月4日)を参照してください。( Junio C Hamano
によってマージされました---コミット82d0a8c、2018年10月19日)gitster
oidset
: 使用するkhash
メモリフットプリントを削減し、高速化するために、oidset
を使用して再実装します。khash.h
master
Clang 6.0.1で、主にoidsetを使用して重複オブジェクトをチェックするコマンドのパフォーマンス:
$ cmd="./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)'"
$ /usr/bin/time $cmd >/dev/null
0.22user 0.03system 0:00.25elapsed 99%CPU (0avgtext+0avgdata 48484maxresident)k
0inputs+0outputs (0major+11204minor)pagefaults 0swaps
$ hyperfine "$cmd"
Benchmark #1: ./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)'
Time (mean ± σ): 250.0 ms ± 6.0 ms [User: 225.9 ms, System: 23.6 ms]
Range (min … max): 242.0 ms … 261.1 ms
そしてこのパッチで:
$ /usr/bin/time $cmd >/dev/null
0.14user 0.00system 0:00.15elapsed 100%CPU (0avgtext+0avgdata 41396maxresident)k
0inputs+0outputs (0major+8318minor)pagefaults 0swaps
$ hyperfine "$cmd"
Benchmark #1: ./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)'
Time (mean ± σ): 151.9 ms ± 4.9 ms [User: 130.5 ms, System: 21.2 ms]
Range (min … max): 148.2 ms … 170.4 ms
Git 2.21(2019年第1四半期)は、パック内の順序でオブジェクトにアクセスする通常のパターンに従うことにより、commit-graphを書き出すためのコードパスをさらに最適化します。
ÆvarArnfjörðBjarmason ( )によるcommit d7574c9(2019年1月19日)を参照してください。( Junio C Hamanoによってマージされました---コミット04d67b6、2019年2月5日)avar
gitster
を使用して、「commit-graphwrite」ステップをわずかに最適化
FOR_EACH_OBJECT_PACK_ORDER
しfor_each_object_in_pack()
ます。
Derrick Stoleeは、Windowsで独自のテストを行い、高い精度で2%の改善を示しました。
Git 2.23(2019年第3四半期)は、パックオブジェクトのグループ化ヒントとして使用されるオブジェクトへのパスをスケルチするためのgit rev-list --objects
「」オプションで学習した「」を改善します。--no-object-names
Emily Shaffer()によるcommit 42357b4(2019年6月19日)を参照してください。( Junio C Hamanoによってマージされました---コミットf4f7e75、2019年7月9日)nasamuffin
gitster
rev-list
--no-object-names
:配管を有効にすることを教える
cat-file
追加情報なしで非コミットオブジェクトのOIDのみを出力するオプションをrev-listに与えることにより、より簡単な解析を可能にします。
これは短期間のシムです。後で、rev-list
見つかったオブジェクトのタイプをに似た形式で印刷する方法を教える必要がありますcat-file
。
このコミットの前に、次のように、からの出力rev-list
をcat-fileにパイプする前にマッサージする必要がありました。
git rev-list --objects HEAD | cut -f 1 -d ' ' |
git cat-file --batch-check
OIDの最後に非表示の空白が存在するため、ルートツリーを処理する場合、これは特に予想外でした。
git rev-list --objects --filter=tree:1 --max-count=1 HEAD |
xargs -I% echo "AA%AA"
これで、追加されたテストケースのように、直接パイプすることができます。
git rev-list --objects --no-object-names HEAD | git cat-file --batch-check
これが次の違いです。
vonc@vonvb:~/gits/src/git$ git rev-list --objects HEAD~1..
9d418600f4d10dcbbfb0b5fdbc71d509e03ba719
590f2375e0f944e3b76a055acd2cb036823d4b44
55d368920b2bba16689cb6d4aef2a09e8cfac8ef Documentation
9903384d43ab88f5a124bc667f8d6d3a8bce7dff Documentation/RelNotes
a63204ffe8a040479654c3e44db6c170feca2a58 Documentation/RelNotes/2.23.0.txt
そして、と--no-object-name
:
vonc@vonvb:~/gits/src/git$ git rev-list --objects --no-object-names HEAD~1..
9d418600f4d10dcbbfb0b5fdbc71d509e03ba719
590f2375e0f944e3b76a055acd2cb036823d4b44
55d368920b2bba16689cb6d4aef2a09e8cfac8ef
9903384d43ab88f5a124bc667f8d6d3a8bce7dff
a63204ffe8a040479654c3e44db6c170feca2a58
Git 2.31(Q1 2021)では、コア内のrevindexへの抽象アクセスにより、パックファイルに格納されているオブジェクトをパックに表示される順序で列挙できるようになり、ディスク上で事前計算されたrevindexを導入する準備が整います。これにより、これらの操作が高速化されます。
commit e5dcd78、commit d5bc7c6、commit 8389855、commit 1c3855f、commit 2891b43、commit b130aef 、 commit 0a7e364 、 commit fc150ca、commit 3a3f54d、commit 45bef5c、commit 78232bf 、commit 011f3fd 、commit a78 6a5c10c、コミット66cbd3e、コミット952fc68、コミットf33fb6e(2021年1月13日)テイラーブラウ(ttaylorr
)。Jeff King()によるcommit 779412b(2021年1月14日)
を参照してください。( Junio C Hamanoによってマージされました---コミットbcaaf97、2021年1月25日)peff
gitster
サインオフ-作成者:Taylor Blau
.idx
次のいくつかのパッチでは、メモリに逆インデックスをロードする(コア内のコンテンツの逆をマッピングする)か、まだ導入されていないディスク上のフォーマットから直接ロードする準備をします。
その準備として、呼び出し元が構造内のrevindexポインターに明示的にインデックスを付けることを回避するAPIを導入しますpacked_git
。
リバースインデックスを操作する方法は4つあります。したがって、既存のAPIが削除さ
れるまでに、4つの関数が''からエクスポートされます。
発信者は次のことを行うことができます。pack-revindex.h
パックのリバースインデックスをロードします。
これには、インデックスを開き、配列を生成してから並べ替えることが含まれます。
インデックスを開くことが失敗する可能性があるため、この関数(' load_pack_revindex()
')はintを返します。
したがって、それは単一の引数のみを取ります:' struct
packed_git'''呼び出し元はの逆インデックスを構築したいと考えています。
この関数は、現在のAPIと新しいAPIの両方に適しています。
呼び出し元は引き続きリバースインデックスを明示的に開く必要がありますが、この関数は最終的に、ディスク上のフォーマット(存在する場合)からリバースインデックスを検出してロードする方法を学習します。
それ以外の場合は、メモリ内に最初から生成することにフォールバックします。
パック位置をオフセットに変換します。
この操作は現在、と呼ばれてpack_pos_to_offset()
います。
パックと位置を取り、対応するを返しますoff_t
。
呼び出し元は障害を処理して続行するのに適していないため、エラーが発生するとBUG()が呼び出されます。
パック位置をインデックス位置に変換します。
同上; これはパックと位置を取り、を返しますuint32_t
。
この操作はとして知られていpack_pos_to_index()
ます。
エラー状態についての同じ考え方がここでも当てはまります。
特定のオフセットのパック位置を見つけます。
この操作は現在、として知られていoffset_to_pack_pos()
ます。オブジェクトがそのオフセットに存在する場合は
、パック、オフセット、および位置が書き込まれる場所へのポインターが必要です。
それ以外の場合は、失敗を示すために-1が返されます。uint32_t
以前は直接アクセス'->offset'
していた一部の発信者とは異なり、この呼び出しに関するエラーチェックはやや堅牢です。
呼び出し元は常に2つのオブジェクトの境界を指すオフセットを渡す必要があるため、これは重要です。
APIは、直接アクセスとは異なり、それを強制します。'->nr'
これは、戻り値をチェックしないがチェックできる呼び出し元が、signedfromを'revindex'配列へのインデックスとして扱う後続のパッチで重要になり-1
ますfind_revindex_position()
。
2つの設計いぼが新しいAPIに引き継がれています。
- 範囲外のオブジェクトのインデックス位置を要求すると(そのようなオブジェクトが存在しないため)BUG()になりますが、パックの最後に存在しないオブジェクトのオフセットを要求すると、の合計サイズが返されます。パック。
これにより、(ディスク上のサイズを計算するために)隣接する2つのオブジェクトのオフセットの差を常に取得したいが、パックの最後の境界について心配したくない呼び出し元にとって便利です。
offset_to_pack_pos()
逆インデックスを遅延ロードしますが、ロードpack_pos_to_index()
しません(前者の呼び出し元はエラーの処理に適していますが、後者の呼び出し元はそうではありません)。
Git 2.32(2021年第2四半期)では、" git (branch|tag) --format=...
"がマイクロ最適化されています。
ZheNing Hu()によるcommit 844c3f0 ( 2021年4月20日)およびcommit 22f69a8(2021年4月19日)を参照してください。( Junio C Hamanoによってマージされました---コミットc108c8c、2021年5月7日)adlternative
gitster
支援者:Junio C Hamano
支援者:Jeff King
支援者
:RenéScharfe署名者:ZheNing Hu
(man)を使用すると、すべてのrefが独自の出力strbufとエラーstrbufを割り当てます。
ただし、各ステップrefの出力に最終的なstrbufを再利用できます。ゼロ以外の値を返し、エラーバッファの内容を出力すると
、gitが終了するという事実にもかかわらず、エラーバッファも再利用されます。git for-each-ref
format_ref_array_item()
パフォーマンスgit for-each-ref
テストツールを使用したGitリポジトリ自体のパフォーマンスhyperfine
は、23.7ミリ秒±0.9ミリ秒から22.2ミリ秒±1.0ミリ秒に変更されます。
最適化は比較的マイナーです。
同時に、この最適化を(man)と(man)に適用します。git tag -l
git branch -l
このアプローチは、組み込みのcat-fileを高速化するために79ed0a5( " cat-file
:すべての出力に単一のstrbufを使用する"、2018-08-14、Git v2.19.0-rc0 --merge)で使用されるアプローチに似ています。