Ninja: タイムスタンプの代わりにファイル特性を使用するオプション

作成日 2018年08月14日  ·  15コメント  ·  ソース: ninja-build/ninja

他の問題で指摘されているように、タイムスタンプを使用して何かを再構築するかどうかを判断すると、問題が発生する可能性があります。 タイムスタンプは便利で比較的高速ですが、ファイルの内容のハッシュなど、ファイル自体に固有のいくつかの特性に基づいてビルドの決定を行うことが望ましい場合がよくあります。

私はgitをよく使用しますが、ブランチを変更するだけで再構築がトリガーされるのは面倒です。 理想的には、現在の作業ブランチから他のブランチに切り替え、ファイルに触れずに元のブランチに切り替えて、何も再構築する必要がないようにすることができます。 私の知る限り、ビルドシステムがタイムスタンプを使用している場合、それは不可能です。 ファイルハッシュを使用すると、この特定の問題が解決されます。

ファイルハッシュまたは他のそのような本質的なファイル特性を使用すると忍者の速度が低下する可能性があることを理解しているので、それらを使用することはオプションであるはずです。

feature

最も参考になるコメント

私も仕事でハッシュを使用し、大成功を収めました。 これは#929に基づいていますが、 https://github.com/moroten/ninja/commits/hashedに示されているように、多数のパッチが適用されています。 選択したルールのhash_input = 1は非常に便利です。 私のブランチには、ファイルがstatの頻度が高すぎて、$# O(n) #$ではなくO(n^2)になるというバグがまだ含まれています。 バグは偽のエッジに関連しています。

1つの問題は、偽のエッジをどのように処理するかです。 偽のエッジを使用して、ヘッダーファイルなどをグループ化します。 したがって、私の実装は偽のエッジを再帰的に繰り返します。 #1021のバグにも関連しています。

もう1つの考えは、ハッシュを忍者のファーストクラスのメンバーにすることです。つまり、ハッシュをビルドログに移動します。 SHA256を使用することは、Bazelのリモート実行APIのサポートを追加するための1つのステップになります。 C ++の実装は、 https://gitlab.com/bloomberg/recc/にあります。 それはかなりいいことではないでしょうか?

残念ながら、偽のエッジのセマンティクスを整理すると、下位互換性が失われる可能性があります。

全てのコメント15件

929には実装があります。 毎日何千ものビルドで(フォークで)正常に使用されていますが、マージは考慮されていません。

929はシングルスレッドであるため、 ccacheや他のソリューションのように遅くなる可能性があります。 また、ビルド定義を変更する必要がないように、これは代わりにコマンドラインフラグにする必要があると思います。

ハッシュはすべてのルールに適用されるため、コマンドラインフラグにすることはできません(または少なくともすべきではありません)。 たとえば、リンクルールのすべての入力をハッシュすることはコストがかかるため望ましくありませんが、ソースファイルとその既知の依存関係は適切な候補です。 その区別のために、それはビルド記述の一部でなければなりません。 さらに、この機能を利用するには、常にフラグを一貫して使用する必要があります。 時々だけではありません。

シングルスレッド引数への応答:はい、シングルスレッドループに命令を追加します。 実際には、単一のスレッドが機能するよりも多くの作業を取得する場合にのみ問題になります(つまり、1つのスレッドが処理できるよりも多くのルールが終了します(depslog + hashlog + ...))。 そうして初めてハッシュが痛い。 それ以外の場合、シングルスレッドループはジョブが終了するのを待機します。 -j1000の実験でも、ハッシュで忙しい忍者を見たことがありません。 (そして、高速で終了するルールの場合、ハッシュはとにかく安全な時間には面白くありません。)

また、考慮してください。murmurハッシュを使用したハッシュはかなり高速であり、大きなソースファイルでもハッシュに数ミリ秒しかかかりません。 さらに、ハッシュは、ソースファイル(および依存関係)がコンパイラーによって確認された直後に発生します。 したがって、通常はファイルシステムキャッシュから読み取られます。
ビルド中に(実行されたルールと並行して)ハッシュが発生するため、通常、全体的なビルド時間は測定可能な影響を受けません。

