動的実行

問題を報告 ソースを表示

動的実行は、同じアクションのローカル実行とリモート実行を並列に開始する Bazel の機能で、最初に完了したブランチからの出力を使用して他のブランチをキャンセルします。リモート ビルドシステムの実行能力や大規模な共有キャッシュを、ローカル実行の低レイテンシと組み合わせることで、クリーンビルドと増分ビルドの両方の長所を享受できます。

このページでは、動的実行を有効化、調整、デバッグする方法について説明します。このページは、ローカル実行とリモート実行の両方を設定し、パフォーマンスを向上させるために Bazel の設定を調整しようとしている方を対象としています。リモート実行をまだ設定していない場合は、まず Bazel のリモート実行の概要をご覧ください。

動的実行を有効にしますか?

動的実行モジュールは Bazel の一部ですが、動的実行を利用するには、同じ Bazel の設定からローカルとリモートの両方でコンパイルできる必要があります。

動的実行モジュールを有効にするには、--internal_spawn_scheduler フラグを Bazel に渡します。これにより、dynamic という新しい実行戦略が追加されます。これで、--strategy=Javac=dynamic など、動的に実行するニーモニックの戦略としてこれを使用できるようになりました。動的実行を有効にするニーモニックを選択する方法については、次のセクションをご覧ください。

動的戦略を使用するニーモニックの場合、リモート実行戦略は --dynamic_remote_strategy フラグから取得され、ローカル戦略は --dynamic_local_strategy フラグから取得されます。--dynamic_local_strategy=worker,sandboxed を渡すと、動的実行のローカル ブランチのデフォルトがワーカーまたはサンドボックス実行の順序で設定されます。--dynamic_local_strategy=Javac=worker を渡すと、Javac ニーモニックのデフォルトがオーバーライドされます。リモート バージョンの場合も同様です。どちらのフラグも複数回指定できます。ローカルで実行できないアクションは通常どおりリモートで実行されます。その逆も同様です。

リモート システムにキャッシュがある場合、--dynamic_local_execution_delay フラグを指定すると、リモート システムがキャッシュ ヒットを示した後、ローカル実行にミリ秒単位で遅延が追加されます。これにより、キャッシュ ヒットが増える可能性がある場合に、ローカル実行を回避できます。デフォルト値は 1, 000 ミリ秒ですが、通常キャッシュ ヒットに要する時間よりも少し長くなるように調整する必要があります。実際の時間は、リモート システムとラウンドトリップ時間の両方によって異なります。通常、ラウンドトリップ レイテンシが増加するのに十分な距離にあるユーザーがいる場合を除き、特定のリモート システムのすべてのユーザーが同じ値になります。Bazel プロファイリング機能を使用すると、一般的なキャッシュ ヒットにかかる時間を確認できます。

動的実行は、永続ワーカーだけでなく、ローカルのサンドボックス戦略でも使用できます。永続ワーカーは、動的実行で使用されるとサンドボックス化で自動的に実行されます。マルチプレックス ワーカーは使用できません。Darwin システムと Windows システムでは、サンドボックス化された戦略が遅くなる可能性があります。--reuse_sandbox_directories を渡すと、これらのシステムでサンドボックスを作成するオーバーヘッドを削減できます。

動的実行は standalone 戦略で実行することもできますが、standalone 戦略は実行開始時に出力ロックを取得する必要があるため、リモート戦略が先に終了することを効果的にブロックします。--experimental_local_lockfree_output フラグを使用すると、この問題を回避できます。これにより、ローカル実行で出力に直接書き込むことができます。ただし、先に行われた場合はリモート実行で中断されます。

動的実行のブランチの 1 つが最初に終了したものの失敗した場合、アクション全体が失敗します。これは、ローカル実行とリモート実行の違いが見逃されることを防ぐために、意図的に選択されています。

動的実行とそのロックの仕組みに関する詳しい背景情報については、Julio Merino の優れたブログ投稿をご覧ください。

動的実行を使うのはどのような場合ですか?

動的実行には、なんらかの形式のリモート実行システムが必要です。キャッシュミスはアクションの失敗と見なされるため、現在、キャッシュ専用のリモート システムは使用できません。

すべてのタイプのアクションがリモート実行に適しているわけではありません。最適な候補は、たとえば、永続ワーカー を使用することでローカルならば本質的に高速であるものか、またはリモート実行のオーバーヘッドが実行時間の大半を占めるほど高速に実行されるワーカーです。ローカルで実行される各アクションは、ある程度の CPU リソースとメモリリソースをロックするため、これらのカテゴリに該当しないアクションを実行すると、実行が遅れるだけです。

