iOS-Trace:自主應用程式效能最佳化
iOS-Trace 是一個專為 iOS 與 iPadOS 應用程式(執行於實體 iPhone/iPad 裝置或本機 iOS 模擬器上,支援 SwiftUI、UIKit、Metal、CoreAudio / AVAudioEngine 和 URLSession 網路)設計的閉環效能最佳化引擎。
其核心目標是消除手動操作 Instruments GUI 的需求。AI 代理程式可以自主診斷、定位瓶頸、實施程式碼變更、透過差異性基準測試重新測試,並在實證數據驗證下持續迭代,直至達成效能目標。
+-------------------------------------------------------------------------+
| 自主最佳化循環 |
| |
| 1. 目標對齊 ──> 2. 診斷性追蹤 ──> 3. 標靶式程式碼修復 |
| ^ │ |
| │ ▼ |
| └────── 若未達目標則迭代 <── 4. 重新測試驗證 |
+-------------------------------------------------------------------------+
第一階段:使用者目標對齊(事前問卷)
在修改程式碼或於行動裝置上收集追蹤資料之前,代理程式必須先與使用者就優化目標與成功標準達成共識。
模態工具 vs. 對話互動
- 若您的代理平台提供互動式問卷/模態工具(例如
ask_question、輸入對話框或可選選項列表),請呼叫它以整潔的方式向使用者呈現這些選擇。 - 若無可用的模態工具,請直接在對話中以結構化選項和具體建議值詢問使用者。
應向使用者詢問的問題
-
主要優化目標:
- 選項 A:減少電池消耗、CPU 使用率與熱節流。
- 選項 B:降低記憶體佔用、瞬時分配峰值,或防止 Jetsam OOM 崩潰。
- 選項 C:消除 UI 畫面卡頓與動畫幀丟失(ProMotion 120Hz 卡頓)。
- 選項 D:加速行動應用程式的冷啟動時間。
- 選項 E:最佳化網路無線電開銷與批次請求效率。
-
具體效能目標(提供建議預設值):
- 電池與 CPU 目標:
- 閒置基線目標:< 15 M/s 指令,CPU 影響 < 0.3。
- 活躍工作負載目標:< 80 M/s 指令(或指定:減少 30% - 50%)。
- 記憶體與 Jetsam 目標:
- 最大常駐 RAM:限制在 < 150 MB(標準工具程式)或 < 300 MB(媒體/豐富互動應用程式)。
- 分配事件速率:穩態互動期間 < 400 事件/秒。
- 記憶體洩漏:嚴格為 0 個持續性洩漏。
- UI 流暢度與幀率目標:
- 卡頓率:< 5.0 ms/s(可接受),< 1.0 ms/s(流暢 / 120Hz ProMotion 等級)。
- 最大卡頓時長:< 16.6ms (60Hz) 或 < 8.33ms (120Hz)。
- 啟動時間目標:
- 到第一個畫面的時間:< 400 ms(優異),< 800 ms(可接受)。
- 網路與無線電目標:
- 無線電開銷:將定期 ping 批次化為單一突發請求,以避免無線電尾部待機功耗。
- 電池與 CPU 目標:
-
基準測試使用者情境:
- 詢問使用者要對哪個特定畫面、使用者互動、實體裝置或模擬器進行基準測試。
一旦確認目標,即進入第二階段。
範圍與先決條件
- 目標平台:執行於透過 USB 或本機網路連接的實體 iPhone/iPad 裝置,或本機 iOS 模擬器上的 iOS 與 iPadOS 應用程式。(對於 macOS 桌面應用程式,請使用 macOS-Trace)。
- 主機系統:macOS 12+,運行於 Apple Silicon 或 Intel Mac,需安裝完整 Xcode 或 Xcode 命令列工具(
xcode-select -p,xcrun xctrace version)。 - 裝置就緒狀態:
- 實體 iPhone/iPad 必須已解鎖並受 Mac 主機信任。
- 執行
xcrun xctrace list devices時,裝置必須出現在== Devices ==下方(而非== Devices Offline ==)。 - 自動鎖定應設為「永不」,或在錄製期間保持顯示器開啟,以防止 iOS 暫停背景程序。
- Python:Mac 主機上需有 Python 3.8+(僅使用標準函式庫;零外部 pip 依賴項)。
- 程序權限:需使用除錯版或開發者佈建設定檔建置的應用程式,並帶有
get-task-allow權限,才能以--attach <PID>附加。
代理規則
在自動化效能剖析期間,請務必遵守以下不可協商的規則:
- 驗證裝置連線狀態:在錄製前務必執行
xcrun xctrace list devices。切勿嘗試對列在== Devices Offline ==下方的裝置進行錄製。 - 防止螢幕鎖定與背景運行:若 iOS 裝置鎖定或返回主畫面,iOS 會暫停或限制程序,產生無效的測量結果。請保持裝置喚醒且應用程式在前景運行。
- 先建立基線:切勿僅錄製活躍工作負載。在錄製活躍狀態之前,務必先擷取一次閒置基線記錄(應用程式在前景、裝置螢幕開啟、工作負載暫停)。計算:
Delta = 活躍狀態 - 基線。 - 使用相同的測試參數:比較時應使用相同的取樣持續時間(預設:
60s)、相同的裝置電池狀態(避免在電量低於 20% 或低耗電模式下進行剖析)以及相同的測試輸入數據。 - 無第三方 Python 依賴:所有隨附的腳本(
scripts/compare_elements.py、scripts/parse_power.py、scripts/top_categories.py)僅使用 Python 3 標準函式庫。 - 輸出儲存至
/tmp/ios-traces/:將所有.trace套件和.xml匯出檔案儲存至/tmp/ios-traces/,並使用時間戳記和情境標籤命名。 - 保護對話上下文預算:追蹤檔案和原始匯出的 XML 文件可能達到數十或數百 MB。切勿將原始
.trace輸出、完整呼叫樹或未經解析的 Allocations XML 直接放入代理程式的對話上下文中。請務必使用隨附的 Python 腳本來串流、過濾、排序和彙總數據後再讀取。 - 專注於主要瓶頸:不要將微小的最佳化分散到數十個無關的實用函數上。先進行效能剖析以確認主要貢獻者(例如冗餘的 surface 實例、高頻計時器重新評估、無緩衝 I/O),並僅將最佳化工作集中在該根本原因上。
- 切勿在未經許可的情況下擅自更改 UI、視覺效果或核心行為:如果效能瓶頸涉及視覺保真度(例如模糊/材質、幀動畫、陰影、佈局轉場)或基本軟體行為:
- 未經使用者明確許可,不得單方面移除或降級該視覺功能。
- 先正式詢問使用者許可(使用互動提示或明確的聊天訊息)。
- 清楚說明取捨:描述變更前後的視覺效果,解釋該功能為何消耗資源,並呈現具體的預期效能增益(例如:「停用卡片檢視上的動態陰影和模糊,可將 GPU 平均影響從 1.8 降至 0.2,並消除 120Hz ProMotion 卡頓」)。
- 清理錄製產生的暫存檔:每次
xctrace錄製都會在使用者的系統暫存資料夾($TMPDIR)中寫入數 GB 的暫態核心追蹤(instruments*.ktrace)和 Instruments CLI 快取(C/com.apple.dt.InstrumentsCLI)。scripts/run_trace.sh會在結束時自動移除它們(無論成功或失敗)。當直接執行xctrace時,請在結束前自行清理:切勿在結束會話時留下數百 GB 的暫態錄製數據。find "${TMPDIR:-/tmp}" -maxdepth 1 -type f -name 'instruments*.ktrace' -delete 2>/dev/null || true
4 階段最佳化協定
第二階段:診斷性效能剖析與歸因
在撰寫程式碼之前,先在裝置上測量當前狀態並隔離根本原因:
# 1. 確認目標裝置在線且已解鎖(可選檢查;
# 當省略 --device 時,run_trace.sh 會自動偵測第一個連接的 iPhone/iPad)
xcrun xctrace list devices
BUNDLE_ID="com.example.MyApp"
# SKILL_DIR = 此技能的安裝目錄(若安裝在其他位置,請調整)
SKILL_DIR="$HOME/.claude/skills/ios-trace"
# 2. 錄製 60 秒閒置基線(裝置解鎖、應用程式在前景、工作負載暫停)
"$SKILL_DIR/scripts/run_trace.sh" \
--bundle-id "$BUNDLE_ID" \
--template power \
--duration 60s \
--label "01-baseline"
# 3. 在應用程式中觸發目標工作負載,錄製活躍狀態
"$SKILL_DIR/scripts/run_trace.sh" \
--process "MyApp" \
--template power \
--duration 60s \
--label "02-pre-opt"
# 4. 計算最佳化前的差異
python3 "$SKILL_DIR/scripts/compare_elements.py" \
/tmp/ios-traces/01-baseline-power.xml:"閒置基線" \
/tmp/ios-traces/02-pre-opt-power.xml:"最佳化前活躍狀態"
使用專門的範本來歸因瓶頸:
- 執行
--template time以隔離工作執行緒上的熱點函式呼叫樹。 - 執行
--template alloc搭配scripts/top_categories.py以識別導致記憶體壓力的 thrashing 分配。 - 在滾動期間執行
--template hitches以隔離 ProMotion 顯示器上的渲染與提交延遲。 - 執行
--template network以偵測連續未批次化的無線電喚醒。
第三階段:標靶式程式碼修改
根據發現結果進行最小且精確的修復:
- 記憶體峰值與 Jetsam:使用
CGImageSourceCreateThumbnailAtIndex對高解析度影像進行降取樣,而非載入完整尺寸的UIImage。 - ProMotion 卡頓:移除複雜的陰影和離螢幕混合傳遞;將繁重的視圖佈局計算從主執行緒卸載。
- 無線電能量開銷:將定期的網路請求批次化為統一的承載突發,以消除無線電尾部待機功耗。
- 即時音訊:確保 CoreAudio 渲染回呼(
AVAudioEngine或 RemoteIO)中無堆積分配。
重新建置應用程式並部署至裝置。
第四階段:重新測試、量化審查與決策閘門
在相同條件下重新執行效能剖析,並評估差異:
# 1. 錄製最佳化後的活躍工作負載
"$SKILL_DIR/scripts/run_trace.sh" \
--process "MyApp" \
--template power \
--duration 60s \
--label "03-post-opt"
# 2. 針對基線比較最佳化前 vs 最佳化後
python3 "$SKILL_DIR/scripts/compare_elements.py" \
/tmp/ios-traces/01-baseline-power.xml:"閒置基線" \
/tmp/ios-traces/02-pre-opt-power.xml:"最佳化前活躍狀態" \
/tmp/ios-traces/03-post-opt-power.xml:"最佳化後活躍狀態"
範例決策輸出:
情境 Sec CPU 平均 CPU 最大 顯示器 GPU 平均 總指令數 指令 M/s WiFi 傳送/接收
=========================================================================================================
閒置基線 60 0.12 0.60 0.05 0.00 0.85G 14.2 0.0/0.0MB
最佳化前活躍狀態 60 2.40 4.80 1.10 1.80 15.60G 260.0 14.2/1.8MB
最佳化後活躍狀態 60 0.55 1.10 0.15 0.20 4.20G 70.0 2.1/0.4MB
---------------------------------------------------------------------------------------------------------
相對於基線的差異 [閒置基線]:
最佳化前活躍狀態 +245.8 M/s 指令,CPU 平均差異 +2.28,WiFi 傳送差異 +14.20MB
最佳化後活躍狀態 +55.8 M/s 指令,CPU 平均差異 +0.43,WiFi 傳送差異 +2.10MB
最佳化差異(最佳化後 vs 最佳化前):
指令吞吐量: -73.1% (70.0 vs 260.0 M/s)
CPU 平均影響: -77.1% (0.55 vs 2.40)
GPU 平均影響: -88.9% (0.20 vs 1.80)
WiFi 無線電流量: -85.2% (2.1MB vs 14.2MB)
決策閘門邏輯:
- 目標已達成:向使用者呈現實證比較表並結束。
- 目標未達成:保留先前的優化,隔離下一個剩餘熱點,然後重複第三階段和第四階段。
報告後清理:在使用者接受最終優化報告後,刪除 /tmp/ios-traces/ 下累積的 .trace 套件(每個可能數十 GB),除非使用者明確要求保留。報告中顯示的最終比較表和 .xml 匯出檔即為足夠的證據。
直接 CLI 命令
若直接執行 xctrace 而不使用 run_trace.sh:
錄製
# 附加到正在裝置上執行的應用程式
xcrun xctrace record \
--device <DEVICE_UDID> \
--template 'Power Profiler' \
--time-limit 60s \
--output /tmp/ios-traces/power.trace \
--attach <PID>
# 冷啟動裝置上的應用程式
xcrun xctrace record \
--device <DEVICE_UDID> \
--template 'Time Profiler' \
--time-limit 30s \
--output /tmp/ios-traces/launch.trace \
--launch -- com.example.MyApp <LaunchArgs>
匯出
# 匯出電源影響表(CPU 指令、能量、GPU、顯示器、WiFi/行動網路)
xcrun xctrace export \
--input /tmp/ios-traces/power.trace \
--xpath "/trace-toc/run[@number='1']/data/table[@schema='ProcessSubsystemPowerImpact']" \
> /tmp/ios-traces/power.xml
# 匯出分配摘要表
xcrun xctrace export \
--input /tmp/ios-traces/alloc.trace \
--xpath "/trace-toc/run[@number='1']/data/table[@schema='all-allocations-summary']" \
> /tmp/ios-traces/alloc.xml
範本參考
透過 scripts/run_trace.sh 和無頭 xctrace 在 iOS 上支援的 Instruments 範本:
運算、電池與能量
| 範本 | 簡稱 | 目標指標與匯出結構描述 | 使用案例 |
|---|---|---|---|
Power Profiler | power | 指令/秒 (M/s)、CPU/GPU/顯示器/WiFi/行動網路能量影響 (ProcessSubsystemPowerImpact)。 | 全面的電池消耗、熱節流與 A/B 基準測試。 |
Time Profiler | time | 按執行緒的 CPU 樣本權重、呼叫樹熱點、主執行緒阻塞方法。 | 高 CPU 使用率、失控的工作執行緒和熱門函數路徑。 |
CPU Counters | cpu | 由核心提供的精確 CPU 效能計數器。 | 深入的微架構分析。 |
Metal System Trace | metal | GPU 著色器編譯、指令緩衝區生命週期、GPU 閒置與忙碌時間。 | Metal 渲染管道瓶頸。 |
記憶體與分配
| 範本 | 簡稱 | 目標指標與匯出結構描述 | 使用案例 |
|---|---|---|---|
Allocations | alloc | 依類別與呼叫樹的活躍分配、持久性分配、VM 區域大小 (all-allocations-summary)。 | 偵測記憶體峰值、Jetsam 原因與循環參考洩漏。 |
Leaks | leaks | 洩漏區塊、類型與參考路徑。 | 確認記憶體洩漏已修復。 |
UI 與圖形
| 範本 | 簡稱 | 目標指標與匯出結構描述 | 使用案例 |
|---|---|---|---|
Hangs | hangs | 主執行緒阻塞持續時間、AppKit/UIKit 反覆嘗試時長。 | 偵測凍結的 UI 或動畫過程中的阻塞。 |
Core Animation | animation | 提交/渲染/ vsync 間隔,GPU 與 CPU 時間。 | 隔離動畫幀的渲染與提交延遲。 |
Hitches (ProMotion) | hitches | 卡頓率、卡頓持續時間、卡頓類別(渲染 vs 提交 vs 佇列)。 | 高更新率顯示器上的短暫卡頓。 |
媒體與音訊
| 範本 | 簡稱 | 目標指標與匯出結構描述 | 使用案例 |
|---|---|---|---|
Audio | audio | 音訊單元處理時間、緩衝區下溢、即時執行緒排程延遲。 | CoreAudio 與 AVAudioEngine 應用程式。 |
網路
| 範本 | 簡稱 | 目標指標與匯出結構描述 | 使用案例 |
|---|---|---|---|
Network | network | 依連線、傳送/接收位元組、DNS、TCP 狀態的封包活動。 | 偵測連續的無線電喚醒與未批次化的請求。 |
隨附腳本參考
所有腳本位於此技能的 scripts/ 目錄中:
ls -la "$SKILL_DIR/scripts/"
run_trace.sh — 單一命令錄製與匯出
當指定 --template power 時自動匯出結構化的 XML 至 /tmp/ios-traces/。
語法:
SKILL_DIR="$HOME/.claude/skills/ios-trace"
"$SKILL_DIR/scripts/run_trace.sh" [--bundle-id <id> | --process <name>] [--device <udid>] --template <name> --duration <s> --label <tag>
範例:
# 附加到正在執行的程序(名稱符合即可)
# 注意:同時執行多個具有相同名稱的程序時,--process 可能會附加到錯誤的 PID。
# 在這種情況下請改用 --attach <PID>。
"$SKILL_DIR/scripts/run_trace.sh" --process Safari --template power --duration 30s --label "safari-browse"
# 指定具有獨特 PID 的模擬器應用程式
xcrun xctrace list devices
"$SKILL_DIR/scripts/run_trace.sh" --bundle-id com.apple.mobilesafari --device <DEVICE_UDID> --template power --duration 30s --label "safari-browse-device"
compare_elements.py — 多個 XML 匯出檔案的差異化與合併
從時間序列 XML 資料(通常是 ProcessSubsystemPowerImpact 或類似結構)輸出供人類與 AI 閱讀的比較表。
語法:
python3 "$SKILL_DIR/scripts/compare_elements.py" <path>:<label> [<path>:<label> ...]
top_categories.py — 熱門分配類別
從 StudiedAllocationCategories 或 all-allocations-summary XML 表格中排序並彙總頂層分配類別。可依位元組/計數/平均進行過濾和排序。
語法:
python3 "$SKILL_DIR/scripts/top_categories.py" <path> [--sort-by bytes|count|categories] [--top N]
parse_power.py — 結構化 JSON 輸出
對 AI 代理程式非常有用。可輸出易於程式處理的 JSON。
語法:
python3 "$SKILL_DIR/scripts/parse_power.py" <path> [--json]