最後に、#929の実装はオプトインであり、この機能を使用していない人には無料で提供されます(ifステートメントを除く)。

たとえば、リンクルールのすべての入力をハッシュすることはコストがかかるため望ましくありませんが、ソースファイルとその既知の依存関係は適切な候補です。

リンカへの入力のハッシュは、リンクが完全にスキップされることが多いため(たとえば、フォーマットの変更やコメントの変更など)、特に望ましいと言えます。 オブジェクトファイルのコンパイルが1つずつ完了すると、ビルドの実行中にハッシュ計算が発生する可能性があります(ご指摘のとおり)。

本当に遅すぎる場合(たとえば、大きな静的ライブラリの場合)、中間ファイルではなく、純粋な入力に対してのみハッシュを実装することを検討できます。 これで、少なくとも「Gitブランチを切り替えると完全に再構築される」というケースは解決されます。

さらに、この機能を利用するには、常にフラグを一貫して使用する必要があります。 時々だけではありません。

これは利点だと思います。単一のブランチで作業していて、高速に反復したい場合は、ハッシュを使用しません。 さまざまな機能のブランチを比較する場合は、ハッシュを使用します。

さらに、この機能を利用するには、常にフラグを一貫して使用する必要があります。 時々だけではありません。

これは利点だと思います。単一のブランチで作業していて、高速に反復したい場合は、ハッシュを使用しません。 さまざまな機能のブランチを比較する場合は、ハッシュを使用します。

ただし、非ハッシュからハッシュに切り替える方法が必要になります。つまり、ファイルの現在の状態をハッシュする必要があるため、次の再構築でハッシュを使用できます(おそらく存在しなかったか、存在していません)。フラグを渡さなかった場合の日付)。

ハッシュはまだタイムスタンプを最初に使用するので、タイムスタンプが一致する場合は、ハッシュを比較する必要はないと思います。 つまり、最初の数回のビルドで一部のファイルが不必要に再コンパイルされる可能性がありますが、それは頻繁には発生しないはずです(ほとんどの場合、タイムスタンプヒューリスティックは結局のところ正しいです)。

私も仕事でハッシュを使用し、大成功を収めました。 これは#929に基づいていますが、 https://github.com/moroten/ninja/commits/hashedに示されているように、多数のパッチが適用されています。 選択したルールのhash_input = 1は非常に便利です。 私のブランチには、ファイルがstatの頻度が高すぎて、$# O(n) #$ではなくO(n^2)になるというバグがまだ含まれています。 バグは偽のエッジに関連しています。

1つの問題は、偽のエッジをどのように処理するかです。 偽のエッジを使用して、ヘッダーファイルなどをグループ化します。 したがって、私の実装は偽のエッジを再帰的に繰り返します。 #1021のバグにも関連しています。

もう1つの考えは、ハッシュを忍者のファーストクラスのメンバーにすることです。つまり、ハッシュをビルドログに移動します。 SHA256を使用することは、Bazelのリモート実行APIのサポートを追加するための1つのステップになります。 C ++の実装は、 https://gitlab.com/bloomberg/recc/にあります。 それはかなりいいことではないでしょうか?

残念ながら、偽のエッジのセマンティクスを整理すると、下位互換性が失われる可能性があります。

その間、私はChromiumでブランチを切り替えるユースケースの解決策を考案しました(これは特に苦痛です)。 私が使用する小さなGoプログラムとスクリプトはここにあります: https ://github.com/bromite/mtool

自由にユースケースに適合させてください。Chromiumで機能する場合は、小規模なビルドプロジェクトでも機能するはずです(実行にかかる時間はごくわずかです)。 唯一の欠点は、私がそこで公開したスクリプトをそのまま使用すると、各gitリポジトリの親ディレクトリに.mtoolファイルが散らばってしまうことですが、グローバルなgitignoreできないものはありません。

