WWDC Quick Look 💓 By SwiftGGTeam
Demystify parallelization in Xcode builds

Demystify parallelization in Xcode builds

元の動画を見る

ハイライト

Xcode 14 では、新しい Swift ドライバーの統合、エミットモジュールの分離、および Eager Linking を通じて、クロスターゲットのビルド タスクを早期に並行して実行できます。同時に、ビルド タイムライン ツールを使用すると、開発者はビルド内の並列ボトルネックを初めて直感的に確認できるようになります。

主要内容

構築が遅い根本原因: クリティカル パスが長すぎる

Cmd+B を押すと、Xcode ビルド システムはプロジェクト全体の依存関係グラフを分析します。コンパイルにより .o ファイルが生成され、リンカーは .o ファイルを使用して実行可能ファイルを生成します。これらのタスク間には、明確な入力と出力の依存関係があります。

プロジェクトが大きくなるにつれて、依存関係グラフはより複雑になります。ビルド システムは、依存関係のないタスクに優先順位を付け、完了後に下流のタスクのロックを解除します。ただし、「クリティカル パス」を形成するためにいくつかのタスクが常につながっています。CPU コアの数に関係なく、このパス上のタスクは 1 つずつ実行する必要があります。

クリティカル パスを短縮する方法は 1 つだけです。パスの依存関係を解消し、より多くのタスクを並列化できるようにすることです。

Xcode 14 の 2 つのレベルの並列化

Apple はビルドの並列処理に 2 つのレベルで対処します。

  1. ターゲット内部: コンパイル、リソースのコピー、コード署名などのビルド フェーズは、入力と出力の依存関係に従ってスケジュールされ、無関係なフェーズは並行して実行されます。
  2. ターゲット間: エミットモジュールの分離と Eager Linking により、ダウンストリーム ターゲットは、アップストリームが完全にコンパイルされ、リンクされるまで開始する前に待つ必要がなくなりました。

詳細

ビルド タイムライン: ビルドの並列処理に関する真実を初めて確認する

06:29

Xcode 14 の新機能、ビルド タイムラインは、ビルドのパフォーマンスを理解するための中心的なツールです。これは、階層ではなく並列処理によって編成される従来のビルド ログとは異なります。

タイムラインの見方:

  • 横軸 = 時間、タスクが長いほど、占める範囲が広くなります。
  • 縦軸 = 並列度、同時行数は同時に実行されるタスクの数を表します。
  • 色 = 異なるターゲット
  • 空白 = タスクは入力を待機しています。これが最適化の目標です。

開き方: ビルドが完了したら、ビルド ログのエディター オプションで [アシスタント] を選択すると、下部にビルド タイムラインが表示されます。タスクが選択されると、ビルド ログは対応するエントリを自動的に見つけます。そしてその逆も同様です。

Option キーを押しながらエリアを選択すると、ビューがその期間に自動的にズームされます。これは、ローカルのボトルネックを分析するときに役立ちます。

ターゲット内での並列化: スクリプトフェーズの依存関係宣言

10:35

ビルド フェーズでは、ターゲットのビルド ステップを説明します。コンパイル、リソースのコピー、リンク、スクリプトの実行はすべてビルド フェーズです。

リソースのコンパイルとコピーは相互に依存しないため、並行して実行できます。ただし、リンカーには .o ファイルが必要であるため、リンクはコンパイルが完了するまで待つ必要があります。

スクリプト実行フェーズは特別です。スクリプトには自動入出力推論がないため、手動で宣言する必要があります。宣言が不完全な場合、ビルド システムはデータ競合を避けるためにスクリプトをシリアルに実行します。

Xcode 14 では ユーザー スクリプト サンドボックス が導入され、スクリプトは宣言された入力と出力にのみアクセスするように強制されます。

// 在 Build Settings 中设置
ENABLE_USER_SCRIPT_SANDBOXING = YES

スクリプトが宣言されていないファイルにアクセスしようとすると、ビルドは単純に失敗し、問題のあるパスがリストされます。これは、欠落している依存関係宣言を発見するのに役立ちます。

スクリプトが入力と出力を正しく宣言すると、スクリプトを開くことができます。FUSE_BUILD_SCRIPT_PHASES = YES複数のスクリプトを並行して実行します。

