動的実行

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

動的実行の分岐の 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 の場合は上記の説明をご覧ください)。ただし、失敗の原因を取り除くことができます。