動的実行は、バージョン 0.21 以降の 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 の頭字語のデフォルトがオーバーライドされます。リモート バージョンも同様に機能します。どちらのフラグも複数回指定できます。アクションをローカルで実行できない場合は、通常どおりリモートで実行されます。その逆も同様です。
リモート システムにキャッシュがある場合、--local_execution_delay
フラグは、リモート システムがキャッシュヒットを示した後に、ローカル実行にミリ秒単位の遅延を追加します。これにより、キャッシュ ヒットが発生する可能性が高い場合にローカル実行を実行する必要がなくなります。デフォルト値は 1, 000 ミリ秒ですが、キャッシュ ヒットに通常かかる時間より少し長くなるように調整する必要があります。実際の時間は、リモート システムとラウンドトリップの所要時間の両方によって異なります。通常、この値は、一部のユーザーが十分に離れているためにラウンドトリップ レイテンシが増加する場合を除き、特定のリモート システムのすべてのユーザーで同じになります。Bazel のプロファイリング機能を使用して、一般的なキャッシュヒットの所要時間を調べることができます。
動的実行は、ローカル サンドボックス化戦略と永続ワーカーで使用できます。永続ワーカーは、動的実行で使用すると自動的にサンドボックスで実行されます。マルチプレックス ワーカーは使用できません。Darwin システムと Windows システムでは、サンドボックス化された戦略が遅くなることがあります。--reuse_sandbox_directories
を渡すと、これらのシステムでサンドボックスを作成するオーバーヘッドを減らすことができます。
動的実行は standalone
戦略で実行することもできますが、standalone
戦略は実行を開始するときに出力ロックを取得する必要があるため、リモート戦略が先に完了するのを効果的にブロックします。--experimental_local_lockfree_output
フラグを使用すると、ローカル実行が出力に直接書き込むことができますが、リモート実行が先に完了した場合は、リモート実行によって中止されます。これにより、この問題を回避できます。
動的実行のいずれかのブランチが先に完了しても失敗した場合、アクション全体が失敗します。これは、ローカル実行とリモート実行の違いが気付かれないことを防ぐための意図的な選択です。
動的実行とそのロックの仕組みについて詳しくは、Julio Merino の優れたブログ投稿をご覧ください。
動的実行を使用するタイミング
動的実行には、なんらかのリモート実行システムが必要です。現在のところ、キャッシュのみのリモート システムを使用することはできません。キャッシュミスは失敗したアクションと見なされます。
すべてのタイプのアクションがリモート実行に適しているわけではありません。最適な候補は、永続ワーカーの使用などにより、ローカルで本質的に高速であるもの、またはリモート実行のオーバーヘッドが実行時間を圧倒するほど高速に実行されるものです。ローカルで実行される各アクションは、一定量の CPU リソースとメモリ リソースをロックするため、これらのカテゴリに該当しないアクションを実行しても、該当するアクションの実行が遅れるだけです。
リリース 5.0.0-pre.20210708.4 以降、パフォーマンス プロファイリングには、動的実行競合に敗れた後のワーク リクエストの完了に費やされた時間など、ワーカーの実行に関するデータが含まれます。動的実行ワーカー スレッドがリソースの取得にかなりの時間を費やしている場合や、async-worker-finish
で多くの時間を費やしている場合は、ローカル アクションが遅く、ワーカー スレッドが遅延している可能性があります。
8 つの Javac ワーカーを使用する上記のプロファイルでは、多くの Javac ワーカーが競合に敗れ、async-worker-finish
スレッドで作業を完了しています。これは、ワーカー以外のメモニクスがワーカーを遅らせるのに十分なリソースを消費したことが原因でした。
Javac のみを動的実行で実行すると、開始されたワーカーの約半分だけが、作業を開始した後に競合に敗れてしまいます。
以前推奨されていた --experimental_spawn_scheduler
フラグは非推奨になりました。動的実行が有効になり、すべてのメモニカのデフォルト戦略として dynamic
が設定されます。このため、このような問題が発生することがあります。
トラブルシューティング
動的実行の問題は、ローカル実行とリモート実行の特定の組み合わせでのみ発生するため、問題がわかりにくく、デバッグが難しい場合があります。--debug_spawn_scheduler
は、これらの問題のデバッグに役立つ動的実行システムからの追加出力を追加します。--local_execution_delay
フラグとリモートジョブとローカルジョブの数を調整して、問題を簡単に再現することもできます。
standalone
戦略を使用した動的実行で問題が発生した場合は、--experimental_local_lockfree_output
なしで実行するか、ローカル アクションをサンドボックス化された状態で実行してみてください。これによりビルドが少し遅くなる可能性があります(Mac または Windows の場合は上記を参照)。ただし、エラーの原因となる可能性のあるものが除外されます。