2014年にgit clone --depth=1 ...
提案されたものはGit2.22で2019年第2四半期に速くなります。
これは、最初の " git clone --depth=...
"部分クローン中に、プロミサーオブジェクト(定義上、反対側からフェッチされたすべてのオブジェクト)を列挙してスキップする接続チェックの大部分にサイクルを費やすことは無意味だからです。
これは最適化されています。
clone
:部分的なクローンのオブジェクトチェックを高速化します
部分的なクローンの場合、完全な接続チェックを行うのは無駄です。プロミサーオブジェクト(部分クローンの場合はすべて既知のオブジェクト)をスキップし、それらをすべて列挙して接続チェックから除外すると、大規模なリポジトリではかなりの時間がかかる可能性があります。
せいぜい、必要な参照によって参照されるオブジェクトを確実に取得する必要があります。
部分クローンの場合は、これらのオブジェクトが転送されたことを確認してください。
結果:
Test dfa33a2^ dfa33a2
-------------------------------------------------------------------------
5600.2: clone without blobs 18.41(22.72+1.09) 6.83(11.65+0.50) -62.9%
5600.3: checkout of result 1.82(3.24+0.26) 1.84(3.24+0.26) +1.1%
62%高速!
Git 2.26(2020年第1四半期)では、部分クローンでフェッチするときに、不要な接続チェックが無効になりました。
Jonathan Tan()によるcommit 2df1aa2、commit 5003377(2020年1月12日)を参照してください。( Junio C Hamanoによってマージされました---コミット8fb3945、2020年2月14日)jhowtan
gitster
サインオフ-作成者:Jonathan Tan
レビュー者:Jonathan Nieder
コミットdfa33a298d( " clone
:部分クローンのオブジェクトチェックを高速化"、2019-04-21、Git v2.22.0-rc0 --merge )は、クローン作成時に実行される接続チェックを最適化し--filter
て、refsが直接指すオブジェクトの存在のみをチェックします。
しかし、これは十分ではありません。それらはまた、有望なオブジェクトである必要があります。
代わりに、これらのオブジェクトがプロミサーオブジェクトである、つまりプロミザーパックに表示されることを確認して、このチェックをより堅牢にします。
と:
fetch
:完全な接続チェックをやめる--filter
サインオフ-作成者:Jonathan Tan
レビュー者:Jonathan Nieder
フィルタが指定されている場合、フェッチしたばかりのパックファイルの内容を完全に接続チェックする必要はありません。参照されるオブジェクトがpromisorオブジェクトであることを確認するだけで済みます。
これにより、多くのpromisorオブジェクトがあるリポジトリへのフェッチが大幅に高速化されます。これは、接続チェック中にすべてのpromisorオブジェクトが列挙され(興味がないことを示すため)、かなりの時間がかかるためです。
また、Git 2.26(2020年第1四半期)では、部分クローンが使用するオブジェクトフィルタリング基準の一部は本質的にオブジェクトトラバーサルに依存しているため、オブジェクト到達可能性ビットマップ機構と部分クローン機構は連携して機能するように準備されていませんでしたが、ビットマップ機構はそのオブジェクトトラバーサルをバイパスするための最適化。
しかし、彼らが一緒に働くことができるいくつかのケースがあり、彼らは彼らについて教えられました。
Junio C Hamano()によるcommit 20a5fd8(2020年2月18日)を参照してください。commit 3ab3185
、commit 84243da、commit 4f3bd56、commit cc4aa28、commit 2aaeb9a、commit 6663ae0、commit 4eb707e、commit ea047a8、commit 608d9c9、commit 55cb10f、commit 792f811、commit d90fe06(14 Feb 2020)を参照してください。551cf8b(2020年2月13日)Jeff King()gitster
peff
。( Junio C Hamano
によってマージされました---コミット0df82d9、2020年3月2日)gitster
pack-bitmap
BLOB_LIMIT
:フィルタリングを実装する
サインオフ-作成者:Jeff King
以前に実装さBLOB_NONE
れたコミットBLOB_LIMIT
と同様に、結果内のブロブのサイズを確認し、必要に応じてビットの設定を解除することで、フィルターをサポートできます。
これはより少し高価ですBLOB_NONE,
が、それでも顕著なスピードアップを生み出します(これらの結果はgit.gitにあります):
Test HEAD~2 HEAD
------------------------------------------------------------------------------------
5310.9: rev-list count with blob:none 1.80(1.77+0.02) 0.22(0.20+0.02) -87.8%
5310.10: rev-list count with blob:limit=1k 1.99(1.96+0.03) 0.29(0.25+0.03) -85.4%
BLOB_NONE
実装は、blobタイプのビットマップをウォークしながらオブジェクトごとに移動する必要があることを除いて、実装と似ています(一致をマスクすることはできませんが、blobごとに個別にサイズを検索する必要があるため) 。
使用の秘訣ctz64()
は、から取得されshow_objects_for_type()
ます。これは、同様に個々のビットを見つける必要があります(ただし、blobのない大きなチャンクをすばやくスキップしたい)。
Git 2.27(2020年第2四半期)は、「約束された」オブジェクトが約束されたリモートリポジトリからオンデマンドで遅延して取得可能であると想定される部分クローンリポジトリでのコミット祖先接続チェックを簡素化します。
Jonathan Tan()によるcommit 2b98478(2020年3月20日)を参照してください。(濱野純雄による合併---コミット0c60105、 2020年4月22日)jhowtan
gitster
connected
:常に部分的なクローン最適化を使用する
サインオフ-作成者:Jonathan Tan
レビュー者:Josh Steadmon
50033772d5 ( " :部分クローンの約束connected
を確認する"、2020-01-30、Git v2.26.0-rc0-バッチ#5にリストされているマージ)を使用すると、高速パス(約束パックのチェック)が次のサブセットを渡すようになります。スローパス(rev-list)>-チェックするすべてのオブジェクトがプロミザーパックで見つかった場合、ファストパスとスローパスの両方が通過します。check_connected()
これは、低速パスを実行する必要があるときはいつでも高速パスを試行できることを意味します。
高速パスは現在、フラグによって保護されています。したがって、そのフラグを削除します。
また、高速パスを低速パスにフォールバックします。高速パスに障害が発生した場合、障害のあるOIDと残りのすべてのOIDがrev-listに渡されます。
ユーザーに見える主な利点は、部分クローンからのフェッチのパフォーマンスです。具体的には、フェッチの前に行われる接続チェックの高速化です。
特に、私のコンピューター上の部分クローンへのno-opフェッチは、7秒から0.01秒に高速化されました。これは、2df1aa239c( " fetch
:完全な接続チェックを忘れるif --filter"、2020-01-30、Git v2.26.0-rc0 --マージがバッチ#5にリストされている)の作業を補完するものであり、前述の50033772d5。そのコミットでは、フェッチ後の接続チェックが高速化されました。
高速パスを追加すると、次の場合にパフォーマンスが低下する可能性があります。
部分クローンまたは部分クローンへのフェッチが失敗した場合、Gitは無駄に実行rev-list
されます(フェッチされたものはすべてプロミザーパックに入れられると予想されるため、それが行われなかった場合、rev-listも失敗する可能性があります) 。
部分的なクローンがreceive-packを提供するという(私の意見ではありそうもない)イベントで、receive-packによって実行される接続チェック。
これらのケースは非常にまれであり、この場合のパフォーマンスの低下は十分に小さい(追加のオブジェクトDBアクセス)ため、フラグを回避することの利点がこれらのケースを上回っていると思います。
Git 2.27(2020年第2四半期)では、オブジェクトフィルター " --filter=tree:0
"を使用したオブジェクトウォークで、利用可能な場合にパックビットマップを利用できるようになりました。
ジェフキング()によるコミット9639474、コミット5bf7f1e(2020年5月4日)を参照してください。Taylor Blau()によるcommit b0a8d48、commit 856e12c(2020年5月4日)を
参照してください。(濱野純雄による合併---コミット69ae8ff、 2020年5月13日)peff
ttaylorr
gitster
サインオフ-作成者:Taylor Blau
以前のパッチでは、特定のタイプのすべてのオブジェクトを除外する他のフィルターを簡単に定義できるようにしました。これを使用して、' 'が。に等しい--filter=tree:<n>
場合に''フィルターのビットマップレベルのフィルタリングを実装します。n
0
n > 0
' 'の値の場合、オブジェクトフィルタリング機構は、特定のツリーの深さを決定するために本格的なツリートラバーサルを必要とするため、一般的なケースはビットマップによって支援されません。
同じツリーオブジェクトはコンテキストに応じて異なる深さを持つ可能性があるため、これをキャッシュすることも自明ではありません(たとえば、2つのコミット間でツリーがディレクトリ階層で上に移動された)。
しかし、' n = 0
'の場合は助けることができ、このパッチはそうします。
このツリーとカーネルを使用したマスターで実行p5310.11
すると、このケースが大幅に役立つことがわかります。
Test master this tree
--------------------------------------------------------------------------------
5310.11: rev-list count with tree:0 10.68(10.39+0.27) 0.06(0.04+0.01) -99.4%
と:
ジェフキング()によるコミット9639474、コミット5bf7f1e(2020年5月4日)を参照してください。Taylor Blau()によるcommit b0a8d48、commit 856e12c(2020年5月4日)を
参照してください。(濱野純雄による合併---コミット69ae8ff、 2020年5月13日)peff
ttaylorr
gitster
pack-bitmap
:オブジェクトフィルターをフィルイントラバーサルに渡します
サインオフ-作成者:Jeff King
サインオフ-作成者:Taylor Blau
ビットマップトラバーサルでは、コミットがビットマップパックファイルに含まれていないため(たとえば、最後の完全な再パック以降のプッシュまたはコミットのため)、一部のコミットを手動で実行する必要がある場合があります。
オブジェクトフィルターが与えられた場合、それをこのトラバーサルに渡しません。
ビットマップコードには、ビットマップ結果を後処理するための独自のフィルターがあります(ビットマップパックファイルに記載されているオブジェクトをフィルターで除外する必要があるため)。
また、BLOBフィルターでは、これらのフィルターを渡すパフォーマンス上の理由もありませんでした。フィルイントラバーサルでは結果からそれらを除外できますが、ブロブであるかどうかを確認するために各ツリーエントリを歩く必要があるため、そうする時間を節約することはできません。
しかし、ツリーフィルターをサポートするようになったので、節約の機会があります。tree:depth=0
フィルタは、ツリー(またはそれらが指すサブツリーやブロブのいずれか)にアクセスしないことがわかっているため、ツリーに完全にアクセスすることを回避できることを意味します。
の新しいテストp5310
はこれを示しています(「部分ビットマップ」状態はHEAD~100
、その祖先がすべてビットマップパックに含まれているが、そうでHEAD~100..HEAD
はない状態です)。
結果は次のとおりです(に対して実行linux.git
):
Test HEAD^ HEAD
-------------------------------------------------------------------------------------------------
[...]
5310.16: rev-list with tree filter (partial bitmap) 0.19(0.17+0.02) 0.03(0.02+0.01) -84.2%
節約の絶対数はそれほど多くはありませんが、最初の親のリンクを100個だけ省略したことに注意してください(linux.git
ここのバージョンでは、実際のコミットは894個です)。
より病的なケースでは、ビットマップされていないコミットの割合がはるかに高くなる可能性があります。セットアップに費用がかかるため、perfスクリプトでこのようなケースを作成する必要はありませんでした。これは、節約量をパーセンテージで示すのに十分です。
Git 2.32(Q2 2021)では、特定のオブジェクトが欠落し、遅延して取得できるようにする「プロミザーパック」の処理が最適化されています(少し)。
ジェフ・キング()によるcommit c1fa951、commit 45a187c、commit fcc07e9(2021年4月13日)を参照してください。( Junio C Hamanoによってマージされました---コミット13158b9、2021年4月30日)peff
gitster
revision
:-exclude-promisor-objectsによる解析は避けてください
サインオフ-作成者:Jeff King
が--exclude-promisor-objects
与えられると、オブジェクトをトラバースする前に、プロミザーパック内のすべてのオブジェクトを反復処理し、それらをUNINTERESTINGおよびSEENとしてマークします。
パックを反復して得たOIDをでオブジェクトに変換しますがparse_object()
、これには2つの問題があります。
- 遅いです。パックファイル内のすべてのオブジェクトのすべてのバイトをzlibで膨らませています(そしてデルタから再構築しています)
- ツリーバッファが構造体にアタッチされたままになります。つまり、ヒープの使用量が増えて、圧縮されていないすべてのツリーが同時に格納されます。
これはギガバイトになる可能性があります。
ツリーバッファを解析した後、それらを解放することで、明らかに2番目を修正できます。
しかし、関数がオブジェクトの内容をまったく調べていないことがわかります。私たちが呼び出す唯一の理由は、フラグを設定するためparse_object()
の「」が必要なことです。
ここには2つのオプションがあります。struct object
- を介してオブジェクトタイプだけを検索し
oid_object_info()
、適切なlookup_foo()
関数を呼び出すことができます
- を呼び出すことができます
lookup_unknown_object()
。これにより、構造体が得られます(後で、などの呼び出しを介してOBJ_NONE
自動変換されます)。object_as_type()
lookup_commit()
最初のものは現在のコードに近いですが、各オブジェクトの型を調べるために代償を払っています。
後者はCPUでより効率的ですが、メモリを少し浪費します(「不明な」オブジェクト構造体はすべてのオブジェクトタイプの和集合であるため、一部の構造体は必要以上に大きくなります)。
また、lookup_object()
直接呼び出すが処理する準備ができていないコードに潜在的なバグを引き起こすリスクもありますOBJ_NONE
(このようなコードはすでにバグがありますが、使用lookup_unknown_object()
頻度が低いため、隠れている可能性があります)。
ここでは2番目のオプションを選択しました。
リスクは高くないと思います(とにかくそのようなバグを見つけて修正したいと思います)。全体としてはより効率的であるはずです。
p5600の新しいテストは、改善を示しています(これはgit.gitにあります):
Test HEAD^ HEAD
-------------------------------------------------------------------------------
5600.5: count commits 0.37(0.37+0.00) 0.38(0.38+0.00) +2.7%
5600.6: count non-promisor commits 11.74(11.37+0.37) 0.04(0.03+0.00) -99.7%
このスクリプトでは、新しく複製された部分リポジトリ内のすべてのオブジェクトがプロミサーオブジェクトであるため、特に大きな改善が見られます。
したがって、それらすべてにマークを付けた後、トラバースするものは何も残っていません。