リリース 5.0.0-pre.20210708.4 以降、パフォーマンス プロファイリングには、ワーカーの実行に関するデータ(動的実行競合に失敗した後の処理リクエストの完了に要した時間など)が含まれています。動的実行ワーカー スレッドがリソースの取得にかなりの時間を費やしている場合や、async-worker-finish に多くの時間を費やしている場合は、ワーカー スレッドを遅延させるローカル アクションが遅い可能性があります。

動的実行パフォーマンスが低いデータのプロファイリング

8 つの Javac ワーカーを使用する上記のプロファイルでは、多くの Javac ワーカーが競合に負けて async-worker-finish スレッドで作業を終了しています。これは、非ワーカーのニーモニックがワーカーを遅延させるのに十分なリソースを使用していることが原因でした。

動的実行パフォーマンスに優れたデータのプロファイリング

Javac のみを動的実行で実行した場合、開始されたワーカーの約半分のみが処理の開始後に競合に負けます。

以前推奨されていた --experimental_spawn_scheduler フラグは非推奨となりました。これにより、動的実行が有効になり、dynamic がすべてのニーモニックのデフォルト戦略として設定されます。多くの場合、この種の問題が生じます。

パフォーマンス

動的実行のアプローチでは、ローカルとリモートに十分なリソースがあり、全体的なパフォーマンスを改善するには、追加のリソースを費やすだけの価値があることを前提としています。ただし、リソースを過度に使用すると、Bazel 自体やその動作のマシンが遅くなったり、リモート システムに予期しない負荷がかかる可能性があります。動的実行の動作を変更するには、いくつかの方法があります。

--dynamic_local_execution_delay は、リモート ブランチの開始からローカル ブランチの開始をミリ秒数だけ遅延させます。これは、現在のビルド中にリモート キャッシュ ヒットがあった場合のみです。これにより、リモート キャッシュの恩恵を受けるビルドで、ほとんどの出力がキャッシュで見つかる可能性が高い場合に、ローカル リソースを無駄にすることがなくなります。キャッシュの品質によっては、これを減らすことで、より多くのローカル リソースを使用する代わりに、ビルド速度が向上する場合があります。

--experimental_dynamic_local_load_factor は、試験運用版の高度なリソース管理オプションです。0 ~ 1 の値を取ります(0 の場合、この機能が無効になります)。 0 より大きい値に設定すると、多くのアクションのスケジュール設定が待っているときに、Bazel はローカルでスケジュール設定されたアクションの数を調整します。1 に設定すると、(--local_cpu_resources に従い)使用可能な CPU の数だけスケジュールされるアクションの数が増えます。値を小さくすると、実行できるアクションの数が多くなり、それに応じてスケジュールされるアクションの数が少なくなります。一見矛盾するように聞こえるかもしれませんが、優れたリモート システムでは、多数のアクションが実行されている場合、ローカル実行はあまり役に立ちません。また、ローカル CPU をリモート アクションの管理に充てる方が適切な場合があります。

--experimental_dynamic_slow_remote_time は、リモート ブランチが少なくともこの時間にわたって実行されている場合、ローカル ブランチの開始を優先します。通常、レースに勝つ可能性が最も高いため、最後にスケジュールされたアクションが優先されますが、リモート システムがハングアップしたり、非常に長い時間がかかったりした場合は、ビルドを先に進める可能性があります。これは、修正すべきリモート システムの問題が隠れてしまう可能性があるため、デフォルトでは有効になっていません。このオプションを有効にする場合は、リモート システムのパフォーマンスをモニタリングしてください。

--experimental_dynamic_ignore_local_signals を使用すると、特定のシグナルが原因でローカルのスポーンが終了したときに、リモート ブランチに引き継ぐことができます。これは、主に、リソースの使用量が多すぎるとワーカー プロセスが強制終了される可能性があるワーカー リソースの上限(--experimental_worker_memory_limit_mb--experimental_worker_sandbox_hardening--experimental_sandbox_memory_limit_mb を参照)と併用して有用です。

JSON トレース プロファイルには、パフォーマンス関連のグラフが多数含まれています。これは、パフォーマンスとリソース使用量のトレードオフを改善する方法の特定に役立ちます。

トラブルシューティング

動的実行の問題は、ローカル実行とリモート実行の特定の組み合わせでのみ発生する可能性があるため、わかりにくく、デバッグが難しい場合があります。--debug_spawn_scheduler は、こうした問題のデバッグに役立つ動的実行システムからの出力を追加します。--dynamic_local_execution_delay フラグと、リモートジョブとローカルジョブの数を調整して、問題を簡単に再現することもできます。

standalone 戦略を使用した動的実行で問題が発生した場合は、--experimental_local_lockfree_output を使用せずに実行してみるか、ローカル アクションをサンドボックス化して実行してください。これにより、ビルドが多少遅くなる可能性があります(Mac または Windows の場合は上記をご覧ください)。ただし、エラーの原因となる問題は解消できます。