動的実行は、Bazel バージョン 0.21 以降の機能です。同じアクションのローカル実行とリモート実行が並行して開始され、最初に完了したブランチの出力が使用され、他のブランチがキャンセルされます。リモート ビルドシステムの実行能力や大規模な共有キャッシュと、ローカル実行の低レイテンシを組み合わせることで、クリーン ビルドと増分ビルドの両方で最高のパフォーマンスを実現します。
このページでは、動的実行を有効化、チューニング、デバッグする方法について説明します。ローカル実行とリモート実行の両方が設定されていて、パフォーマンスを向上させるために 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
フラグを使用すると、ローカル実行で出力を直接書き込めるようにすることで、この問題を回避できます。ただし、リモート実行が先に完了した場合は、ローカル実行が中止されます。
動的実行のブランチの 1 つが最初に終了しても失敗した場合、アクション全体が失敗します。これは、ローカル実行とリモート実行の違いが見過ごされないようにするための意図的な選択です。
動的実行とそのロックの仕組みについて詳しくは、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 を使用している場合は上記を参照)。ただし、失敗の原因となる可能性のあるものがいくつか取り除かれます。