mcpField
A
axiom-concurrency-profiling

axiom-concurrency-profiling

github.com/charleswiltgen開発1.2k172 回インストール今日更新
リポジトリ

axiom-concurrency-profiling について

InstrumentsでSwiftのasync/await性能問題を調べるためのガイドです。タスク実行、アクター競合、スレッドプール枯渇、優先度逆転の診断を扱います。

主な機能

  1. 01Swiftタスクとアクターの動作追跡
  2. 02メインスレッドのブロック診断
  3. 03アクター競合の特定
  4. 04スレッドプール枯渇の調査
  5. 05タスク優先度逆転の確認

活用シーン

  1. 01非同期処理中のUIの停止を調査
  2. 02実行されない待機タスクを特定
  3. 03予期しないアクターアクセスの直列化を追跡

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-concurrency-profiling

インストール後、Claude Code で/axiom-concurrency-profilingを入力して読み込まれたか確認します。

詳細

並行処理のプロファイリング — Instruments のワークフロー

Instruments を使って、Swift の async/await コードをプロファイリングし、最適化します。

使用する場面

✅ 次の場合に使用します:

  • 非同期処理中に UI がカクつく
  • アクターの競合が疑われる
  • タスクがキューに入っているものの、実行されない
  • 非同期処理中にメインスレッドがブロックされる
  • タスク実行の流れを可視化する必要がある

❌ 次の場合は使用しません:

  • 問題が純粋な CPU パフォーマンスに関するもの(Time Profiler を使用してください)
  • 並行処理と無関係なメモリの問題(Allocations を使用してください)
  • 並行処理がボトルネックだと確認できていない

Swift Concurrency テンプレート

表示される内容
トラック情報
Swift Tasksタスクのライフタイム、親子関係
Swift Actorsアクターへのアクセス、競合の可視化
Thread Statesブロック中か実行中か一時停止中か
統計情報
  • 実行中のタスク:現在実行中のタスク
  • 生存中のタスク:ある時点で存在するタスク
  • タスクの総数:作成された累計数
色分け
  • 青:タスクを実行中
  • 赤:タスクが待機中(競合)
  • 灰色:タスクが一時停止中(await 待ち)

ワークフロー 1:メインスレッドのブロックを診断する

症状:UI がフリーズし、メインスレッドのタイムラインが埋まっている

  1. Swift Concurrency テンプレートでプロファイリングします
  2. メインスレッドの「Swift Tasks」レーンを確認します
  3. 長い青色のバー(メインスレッド上で実行中のタスク)を探します
  4. 処理を別の場所に移せるか確認します

解決パターン:

// ❌ Heavy work on MainActor
@MainActor
class ViewModel: ObservableObject {
    func process() {
        let result = heavyComputation()  // Blocks UI
        self.data = result
    }
}

// ✅ Offload heavy work
@MainActor
class ViewModel: ObservableObject {
    func process() async {
        let result = await Task.detached {
            heavyComputation()
        }.value
        self.data = result
    }
}

ワークフロー 2:アクターの競合を見つける

症状:タスクが予期せず直列化され、並列処理が順番に実行される

  1. 「Swift Actors」インストゥルメントを有効にします
  2. アクセスが直列化されているパターンを探します
  3. 赤は待機中、青は実行中です
  4. 赤と青の比率が高い場合は、競合が発生しています

解決パターン:

// ❌ All work serialized through actor
actor DataProcessor {
    func process(_ data: Data) -> Result {
        heavyProcessing(data)  // All callers wait
    }
}

// ✅ Mark heavy work as nonisolated
actor DataProcessor {
    nonisolated func process(_ data: Data) -> Result {
        heavyProcessing(data)  // Runs in parallel
    }

    func storeResult(_ result: Result) {
        // Only actor state access serialized
    }
}

その他の修正方法:

  • アクターを複数に分割します(ドメインを分離します)
  • ホットパスには Mutex を使用します(アクター間の切り替えより高速です)
  • アクターのスコープを縮小します(隔離するプロパティを減らします)

