動的実行

問題を報告 ソースを表示

動的実行は 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 フラグを使用すると、ローカル実行で出力に直接書き込むことができますが、リモート実行が先に終了した場合は、リモート実行によって中止されるため、この問題を回避できます。

動的実行のブランチのいずれかが最初に終了し、それが失敗した場合、アクション全体が失敗します。これは、ローカル実行とリモート実行の違いに気づかれないようにするための意図的な選択です。

動的実行とそのロックの仕組みの詳細については、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 より大きい値に設定すると、多くのアクションがスケジュール待ちになっているときに、ローカルでスケジュールされるアクションの数が調整されます。1 に設定すると、使用可能な CPU の数だけ(--local_cpu_resources に従う)アクションをスケジュールできます。値が小さいほど、実行可能なアクション数が多いほど、スケジュール設定されるアクションの数が少なくなります。これは直感に反するように聞こえるかもしれませんが、優れたリモート システムでは、多くのアクションが実行されている場合にローカル実行はあまり役に立ちません。また、ローカル 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 の場合は上記参照)。ただし、考えられる失敗の原因が解消されます。