关于 axiom-concurrency-profiling
面向 Swift 开发者的 Instruments 并发性能诊断指南,涵盖任务执行、Actor 争用、线程池耗尽与优先级反转。
主要功能
- 01追踪 Swift 任务与 Actor 活动
- 02诊断主线程阻塞
- 03定位 Actor 争用
- 04排查线程池耗尽
- 05检查任务优先级反转
适用场景
- 01排查异步操作期间的界面卡顿
- 02查找已排队但未运行的任务
- 03追踪意外串行化的 Actor 访问
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-concurrency-profiling安装后在 Claude Code 中输入/axiom-concurrency-profiling确认已加载。
详细介绍
并发性能分析 — Instruments 工作流
使用 Instruments 对 Swift async/await 代码进行性能分析和优化。
适用场景
✅ 适用:
- 异步操作期间 UI 卡顿
- 怀疑存在 actor 争用
- 任务已排队但未执行
- 异步工作期间主线程被阻塞
- 需要查看任务执行流程
❌ 不适用:
- 问题纯属 CPU 性能问题(使用 Time Profiler)
- 与并发无关的内存问题(使用 Allocations)
- 尚未确认并发是瓶颈
Swift Concurrency 模板
显示内容
| 轨道 | 信息 |
|---|---|
| Swift Tasks | 任务生命周期、父子关系 |
| Swift Actors | Actor 访问、争用可视化 |
| Thread States | 阻塞、运行或挂起状态 |
统计数据
- Running Tasks:当前正在执行的任务
- Alive Tasks:某一时刻存在的任务
- Total Tasks:累计创建的任务数
颜色说明
- 蓝色:任务正在执行
- 红色:任务正在等待(争用)
- 灰色:任务已挂起(等待中)
工作流 1:诊断主线程阻塞
症状:UI 冻结,主线程时间线被占满
- 使用 Swift Concurrency 模板进行性能分析
- 查看主线程 → “Swift Tasks”轨道
- 找到较长的蓝色条(任务正在主线程上执行)
- 检查是否可以将工作移出主线程
解决方案模式:
// ❌ 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:查找 Actor 争用
症状:任务意外地串行化,并行工作按顺序运行
- 启用“Swift Actors”工具
- 查找串行访问模式
- 红色 = 等待,蓝色 = 执行
- 红蓝比例较高 = 存在争用问题
解决方案模式:
// ❌ 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
}
}
更多修复方法:
- 将一个 actor 拆分为多个(按领域分离)
- 对热点路径使用 Mutex(比 actor 跳转更快)
- 缩小 actor 的作用范围(减少隔离属性)
工作流 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:优先级反转
症状:高优先级任务等待低优先级任务
- 在 Instruments 中检查任务优先级
- 沿等待链追踪
- 确保关键路径使用适当的优先级
// ✅ Explicit priority for critical work
Task(priority: .userInitiated) {
await criticalUIUpdate()
}
线程池模型
Swift 使用与 CPU 核心数相匹配的协作式线程池:
| 方面 | GCD | Swift Concurrency |
|---|---|---|
| 线程 | 无限制增长 | 固定为核心数 |
| 阻塞 | 创建新线程 | 挂起并释放线程 |
| 依赖关系 | 隐藏 | 由运行时跟踪 |
| 上下文切换 | 完整的内核切换 | 轻量级续体 |
阻塞为何后果严重:
- 每个被阻塞的线程都会占用内存和内核结构
- 线程数量有限,线程被阻塞就无法取得进展
- 线程池耗尽会导致应用死锁
快速检查(性能分析前)
先运行以下检查:
-
工作是否真的异步?
- 查找挂起点(
await) - 异步函数中的同步代码仍会造成阻塞
- 查找挂起点(
-
是否在 await 期间持有锁?
// ❌ Deadlock risk mutex.withLock { await something() // Never! } -
任务是否处于紧密循环中?
// ❌ 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) } } } -
是否在异步上下文中使用 DispatchSemaphore?
- 始终不安全——改用
withCheckedContinuation
- 始终不安全——改用
常见问题摘要
| 问题 | Instruments 中的症状 | 修复方法 |
|---|---|---|
| MainActor 过载 | 主线程上出现较长的蓝色条 | Task.detached、nonisolated |
| Actor 争用 | 红蓝比例较高 | 拆分 actor、使用 nonisolated |
| 线程耗尽 | 所有线程上都存在间隔 | 移除阻塞调用 |
| 优先级反转 | 高优先级任务等待低优先级任务 | 检查任务优先级 |
| 任务过多 | 任务创建开销 | 使用任务组 |
安全与不安全的原语
与协作式线程池兼容:
await、actors、任务组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(交互式检查线程状态)
评论
支持 Markdown