ハッシュのニーズにgit ls-files --stage出力を使用していることに注意してください。 ビルドがそれらに依存している場合は、インデックス付けされていないファイルもハッシュするようにgitに要求することは可能です(ただし効率は低下します)。

機能的には、ここで説明している機能を実装するために、ninjaは内部で(gitに依存せずに)同じことを実行でき、同様のパフォーマンス結果が得られると予想されます。

ざっと見てみると、これらの忍者パッチは、入力ファイルに加えてコンパイラのコマンドラインをハッシュしていないようです。 私は何かが足りないのですか?

コマンドラインはすでに忍者によってハッシュされ、ビルドログに保存されています。

ハッシュはまだタイムスタンプを最初に使用するので、タイムスタンプが一致する場合は、ハッシュを比較する必要はないと思います。 つまり、最初の数回のビルドで一部のファイルが不必要に再コンパイルされる可能性がありますが、それは頻繁には発生しないはずです(ほとんどの場合、タイムスタンプヒューリスティックは結局のところ正しいです)。

それは非常に間違っているでしょう。 タイムスタンプが一致しない場合、ハッシュの比較は省略できます。 ビルドシステムは、依存関係が変更されたと想定できます。実際に変更されていない(変更されただけの)場合、ビルドは最適ではありませんが正しいものになります。 ただし、タイムスタンプが一致する場合でも、依存関係が変更され、そのタイムスタンプが強制的にリセットされた可能性があります(編集:またはターゲットがタッチされたため、依存関係よりも新しくなった可能性があります)。 ハッシュを比較して再確認しないと、ビルドが正しくなくなります。

タイムスタンプが一致すると、忍者はすでにすべての作業を想定してスキップしていると思います。 したがって、あなたの例では、これはとにかく間違ったビルドになります。

私は(おそらく私の無知のために)異なる出力を生成し、以前のタイムスタンプを保持するツールを知りません。 なぜそうするのでしょうか?

IMHO、タイムスタンプが一致するときにハッシュチェックをスキップすることは非常に有効な最適化です。

ハッシュチェックは、Ninjaによってすでに提供されている既存のセマンティクスを維持しながら、単にいくつかの「偽のダーティ」な再構築を回避します。

問題は、偽のダーティな再構築ではなく、偽のクリーンな非再構築です。 gitチェックアウトは、上書きするすべてのものに影響します。 ターゲットを依存関係よりも新しくすることができます(そうです、人々はさまざまな正当な理由で生成されたコードをコミットします)。 この場合、ハッシュチェックにより、フォールスクリーンの非再構築が防止されます。

@rulatir私はあなたが言っていることをほとんど理解していると思います:)それが、ハッシュチェックに基づくBazelや他のビルドシステムが実際にツリー内のターゲット出力に反する理由の1つだと思います。

ただし、ビルドシステムがターゲットが以前の既知の時間よりも新しいかどうかをチェックし、必要に応じて再構築する場合、この問題は解決されませんか?

@rulatir私はあなたが言っていることをほとんど理解していると思います:)それが、ハッシュチェックに基づくBazelや他のビルドシステムが実際にツリー内のターゲット出力に反する理由の1つだと思います。

ハッシュはファイルの場所にどのように依存しますか?

ただし、ビルドシステムがターゲットが以前の既知の時間よりも新しいかどうかをチェックし、必要に応じて再構築する場合、この問題は解決されませんか?

タイムスタンプを使用する主な利点は、「以前から知られている」バージョンの署名を追跡する別個のデータベースを維持する必要がないことであると理解しています。 あなたがその利益を放棄することをいとわないのなら、なぜそれらの署名はハッシュであるべきではないのですか?

このページは役に立ちましたか?
0 / 5 - 0 評価