キーポイント:

  • サンドボックスはセキュリティ機能ではなく、プロジェクトのソース コード ディレクトリと派生データのみをカバーします。
  • 入力および出力の宣言が欠落していると、不正確なインクリメンタル ビルドが発生する可能性があります
  • 2 つのスクリプトにファイルの依存関係がある場合は、それらを正しく宣言する必要があります。宣言しないと、並列実行によりデータ競合が発生します。

Swift ドライバーの書き換え: ビルド システムとコンパイラーの緊密な統合

19:50

Xcode 14 より前では、Xcode Build System と Swift Driver はタスクを個別にスケジュールしていました。ドライバーは Swift コンパイルをサブタスクに分割する役割を果たしますが、ビルド システムはこれらのサブタスクを認識できず、それらをグローバルに最適化することはできません。

Xcode 14 の Swift ドライバーは Swift で完全に書き直され、ビルド システムと深く統合されています。すべてのサブタスクは統合タスク プールに入り、ビルド システムによって均一にスケジュールされます。

これにより、次の 2 つの直接的な利点がもたらされます。

  1. スケジューラは、オーバーサブスクリプションを回避するために、実際の CPU コア数に基づいてタスクを割り当てることができます。
  2. コア数の多いマシンでのアイドル時間の大幅な削減

Emit-Module の分離: ダウンストリーム ターゲットはより早くコンパイルを開始します

21:36

Swift ターゲットがコンパイルされると、他のターゲットはコンパイルを開始するためにそのモジュール ファイルを必要とします。以前は、すべてのコンパイル タスクが完了するまでモジュールを生成できませんでした。

Xcode 14 と Swift 5.7 は、モジュール生成を独立した emit-module タスクに分割し、コンパイルされた .o ファイルに依存せずにすべてのソース ファイルから直接モジュールを生成します。

// 编译流程变化
// 以前:Compile all → Link → Emit module → 下游开始编译
// 现在:Emit module (并行) → 下游收到 module 就开始编译
//        Compile all (并行) → Link

ダウンストリーム ターゲットは、上流リンクの完了を待たずに、emit-module が完了するとすぐにコンパイルを開始できます。これにより、クリティカル パスでの待ち時間が大幅に短縮されます。

熱心なリンク: リンクは高度なものにすることもできます

22:28

通常の状況では、ターゲット B がターゲット A にリンクする場合、ターゲット A のリンクが完了するまで待機する必要があります。 Eager Link はこの依存関係を解消します。

Emit-module タスクは、テキスト形式のダイナミック ライブラリ スタブ (TBD ファイル) も生成します。これには、ターゲット ターゲットが最終的にエクスポートするシンボルがリストされます。ターゲット B のリンク タスクは、ターゲット A の完全なリンク生成物を待つ代わりに、このスタブに依存できます。

有効にする方法:

// Build Settings
EAGER_LINKING = YES

キーポイント:

  • 純粋な Swift ターゲット間の動的リンクにのみ適用されます
  • リンクされた製品ではなく、ターゲットの発行モジュール製品に依存します
  • スタブにはシンボルのリストが含まれており、リンカはこの情報を使用して事前に動作を開始します。

重要ポイント

  • ビルド タイムラインを開いてプロジェクトを分析します タイムラインで大きな空き領域を見つけます。これらはブロックされたタスクです。どのターゲットのコンパイルに時間がかかるかを確認し、不要な依存関係チェーンがないか確認します。

  • すべてのスクリプト実行フェーズの完全な入出力を宣言します オンにするENABLE_USER_SCRIPT_SANDBOXING = YESビルド システムに不足している依存関係を見つけさせます。修理後に開けるFUSE_BUILD_SCRIPT_PHASES = YESスクリプトを並行して実行します。

  • 大きなフレームワークを小さな Swift パッケージに分割します エミットとモジュールの分離と積極的なリンクの利点は、モジュール型プロジェクトでより明らかです。小さなモジュールの Emit-module はより速く完了し、ダウンストリーム ターゲットはより早くコンパイルを開始できます。

  • CI でのビルド時間の傾向を監視 ビルド タイムラインは CI で使用できます。各フル ビルドの時間を記録し、異常な増加が見つかった場合はタイムラインをすぐに分析して新しいボトルネックを見つけます。

  • Xcode 14 にアップグレードした後にビルドのパフォーマンスを再評価します Swift ドライバーの書き換えによってもたらされるスケジュールの最適化は、すべてのプロジェクトに有効であり、コードの変更は必要ありません。同じマシン上で Xcode 13 と 14 のビルド時間を比較して、メリットを定量化します。

関連セッション

コメント

GitHub Issues · utterances