macOS-Trace:自主应用程序性能优化
macOS-Trace 是一个针对原生 macOS 应用程序(SwiftUI、AppKit、Metal、CoreAudio、WebKit 及原生二进制程序)的闭环性能优化引擎。
其目标是消除手动 Instruments GUI 交互。AI 代理可以自主诊断、定位瓶颈、实施代码修改、通过差异化基准测试进行重新测试,并不断迭代,直至以实证数据验证性能目标。
+-------------------------------------------------------------------------+
| 自主优化循环 |
| |
| 1. 目标对齐 ──> 2. 诊断追踪 ──> 3. 精准代码修复 |
| ^ │ |
| │ ▼ |
| └────── 若未达标则迭代 <── 4. 重新测试验证 |
+-------------------------------------------------------------------------+
阶段 1:用户目标对齐(预飞问卷)
在修改代码或收集追踪数据之前,代理必须与用户就优化目标和成功标准达成一致。
模态工具 vs 聊天交互
- 如果您的代理平台提供交互式问卷/模态工具(例如
ask_question、输入对话框或可选项列表),请调用它以清晰地向用户呈现这些选项。 - 如果没有可用的模态工具,请在对话中直接向用户提问,提供结构化选项和具体的推荐值。
需要向用户提出的问题
- 主要优化目标:
- 选项 A:降低 CPU 利用率、功耗和热降频。
- 选项 B:降低内存占用、瞬态分配峰值或消除泄漏。
- 选项 C:消除 UI 帧卡顿和丢帧(Hitches)。
- 选项 D:加速应用程序冷启动时间。
- 具体性能目标(提供推荐默认值):
- CPU / 能耗目标:
- 空闲基线目标:指令 < 20 M/s,CPU 影响 < 0.5。
- 活跃工作负载目标:指令 < 100 M/s(或指定:降低 30% - 50%)。
- 内存目标:
- 最大常驻 RAM:上限 < 150 MB(实用工具/音频)或 < 300 MB(媒体/丰富 UI)。
- 分配事件率:稳态执行期间 < 500 事件/秒。
- 内存泄漏:恰好 0 个持久泄漏。
- UI 流畅度目标:
- 卡顿率:< 5.0 毫秒/秒(可接受),< 1.0 毫秒/秒(流畅/无丢帧)。
- 启动时间目标:
- 首帧时间:< 400 毫秒(优秀),< 800 毫秒(可接受)。
- CPU / 能耗目标:
- 基准测试用户场景:
- 询问用户要对哪个具体屏幕、用户交互或工作负载进行基准测试。
一旦目标确认,进入阶段 2。
范围和先决条件
- 目标平台:仅限 macOS 原生桌面应用程序(SwiftUI、AppKit、Metal、CoreAudio / AVAudioEngine、WebKit 宿主视图、原生 CLI 可执行文件)。不支持 iOS 模拟器、远程移动设备或外部浏览器专用 Web 应用。
- Xcode 工具:需要 macOS 12+ 及完整 Xcode 或 Xcode 命令行工具(
xcode-select -p、xcrun xctrace version)。 - Python:Python 3.8+(macOS 预装;零外部 pip 依赖)。
- 进程权限:在 Hardened Runtime 下,
--attach <PID>需要调试构建或具有get-task-allow授权的二进制程序。
代理规则
在自动化性能分析期间,请遵循以下不可协商的规则:
- 首先建立基线:绝不仅仅记录活跃工作负载。在记录活跃状态之前,始终捕获空闲基线(应用在前台打开,工作负载暂停)。计算:
增量 = 活跃 - 基线。 - 记录前验证目标状态:使用
pgrep -x <进程名称>检查进程是否存在,并确保目标功能在记录窗口期间正在积极执行。 - 保持窗口在前台:macOS 会限制被遮挡或最小化窗口的渲染和显示链接(
NSWindowOcclusionState)。遮挡的窗口会产生错误的低 GPU 和 CPU 读数。 - 使用相同的测试参数:比较具有相同采样持续时间(默认:
60s)、相同显示比例、相同窗口大小和相同测试输入数据的运行。切勿将 Debug 构建与 Release 构建进行比较。 - 没有第三方 Python 依赖:所有捆绑脚本(
scripts/compare_elements.py、scripts/parse_power.py、scripts/top_categories.py)仅使用 Python 3 标准库。请勿安装 pip 包。 - 将输出保存到
/tmp/macos-traces/:将所有.trace包和.xml导出文件存储在/tmp/macos-traces/中,并使用时间戳和场景标签命名。 - 保护对话上下文预算:追踪文件和原始导出的 XML 文档可能达到数十或数百兆字节。切勿将原始
.trace输出、完整调用树或未解析的 Allocations XML 转储到代理对话上下文中。始终使用捆绑的 Python 脚本对流数据进行流式处理、过滤、排名和汇总后再读取。 - 专注于主要瓶颈:不要将微优化分散到数十个无关紧要的实用函数上。首先进行性能分析,确认主要贡献者(例如冗余表面实例、高频定时器重新评估、无缓冲 I/O),并将优化工作集中在该根本原因上。
- 绝不在未经许可的情况下改变 UI、视觉效果或核心行为:如果性能瓶颈涉及视觉保真度(如模糊/毛玻璃材质、帧动画、阴影、布局过渡)或基本软件行为:
- 不要单方面移除或降级视觉特性。
- 正式征求用户许可(使用交互式提示或明确的聊天消息)。
- 清晰阐述权衡:描述视觉变化前后的情况,解释该特性为何消耗资源,并呈现具体的预期性能提升(例如,“禁用动态背景模糊将使活动 GPU 影响从 1.5 降低到 0.2,并节省约 50 M/s 的 CPU 指令”)。
- 清理记录产物:每次
xctrace记录都会将几个 GB 的临时内核追踪(instruments*.ktrace)和 Instruments CLI 缓存(C/com.apple.dt.InstrumentsCLI)写入用户系统临时文件夹($TMPDIR)。scripts/run_trace.sh会在退出时(无论是成功还是失败)自动删除它们。当直接运行xctrace时,请在结束前自行清理:切勿在留下数百 GB 的临时记录数据的情况下结束会话。find "${TMPDIR:-/tmp}" -maxdepth 1 -type f -name 'instruments*.ktrace' -delete 2>/dev/null || true
4 阶段优化协议
阶段 2:诊断性能分析与归因
在编写代码之前,测量当前状态并隔离根本原因:
# 1. 预检:应用程序必须正在运行且窗口可见
APP_NAME="YourApp"
pgrep -x "$APP_NAME" || { echo "[ERROR] $APP_NAME 未运行。请先启动它。"; exit 1; }
# SKILL_DIR = 此技能的安装目录(如果安装在其他位置,请调整)
SKILL_DIR="$HOME/.claude/skills/macos-trace"
# 2. 记录 60 秒空闲基线(工作负载暂停,窗口可见)
# run_trace.sh 自行将进程名称解析为 PID
"$SKILL_DIR/scripts/run_trace.sh" --process "$APP_NAME" --template power --duration 60s --label "01-baseline"
# 3. 在应用中触发工作负载,记录活跃状态
"$SKILL_DIR/scripts/run_trace.sh" --process "$APP_NAME" --template power --duration 60s --label "02-pre-opt"
# 4. 计算优化前增量
python3 "$SKILL_DIR/scripts/compare_elements.py" \
/tmp/macos-traces/01-baseline-power.xml:"空闲基线" \
/tmp/macos-traces/02-pre-opt-power.xml:"优化前活跃"
使用专用模板归因瓶颈:
- 运行
--template time隔离热点调用树函数。 - 运行
--template alloc配合scripts/top_categories.py识别抖动分配。 - 在滚动期间运行
--template hitches隔离渲染与提交延迟。
阶段 3:精准代码修改
基于分析结果应用最小化、精确的修复:
- 实时音频线程在分配堆内存?替换为预分配的无锁环形缓冲区。
- WebKit IPC 饱和?限制状态更新并切换到 CSS 变换动画。
- Metal 片段着色器在 Retina 上过度绘制?添加动态分辨率缩放或暂停屏幕外渲染循环。
- 高分辨率图像解码峰值?采用
CGImageSourceCreateThumbnailAtIndex降采样。
重新构建应用程序。
阶段 4:重新测试、定量审查与决策门
在相同条件下重新运行性能分析,并评估增量:
# 1. 记录优化后的活跃工作负载
"$SKILL_DIR/scripts/run_trace.sh" --process "$APP_NAME" --template power --duration 60s --label "03-post-opt"
# 2. 比较优化前 vs 优化后 相对于基线
python3 "$SKILL_DIR/scripts/compare_elements.py" \
/tmp/macos-traces/01-baseline-power.xml:"空闲基线" \
/tmp/macos-traces/02-pre-opt-power.xml:"优化前活跃" \
/tmp/macos-traces/03-post-opt-power.xml:"优化后活跃"
示例决策输出:
场景 Sec CPU 平均 CPU 最大 显示 GPU 平均 总指令 指令 M/s
============================================================================================
空闲基线 60 0.15 0.80 0.05 0.00 1.02G 17.0
优化前活跃 60 2.40 4.80 1.10 1.50 16.20G 270.0
优化后活跃 60 0.65 1.20 0.25 0.10 4.80G 80.0
--------------------------------------------------------------------------------------------
相对于基线的差异 [空闲基线]:
优化前活跃 +253.0 M/s 指令,CPU 平均增量 +2.25
优化后活跃 +63.0 M/s 指令,CPU 平均增量 +0.50
决策门:
- 如果目标达成(例如指令率从 270 M/s 降至 80 M/s,满足 < 100 M/s 的目标):向用户呈现优化前后实证报告并结束。
- 如果目标未达成:隔离剩余瓶颈并开始下一轮迭代。
报告后清理:在用户接受最终优化报告后,除非用户明确要求保留,否则删除存储在 /tmp/macos-traces/ 下的累积 .trace 包(每个可能达到数十 GB)。报告中显示的最终比较表和 .xml 导出文件已足够作为证据。
直接 CLI 参考
# 附加到正在运行的进程
xcrun xctrace record \
--template 'Power Profiler' \
--time-limit 60s \
--output /tmp/macos-traces/power.trace \
--attach $PID
# 导出能耗影响表(指令、CPU、GPU、显示)
xcrun xctrace export \
--input /tmp/macos-traces/power.trace \
--xpath "/trace-toc/run[@number='1']/data/table[@schema='ProcessSubsystemPowerImpact']" \
> /tmp/macos-traces/power.xml
# 导出分配摘要表
xcrun xctrace export \
--input /tmp/macos-traces/alloc.trace \
--xpath "/trace-toc/run[@number='1']/data/table[@schema='all-allocations-summary']" \
> /tmp/macos-traces/alloc.xml
模板参考
scripts/run_trace.sh 和无头 xctrace 支持的 Instruments 模板:
计算与能耗
| 模板 | 短名称 | 目标指标与导出模式 | 用例 |
|---|---|---|---|
Power Profiler | power | 指令数/秒(M/s),CPU/GPU/显示能耗影响(ProcessSubsystemPowerImpact)。 | 目标 A/B 基准测试和能效测试。 |
Time Profiler | time | 按线程的 CPU 采样权重,调用树热点,主线程阻塞方法。 | 高 CPU 利用率、失控线程和热函数路径。 |
CPU Counters | counters | IPC(每周期指令数),L1/L2 缓存未命中,分支预测错误。 | 底层计算和 DSP 算法性能瓶颈。 |
UI 响应性与流畅度
| 模板 | 短名称 | 目标指标与导出模式 | 用例 |
|---|---|---|---|
Animation Hitches | hitches | 卡顿持续时间(毫秒),卡顿率(毫秒/秒),丢帧,应用 vs 渲染阶段延迟。 | 滚动卡顿、丢帧和 CoreAnimation 提交延迟。 |
SwiftUI | swiftui | 视图 body 求值次数,State 失效计数,视图更新频率。 | 不必要的视图重新求值和 State 失效级联。 |
Metal System Trace | metal | GPU 编码器时间,顶点/片段着色器持续时间,帧边界延迟。 | 着色器执行瓶颈、粒子特效开销和渲染管线停顿。 |
内存与分配
| 模板 | 短名称 | 目标指标与导出模式 | 用例 |
|---|---|---|---|
Allocations | alloc | 堆分配,瞬态 vs 持久内存,类别事件率(all-allocations-summary)。 | 高频临时分配、内存峰值和缓冲区抖动。 |
Leaks | leaks | 超出父生命周期的持久内存泄漏,引用循环。 | 遗弃内存、闭包捕获泄漏和未释放的委托循环。 |
启动与并发
| 模板 | 短名称 | 目标指标与导出模式 | 用例 |
|---|---|---|---|
App Launch | launch | 首帧时间,dyld 加载时间,静态初始化器,运行循环设置。 | 冷启动优化(--launch -- <binary_path>)。 |
Swift Concurrency | concurrency | Swift 任务(创建/运行/挂起),Actor 重入,协作池使用。 | async/await 饥饿、Actor 争用和长时间挂起的任务。 |
System Trace | sys | 线程状态转换(运行 |