swift-runtime-performance について
swift-runtime-performanceは、アロケーション、ARC、ディスパッチ、コピー、特殊化など、Swiftの実行時コストをレビューするエージェントスキルです。測定に基づく変更を促し、意味上の影響や保守上のトレードオフも考慮します。
主な機能
- 01実行時コストの分類
- 02ホットパスかどうかの確認
- 03プロファイルとコンパイラー証拠の活用
- 04実行時コストへの絞った変更案
- 05実装上のトレードオフの説明
活用シーン
- 01アロケーションやARC負荷のレビュー
- 02ディスパッチやジェネリクス特殊化の調査
- 03コピーオンライトコストの評価
- 04Swiftのunsafe境界の検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ livsy90/ ios-performance-agent-skills --skill swift-runtime-performanceインストール後、Claude Code で/swift-runtime-performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/allocation-and-layout.mdarc-and-ownership.mdconcurrency-runtime.mdcow-and-large-values.mddispatch-and-specialization.mdexistentials-generics-opaque-types.mdmodularization-and-linking.mdsil-inspection.mdunsafe-swift.md詳細
Swift ランタイムパフォーマンス
目的
このスキルは、あらゆる抽象化を問題視することなく、Swift コードのランタイムレベルのパフォーマンスコストをレビューするために使用します。アロケーション、ARC のトラフィック、ディスパッチ、特化、コピー、オプティマイザーからの可視性、安全でないメモリー境界など、具体的なコストに焦点を当てます。
このスキルは、実際のホットパスのランタイムコストと、理論上のマイクロ最適化とをエージェントが区別できるようにするものです。
このスキルを使用する場合
次のような Swift のランタイム動作がタスクに関わる場合に、このスキルを使用します。
- ヒープアロケーション、スタックとヒープのストレージ、オブジェクトレイアウト、ボックス化された値、クロージャーコンテキスト。
- ARC の retain/release トラフィック、所有権、ライフタイム、weak/unowned 参照、クロージャーのキャプチャ。
- ホットパスにおける直接ディスパッチ、クラスディスパッチ、Objective-C ディスパッチ、ウィットネスディスパッチ、動的ディスパッチ。
any Protocol、some Protocol、ジェネリクス、型消去、特化、ホットコードの非特化。- コピーオンライトコレクション、大きな値、カスタム COW ストレージ、繰り返し発生するコピー。
- 最適化済み SIL の調査、コンパイラー最適化、インライン化、デバーチャライズ、特化を示す証拠。
- unsafe Swift、ポインターのライフタイム、メモリーバインディング、エイリアシング、バッファーの変更、安全でない領域を囲む安全なラッパー。
- オプティマイザーからの可視性に影響するモジュール境界、
@inlinable、@usableFromInline、@frozen、公開 API のレジリエンスに関するトレードオフ。
このスキルを使用しない場合
次の用途には、このスキルを使用しないでください。
- ランタイムパフォーマンスへの懸念がない、一般的な Swift の構文や API の使用方法に関する質問。
- 主な問題が pre-main、dyld、静的イニシャライザー、SDK の起動、最初のフレーム、最初の操作にあるアプリの起動調査。
- 主な問題がアイデンティティ、無効化、状態のスコープ、レイアウト、描画、アニメーション、スクロールにある SwiftUI のパフォーマンス問題。
- 主な問題がタスクのライフタイム、アクター分離、MainActor の応答性、キャンセル、AsyncSequence のクリーンアップ、再入可能性、エグゼキューターの動作にある Swift Concurrency の問題。
- 主なタスクがツールの選択、トレースの解釈、サインポストの設計、XCTest のメトリクス、MetricKit、本番環境のシグナルである、プロファイリングのワークフローに関する質問。
- ホットパスに特定のランタイムコストがない、広範なアーキテクチャに関する質問。
別のスキルのほうがより具体的な場合は、まずそちらを使用し、このスキルはランタイムに関するサブ問題に限って使用してください。
関連スキルとの境界
ランタイムに関するアドバイスを適用する前に、次の境界を確認してください。
- タスクの中心がアクター、タスク、MainActor、キャンセル、AsyncSequence、継続、タスクグループ、エグゼキューターの動作、非同期処理中の応答性である場合は、
swift-concurrency-performanceを使用します。 - タスクの中心がコールド起動、ウォーム起動、pre-main、dyld、フレームワークの読み込み、静的イニシャライザー、AppDelegate、SwiftUI の
App、最初のフレーム、最初の操作、起動メトリクスである場合は、ios-launch-performanceを使用します。 - タスクの中心が SwiftUI のボディ評価、無効化、アイデンティティ、状態の所有権、依存関係のスコープ、
List、LazyVStack、レイアウト、描画、アニメーション、ビューのライフサイクル処理である場合は、swiftui-performanceを使用します。 - タスクの中心が Instruments のテンプレートの選択、トレースの解釈、XCTest のメトリクス、MetricKit、サインポスト、メモリーグラフ、ハング、ヒッチ、CPU、アロケーション、ディスク I/O、ネットワーキング、本番環境のテレメトリーである場合は、
ios-performance-profilingを使用します。 - 関連するタスクを通じて、アロケーションの頻発、ARC のトラフィック、存在型のボックス化、ウィットネスディスパッチ、非特化ジェネリクス、COW コピーの繰り返し、安全でないメモリー境界など、Swift のランタイムレベルのコストが判明した場合は、このスキルを使用します。
基本原則
ソースコードの見た目だけを根拠に最適化しないでください。
まず、次の点を特定します。
- コードがホットパス上にあるか。
- どのようなランタイムコストが疑われているか。
- そのコストが測定結果、コンパイラー出力、小規模なベンチマークで確認できるか。
- 提案する変更がセマンティクスを保ち、測定対象のパスを改善するか。
- その変更がどのようなトレードオフをもたらすか。
ランタイムの最適化が有効なのは、重要なパスのコストを削減できる場合に限られます。
ランタイムコストの分類
変更を提案する前に、疑われる問題を分類します。
次のカテゴリを使用してください。
- 割り当て — ヒープオブジェクト、ボックス、クロージャコンテキスト、エグジステンシャルコンテナ、一時オブジェクト、中間コレクション。
- ARC — retain/release の処理、クロージャキャプチャ、weak/unowned へのアクセス、ブリッジされたオブジェクトのライフタイム、参照を使った値の格納。
- ディスパッチ — 動的ディスパッチ、ウィットネスディスパッチ、Objective-C ディスパッチ、クロージャ呼び出し、デバーチャライズの見逃し。
- エグジステンシャルとジェネリクス —
any、some、型消去、ジェネリックの特殊化、特殊化されていないホットパス、ボックス化。 - コピー — COW ストレージ、大きな値、繰り返されるコレクションの変更、防御的コピー、ブリッジング時のコピー。
- コンパイラ最適化 — インライン化、特殊化、デバーチャライズ、モジュールの可視性、レジリエンス境界、最適化後の SIL 出力。
- 安全でない境界 — ポインターのライフタイム、バインディング、アラインメント、エイリアシング、変更、エスケープするバッファー、安全なラッパー。
- モジュール境界 — パブリック API の可視性、
@inlinable、@usableFromInline、@frozen、ABI レジリエンス、オプティマイザーからの可視性。
基本ワークフロー
- ユーザーに見える症状を特定します。 懸念がレイテンシー、スクロール、繰り返される処理、メモリの増加、CPU 使用率、バイナリサイズ、起動への影響、またはコードレビュー上の理論的リスクのどれに当たるかを特定します。
- ホットパスであることを確認します。 コードが頻繁に実行されるか、多数の要素を処理するか、操作を妨げるか、起動時に実行されるか、測定結果に現れているかを確認します。
- 疑われるコストを分類します。 コードが漠然と「遅い」と述べるのではなく、ランタイムコストの分類を使います。
- 根拠を探します。 Instruments、Allocations、Time Profiler、最適化後の SIL、ベンチマーク結果、XCTest のパフォーマンステスト、または本番環境のシグナルを優先します。
- セマンティクスと実装の仕組みを分けて考えます。 コストが発生する可能性があるという理由だけで、抽象化を取り除かないでください。それが設計上どのような役割を果たしているかを確認します。
- 最小限で安全な変更を提案します。 API の明瞭さを損なわずに、割り当て、ARC の処理、ディスパッチ、コピー、または特殊化の見逃しを減らす、局所的な変更を優先します。
- トレードオフを説明します。 可読性、API の柔軟性、テスト容易性、バイナリサイズ、ABI の安定性、ビルド時間、または保守コストに触れます。
- 結果を検証します。 変更前後の検証手順がないまま、最適化が成功したとは言わないでください。
判断ルール
コードがホットパスにない場合
低レベルの書き換えは避けます。
懸念は理論上妥当かもしれませんが、根拠がなければ重要な影響はない可能性が高いことを説明します。そのパスがユーザーに見えるパフォーマンスに影響すると疑われる場合に限り、測定を提案します。
問題が割り当ての場合
割り当ての発生元を確認します。
- クラスインスタンス。
- クロージャコンテキスト。
- ボックス化された変数。
- エグジステンシャルストレージ。
- 型消去ラッパー。
- 中間の配列、辞書、セット、文字列、またはデータバッファー。
- Swift と Objective-C/Foundation の型の間のブリッジング。
すべての参照型を置き換えるのではなく、繰り返し発生する割り当てを減らすことを優先します。
問題が ARC の場合
変更を提案する前に、所有権とライフタイムを確認します。
ループ内で繰り返される retain/release の処理、大きな所有者をキャプチャするクロージャ、ホットパスでの不要な weak アクセス、参照を使った値の格納を調べます。パフォーマンス改善策として weak や unowned を扱わないでください。これらはまず所有権を扱うためのツールです。
問題がディスパッチの場合
動的ディスパッチが意図的かどうかを確認します。
継承を想定していないアプリレベルのクラスには final を優先します。ジェネリクス、具体型、または内部実装の詳細は、意図した抽象化を維持でき、測定対象のパスで効果がある場合にのみ使用します。
根拠がないまま、役に立つポリモーフィズムを平坦化しないでください。
問題が any Protocol の場合
実行時の異種性が必要かどうかを確認します。
any Protocol が常に誤りというわけではありません。異なる具体型の値を均一に格納または受け渡す必要がある場合に適しています。ホットパスを静的型付けのまま維持できる場合は、ジェネリクスまたはオペーク型を検討します。
問題がジェネリクスにある場合
特殊化が実際に行われているか確認してください。
ジェネリクスが最も効果を発揮するのは、オプティマイザーがホットパスを特殊化できるだけの実装詳細を把握できる場合です。モジュール境界、公開 API のレジリエンス、大きな関数、型消去によって、特殊化が制限されることがあります。
問題がコピーオンライトにある場合
変更のパターンと一意性の境界を確認してください。
COW 値への変更を繰り返しても、ストレージが一意に参照されていれば低コストで済みますが、コピーが何度も発生する場合は高コストになります。独自の COW では値セマンティクスを維持し、スレッドセーフ性に関する前提を文書化する必要があります。
問題が SIL またはコンパイラー出力にある場合
パフォーマンスについて結論を出す際は、Debug SIL ではなく最適化済み SIL を使用してください。
アロケーション命令、retain/release の発生状況、witness ディスパッチ、existential のオープン、クロージャーの生成、特殊化の見落とし、非仮想化の見落としなどの証拠を探してください。SIL は証拠として扱い、特定のコンパイラーバージョンにソースコードを過剰に最適化する口実にしないでください。
問題が unsafe Swift にある場合
unsafe コードを最初の解決策として勧めないでください。
安全な API では操作を十分に効率よく表現できず、抽象化のコストが問題であることが測定で示され、unsafe な領域を安全なラッパーの内側に小さく保てる場合に限って、unsafe API を使用してください。
問題がモジュール境界にある場合
ランタイムの最適化とアーキテクチャやビルド時の問題は分けて考えてください。
@inlinable、@usableFromInline、@frozen は、API に対するコミットメントが許容できる場合にのみ使用してください。これらの属性は、汎用的な「高速化」スイッチではありません。
よくある落とし穴
structだからといって、スタックに割り当てられるとは限りません。classだからといって、自動的にパフォーマンス上の問題になるわけではありません。- 値セマンティクスでも、ヒープストレージや ARC が関わることがあります。
Array、String、Dictionary、Set、Dataは、参照ベースのストレージを内部に隠していることがあります。any Protocolはランタイムコストを伴う便利な抽象化であり、間違いではありません。some Protocolは、any Protocolをあらゆる場合に置き換えられるものではありません。- ジェネリクスが最も効果を発揮するのは、特殊化が行われる場合です。
finalは、継承を想定していないアプリレベルのクラスで適切なデフォルトですが、単独で使えるパフォーマンス改善策として過大評価すべきではありません。@inline(__always)はコードサイズを増やすことがあるため、最初の解決策にすべきではありません。@inlinableは API と ABI に対するコミットメントです。weakとunownedは所有権を扱うツールであり、パフォーマンスを高めるツールではありません。- Debug ビルドでの挙動は、最適化されたランタイムのパフォーマンスを判断する根拠として信頼できません。
- unsafe コードは、軽率に使用すると遅くなったり、最適化しにくくなったり、誤った動作をしたりすることがあります。
- 測定結果から必要性が示されない限り、読みやすいアーキテクチャを低レベルのコードに置き換えないでください。
参照先の振り分け
参照資料は必要なものだけ読み込んでください。デフォルトですべての参照ファイルを読み込まないでください。
references/allocation-and-layout.md— スタックとヒープの動作、オブジェクトのレイアウト、クロージャボックス、存在型のストレージ、一時的な割り当て、値型内部の隠れたヒープストレージを扱う場合に読みます。references/arc-and-ownership.md— retain/release の発生、クロージャキャプチャ、weak/unowned 参照、オブジェクトのライフタイム、参照サイクル、COW の所有権、ブリッジングのライフタイムを扱う場合に読みます。references/dispatch-and-specialization.md— 直接ディスパッチ、クラスディスパッチ、Objective-C ディスパッチ、witness ディスパッチ、クロージャディスパッチ、脱仮想化、インライン化、ジェネリックの特殊化を扱う場合に読みます。references/existentials-generics-opaque-types.md—any Protocol、some Protocol、ジェネリクス、型消去、プロトコル witness ディスパッチ、不透明な戻り値型、存在型を多用するホットパスの置き換えを扱う場合に読みます。references/cow-and-large-values.md— コピーオンライトコレクション、大きな構造体、コレクションの繰り返し変更、カスタム COW ストレージ、一意性チェック、値セマンティクスを持つ API 設計を扱う場合に読みます。references/sil-inspection.md— ソースレベルの推論だけでは不十分で、割り当て、ARC、ディスパッチ、存在型のオープニング、クロージャの生成、特殊化、インライン化について最適化済み SIL の証拠が必要な場合に読みます。references/unsafe-swift.md— Unsafe ポインター、バッファアクセス、メモリバインディング、アラインメント、エイリアシング、手動ライフタイム、Unsafe ラッパー、安全な API の Unsafe コードへの置き換えを扱う場合に読みます。references/modularization-and-linking.md— モジュール境界を越えたオプティマイザーの可視性、パブリック API のレジリエンス、@inlinable、@usableFromInline、@frozen、静的ライブラリと動的ライブラリ、モジュール化による実行時のトレードオフを扱う場合に読みます。references/concurrency-runtime.md— 並行処理に関する質問が、割り当て、ARC、クロージャキャプチャ、SIL の低レベル変換、エグゼキューター関連のオーバーヘッドなど、Swift ランタイムのコストに特化している場合にのみ読みます。アクターの設計、MainActor の応答性、キャンセル、AsyncSequence のクリーンアップ、継続、構造化並行処理のワークフローについては、代わりにswift-concurrency-performanceを使用してください。
証拠と検証
想定されるコストに応じた証拠を優先してください。
- 懸念がオブジェクトの頻繁な生成、ボックス、クロージャコンテキスト、存在型のストレージ、一時コレクションである場合は、Allocations を使用します。
- 懸念が CPU コスト、ディスパッチのオーバーヘッド、コピー、ブリッジング、ホット関数呼び出しである場合は、Time Profiler を使用します。
- 懸念がコンパイラーの最適化、特殊化、脱仮想化、ARC の挿入、存在型のオープニング、クロージャの割り当て、インライン化である場合は、最適化済み SIL を使用します。
- タイトなループ、コレクション操作、ディスパッチパターン、ジェネリックと存在型の比較を切り分ける場合は、小規模なベンチマークを使用します。
- 操作を決定論的に再現できる場合は、XCTest パフォーマンステストを使用します。
- パフォーマンスに関する主張が実際のユーザーパスに影響する場合は、変更前後のトレースを使用します。
課題が Debug ビルドでのみ発生する開発時のパフォーマンスに特化している場合を除き、Debug ビルドで検証しないでください。
出力の要件
コードをレビューするときは、次の内容を回答してください。
- 概要 — 懸念が実際に起こりそうか、理論上のものか、提示されたコードだけでは判断できないかを述べます。
- 推定される実行時コスト — 問題を、アロケーション、ARC、ディスパッチ、エクジステンシャル/ジェネリック、コピー、コンパイラ最適化、unsafe 境界、モジュール境界コストに分類します。
- ホットパスの評価 — このコードがユーザーに見えるパフォーマンスに影響しそうかを説明します。
- 確認すべき証拠 — 仮説を裏付ける測定結果、SIL 出力、ベンチマーク、トレースを挙げます。
- 指摘事項 — 具体的なソースコードのパターンと、それが実行時の処理につながる可能性がある理由を説明します。
- 推奨する変更 — 安全性を保てる最小限の変更を提案します。推奨内容を明確にするのに役立つ場合に限り、コードを含めます。
- トレードオフ — 可読性、抽象化、API の柔軟性、バイナリサイズ、ABI 安定性、ビルド時間、保守性について触れます。
- 検証 — 変更前後の証拠を使って結果を確認する方法を説明します。
回答のスタイル
正確かつ簡潔にしてください。
次のような表現を推奨します。
- 「クロージャーがエスケープし、
selfをキャプチャするため、アロケーションが発生する可能性があります。」 - 「このエクジステンシャルは、ループがホットである場合や、Instruments でアロケーションが確認される場合を除けば、おそらく問題ありません。」
- 「ジェネリック関数がモジュール境界を越えて特殊化されるかどうかは、最適化された SIL で確認してください。」
- 「ケースの集合が固定されており、このパスが測定上ホットであれば、具象 enum が役立つ可能性があります。」
次のような表現は避けてください。
- 「構造体は常に高速です。」
- 「クラスは遅いです。」
- 「プロトコルはパフォーマンスに悪影響です。」
- 「常に
anyをジェネリクスに置き換えてください。」 - 「速度向上のために unsafe ポインターを使ってください。」
- 「
@inline(__always)を追加してください。」 - 「パフォーマンスのためにすべてに
finalを付けてください。」
肯定的なトリガーの例
次のようなプロンプトでは、このスキルを有効にしてください。
- 「ARC とアロケーションのオーバーヘッドについて、Swift のホットパスをレビューしてください。」
- 「このフィードは
any FeedItem値を保持しています。実行時コストにつながる可能性はありますか?」 - 「このプロトコル中心のコードでは、代わりにジェネリクスを使うべきですか?」
- 「この独自の COW 型で、意図せず大量にコピーされることはありますか?」
- 「最適化された SIL を確認して、retain とアロケーションについて説明してもらえますか?」
- 「モジュール境界を越えて使われるこの小さなジェネリック関数で、
@inlinableを使う妥当性はありますか?」 - 「クロージャーを多用するこのパーサーでは、多くのアロケーションが発生します。何を確認すべきですか?」
- 「この unsafe バッファー最適化は妥当ですか?」
否定的なトリガーの例
次のようなプロンプトでは、このスキルを有効にしないでください。
- 「行の更新時に SwiftUI のリストがカクつきます。」
- 「コールド起動後、アプリの最初の画面が表示されるまで時間がかかります。」
- 「このタスクグループでは、キャンセルをどのように構成すべきですか?」
- 「ハングを調査するには、どの Instruments テンプレートを使えばよいですか?」
- 「Swift のプロトコルはどう作ればよいですか?」
- 「この SwiftUI 画面の見た目を改善するにはどうすればよいですか?」
- 「この SDK の初期化を AppDelegate の外に移すべきですか?」
ユーザーが Swift の実行時レベルのコストについて明示的に尋ねていない限り、これらのタスクは、より具体的なスキルに振り分けてください。