ワークフロー 3:スレッドプールの枯渇

症状:タスクがキューに入っているものの、実行されず、タスク実行に空白が生じる

原因:ブロッキング呼び出しによって協調型プールが枯渇します

  1. すべてのスレッドでタスク実行に空白がないか確認します
  2. ブロッキングプリミティブを確認します
  3. 非同期の代替手段に置き換えます

よくある原因:

// ❌ Blocks cooperative thread
Task {
    semaphore.wait()  // NEVER do this
    // ...
    semaphore.signal()
}

// ❌ Synchronous file I/O in async context
Task {
    let data = Data(contentsOf: fileURL)  // Blocks
}

// ✅ Use async APIs
Task {
    let (data, _) = try await URLSession.shared.data(from: fileURL)
}

デバッグフラグ:

SWIFT_CONCURRENCY_COOPERATIVE_THREAD_BOUNDS=1

非同期コンテキストでの安全でないブロッキングを検出します。

ワークフロー 4:優先度の逆転

症状:優先度の高いタスクが、優先度の低いタスクを待機する

  1. Instruments でタスクの優先度を調べます
  2. 待機チェーンをたどります
  3. 重要な処理経路に適切な優先度が設定されていることを確認します
// ✅ Explicit priority for critical work
Task(priority: .userInitiated) {
    await criticalUIUpdate()
}

スレッドプールのモデル

Swift は CPU コア数に応じた協調型スレッドプールを使用します。

項目GCDSwift Concurrency
スレッド数制限なく増加コア数に固定
ブロッキング新しいスレッドを作成一時停止してスレッドを解放
依存関係隠れているランタイムが追跡
コンテキスト切り替えカーネルによる完全な切り替え軽量な継続

ブロッキングが致命的な理由:

  • ブロック中の各スレッドはメモリとカーネル構造体を保持します
  • スレッド数に上限があるため、ブロックされると処理が進まなくなります
  • プールの枯渇によってアプリがデッドロックします

クイックチェック(プロファイリングの前に)

まず、次の項目を確認します。

  1. 処理は本当に非同期ですか?

    • 中断ポイント(await)を探します
    • 非同期関数内の同期コードも、処理をブロックします
  2. await 中にロックを保持していますか?

    // ❌ Deadlock risk
    mutex.withLock {
        await something()  // Never!
    }
    
  3. タスクをタイトなループで実行していますか?

    // ❌ Overhead may exceed benefit
    for item in items {
        Task { process(item) }
    }
    
    // ✅ Structured concurrency
    await withTaskGroup(of: Void.self) { group in
        for item in items {
            group.addTask { process(item) }
        }
    }
    
  4. 非同期コンテキストで DispatchSemaphore を使用していますか?

    • 常に安全ではありません。代わりに withCheckedContinuation を使用してください

よくある問題のまとめ

問題Instruments での症状修正方法
MainActor の過負荷メインスレッド上の長い青色のバーTask.detached、nonisolated
アクターの競合赤と青の比率が高いアクターを分割し、nonisolated を使用
スレッドの枯渇すべてのスレッドに空白が生じるブロッキング呼び出しを削除
優先度の逆転優先度の高いタスクが低いタスクを待機するタスクの優先度を確認
タスクが多すぎるタスク作成のオーバーヘッドタスクグループを使用

安全なプリミティブと安全でないプリミティブ

協調型プールで安全:

  • await、アクター、タスクグループ
  • os_unfair_lock、NSLock(短いクリティカルセクション)
  • Mutex(iOS 18以降)

安全でない(フォワードプログレスに違反):

  • DispatchSemaphore.wait()
  • pthread_cond_wait
  • 同期的なファイル/ネットワーク I/O
  • Task 内での Thread.sleep()

リソース

WWDC:2022-110350、2021-10254

ドキュメント:/xcode/improving-app-responsiveness

スキル:axiom-swift-concurrency、axiom-performance-profiling、axiom-synchronization、axiom-lldb(スレッド状態の対話的な検査)

コメント