行動團隊總希望每台 Mac mini M4 都跑「所有模擬器」,但過載的第一訊號往往不是 CPU,而是壓縮記憶體、SpringBoard 啟動卡住,以及把並行度減一就消失的 XCTest 逾時。本文給出兩張矩陣(記憶體檔位與安全並行路數、節流策略與清理節奏),八條可直接貼進變更單的上線步驟,以及 FAQ 結構化資料,方便值班同學引用而不用翻 Slack。下文同時強調如何把觀測寫進週會:用「每小時模擬器啟動次數」對比「合併請求吞吐」,比口頭爭論「是不是 Xcode 又壞了」更有效。
訊號:你在並行模擬器而不是縮短佇列
- 夜間 flaky 成簇:夜間流水線把四套 UI 套件疊在同一台主機,而筆電開兩個模擬器卻全綠。
- SpringBoard 或 backboardd 看門狗殺行程:即便
xcodebuild間歇仍報成功。 - 磁碟在
~/Library/Developer/CoreSimulator下狂長,CPU 卻低於六成——記憶體壓力觸發分頁與映像抖動。
若你已在多機之間分片測試包,請先讀 Mac mini M4 上的 UI 測試分片 瞭解如何水平切分;本文管的是單機縱向天花板:實體機上最多能同時承載多少模擬器而不讓分頁主導牆鐘時間。把「分片」與「單機路數」混為一談,是多數團隊反覆踩坑的根因。
另一個常見誤區是把「Runner 佇列變短」等同於「提高並行工作者」。若 p95 作業時長在加並行後反而上升,通常說明瓶頸在記憶體與 I/O,而不是 CPU。此時應回到矩陣 A 對應行,先收緊並行,再評估是否增購 UI 專用主機;否則只是在用隨機逾時支付技術債。把 CoreSimulator 目錄大小與壓縮記憶體曲線並排展示,比只看 Jenkins 綠燈更能說服管理層。
矩陣 A — 統一記憶體檔位與安全並行模擬器路數
假設 Xcode 16 時代執行時期、Apple Silicon M4,且 UI 測試會截圖。若同一台機還承擔編譯且未拆編譯池,請在本表基礎上再降一檔。
| 記憶體 | 並行 UI 模擬器(同一大版本系統) | 僅單測的並行模擬器 | 硬停症狀 |
|---|---|---|---|
| 16 GB | 1(極小套件可嘗試 2) | 嚴格 teardown 下最多 3 | SpringBoard 重啟迴圈 > 2 次/小時 |
| 24 GB | 2 路 UI,限制截圖 | 掛閒置關機鉤子時可到 4 | 壓縮記憶體持續 > 5 GB |
| 32 GB+ | 編譯隔離時 3 路 UI | 每作業 RSS 預算到位時最多 6 | 套件執行中 APFS 剩餘 < 45 GB |
維運落地時,建議把「UI 路數」寫成編排器標籤的硬約束,而不是文件裡的軟提示。對混合租戶池,預設寧可排隊也不要在 16 GB 上開第二條 UI 路;對單團隊獨佔機,可在變更視窗內試驗加一路,但必須同時打開磁碟與壓縮記憶體告警,否則無法判斷是「真有餘量」還是「運氣好沒撞上大套件」。
矩陣 B — 節流策略與清理節奏(✓ / ✗)
| 策略 | 利於記憶體 | 過度則傷吞吐 | 何時採用 |
|---|---|---|---|
| 每作業後關閉模擬器 | ✓ | ✓ | 無獨立編譯主機的混合租戶池 |
| 整條流水線重用已啟動模擬器 | ✗ | ✗ | 單團隊獨佔機且有每日清理視窗 |
| 每週抹除不可用裝置 | ✓ | ✓ | 始終;並與 磁碟留存 指標聯動 |
為何分頁躲在 CoreSimulator 裡而不是程序列表頂端
活動監視器裡看似許多程序 footprint 不大,但模擬器疊在圖形與檔案系統上會分配大塊後備儲存,應用程式安裝階段尤其尖峰。Apple Silicon 統一記憶體意味著這些尖峰會與編譯守護程序、Swift 執行時期快取爭搶。實務上不是把 xcodebuild -parallel-testing-worker-number 往上調,而是讓該旋鈕落在矩陣 A 與你 SKU 匹配的那一行。
遠端 Mac 機群會放大錯覺:當 Runner 分布在香港、日本、韓國、新加坡或美國時,開發者常以為延遲是瓶頸。很多時候只是東京機房 16 GB 上跑了四個模擬器——與維吉尼亞四個模擬器行為一致。地理不製造記憶體 GB。
請把並發規劃與 池容量與佇列 SLO 交叉閱讀,確保編排器裡「ui-heavy」佇列永遠不會排程到記憶體不足的主機。對跨區團隊,把「區域」與「記憶體檔位」拆成兩個標籤維度,避免用單一 region 標籤掩蓋硬體差異。
若你在用自研排程器,建議記錄每次作業結束時的模擬器裝置 UUID 與磁碟增量,便於回溯「哪條流水線把池子撐爆」。這類資料在事故覆盤時比「當時 CPU 不高」更有說服力,也能直接支撐採購或縮容決策。
編排器標籤應如何命名才誠實
諸如 macos-latest 的友好標籤掩蓋了佇列背後是 16 GB 還是 32 GB。應把記憶體等級與模擬器策略寫進池名,例如 mac-m4-24g-ui-max2,讓產品側無法把重截圖套件誤排到只為編譯吞吐採購的主機上。
標籤變更需配套儀表板:按小時繪製模擬器啟動次數。若啟動曲線快過合併請求曲線,說明在 thrash 模擬器而非測試程式——在高管問「行動交付為何在無害 Xcode 修補後崩盤」之前先節流。把「啟動次數 / 合併請求」做成 SLO 的輔助指標,比只盯佇列深度更能提前預警。
值得寫進手冊一次的數字預設
- 閒置逾時:無 XCTest 流量 20 分鐘後關閉模擬器。
- 重試上限:每個 PR 的 UI 重試最多 2 次,避免掩蓋系統性記憶體壓力。
- 看門狗預算:每千次測試 SpringBoard 當機超過 0.5% 視為硬節流觸發。
黃金法則:在每週 CoreSimulator 體積降到磁碟護欄以下且壓縮記憶體跨夜間跑批保持平穩之前,不要提高並行工作者。
八步上線
- 度量基線:並行工作者、p95 UI 作業時長、CoreSimulator 目錄體積。
- 打標主機記憶體檔位;未經顯式批准禁止在 16 GB 上打 UI 重標籤。
- 實作流水線 teardown 中的關機鉤子,成功與失敗路徑都要有。
- 排程每週抹除作業,若耗時超過十分鐘要告警。
- 文件化回復:單一環境開關即可把並行減半。
- 培訓行動工程師:本地八模擬器筆電不能證明 CI 容量。
- 配對截圖與影片產物策略,避免磁碟夜間被灌滿。
- 橫向擴容:節流已誠實但佇列仍違約 SLO 時,增加 NodeMac 主機。
FAQ
Apple Silicon 是否天生比 Intel 能塞更多模擬器?
每瓦吞吐更好,但記憶體仍有限。M4 消解的是 CPU 藉口,不是物理。
模擬器是否應共用一個 DerivedData 卷?
更傾向每作業隔離 DerivedData 根以減少 inode 抖動;僅在純編譯主機上合併快取。
哪裡讀 SSH Runner 相關指南?
調模擬器前先讀 NodeMac 說明中心 的遠端接入模式,再處理高延遲鏈路上的穩定性。
實務補充:值班與合規視角
對受監管產業,建議在變更單裡附上:調整前後的並行路數、CoreSimulator 體積截圖、以及一次夜間跑批的壓縮記憶體時間序列。稽核方關心的是「你是否能證明變更是資料驅動」,而不是口號式的「我們優化了 CI」。把矩陣列印成一頁紙貼在 on-call 手冊旁,能顯著縮短凌晨扯皮時間。若團隊同時執行 Android 模擬器與 iOS 模擬器,請把兩類工作負載拆池,否則記憶體模型會完全偏離本文假設。
NodeMac 客戶在獨佔 Mac mini M4 上可為 UI 池單獨購買記憶體檔位,並把清理視窗與業務低峰對齊;這與「共用 Jenkins 老 Mac 靠人情重啟」形成鮮明對比。把本頁連結放進內部 wiki 的「行動 CI 入口」,新人到職第一天就能讀到同一套語言,減少口耳相傳造成的組態漂移。
誠實的模擬器並行度才能讓 Apple Silicon M4 的 GPU 與 Neural Engine 發揮作用——記憶體不再抖動時,UI 測試才會穩定結束而不是神秘逾時。以 原生 macOS 搭配 SSH 自動化與必要時 VNC 觀察卡死模擬器,與設計師本地習慣一致。按區域 租用專用 Mac mini M4 可把 UI 池與編譯池隔離,不必再堆筆電。可預測的單機記憶體 勝過哀求同事重啟共用 Jenkins。當矩陣證明需要新增 UI 專用主機時,請 按區域比對定價,而不是把模擬器無限堆到機群崩潰。