同一台雲端 Mac 同時執行 Xcode 建置、測試、日誌壓縮與產物校驗時,最常見的問題並非某個行程完全失控,而是多個「稍晚完成也無妨」的輔助任務,與關鍵建置工作爭用 CPU、磁碟及記憶體頻寬。結果通常會表現為編譯時間不穩定、測試逾時,或遠端桌面反應遲緩。要解決這類問題,第一步不是盲目降低所有行程的優先級,而是先區分關鍵路徑與背景路徑。
先建立可比較的行程基準線
先在沒有其他任務的情況下執行一次具代表性的建置,再於壓縮、校驗和索引同時運作時重複執行。兩次都要記錄建置耗時、失敗階段,以及主要行程的 CPU、記憶體、狀態和 nice 值。
mkdir -p "$HOME/ci-observe"
ps -axo pid,ppid,user,%cpu,%mem,state,nice,etime,command \
| sort -k4 -nr \
| head -n 40 \
> "$HOME/ci-observe/processes.txt"
iostat -w 2 -c 15 > "$HOME/ci-observe/iostat.txt"
vm_stat 2 15 > "$HOME/ci-observe/vm-stat.txt"
取樣應涵蓋實際工作區與真實的相依套件規模,但不要把首次下載相依套件與穩定建置混入同一條基準線。如果建置時間變長、iostat 持續繁忙,且壓縮行程明顯占用 CPU,就有充分理由調整輔助任務;如果主要問題是記憶體壓力或測試並行度過高,單純修改優先級通常無法奏效。
優先級是一種排程提示,不是資源上限。它適合處理「誰先執行」的問題,但不能取代並行控制,也無法保證某項任務取得固定比例的 CPU。
依關鍵路徑為任務分級
CI 行程可分為三類。第一類是編譯、連結、測試與產物匯出;這些工作決定流水線能否完成,應維持正常優先級。第二類是日誌壓縮、校驗和產生及舊產物整理;這些任務必須完成,但通常不需要搶在建置之前執行。第三類是編輯器索引、互動式分析與臨時診斷,只應在需要時啟動。
不要只按工具名稱套用規則
同一項工具可能出現在不同路徑中。例如,tar 在建置前解壓必要相依套件時屬於關鍵任務,但在建置後壓縮日誌時則屬於背景任務。判斷標準應該是:它是否會阻塞下一個步驟、失敗時是否必須終止流水線,以及輸出是否會立即由目前任務使用。
建議將分級直接寫入流水線設定,而不是仰賴操作者臨時執行。如此一來,審查腳本時便能直接看出哪些命令被降級,也能避免新任務預設繼承不合適的策略。
使用 nice 與 taskpolicy 包裝背景任務
nice 用於調整行程的排程優先值;正數表示更願意讓出執行機會。一般使用者可以降低自己所啟動行程的優先級,但不能任意將行程提升至更高優先級。macOS 的 taskpolicy -b 可將命令置於背景策略下執行,其影響不僅限於 CPU,因此只應套用於已確認不會阻塞關鍵路徑的任務。
以下腳本會讓校驗和以背景低優先級執行,同時讓 Xcode 建置維持正常策略,並分別保留兩者的結束狀態:
#!/bin/zsh
set -u
mkdir -p Artifacts Logs
taskpolicy -b nice -n 10 /bin/sh -c \
'find Artifacts -type f -print0 | xargs -0 shasum -a 256 > Logs/checksums.txt' &
audit_pid=$!
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-destination 'generic/platform=iOS' \
build > Logs/xcodebuild.log 2>&1
build_rc=$?
wait "$audit_pid"
audit_rc=$?
if (( build_rc != 0 )); then
exit "$build_rc"
fi
exit "$audit_rc"
不要對整個 CI Runner 統一套用 taskpolicy -b。這會連同編譯器、測試行程及其子行程一起降級,使整條流水線變慢,卻難以看出究竟是哪項任務造成影響。包裝應盡可能靠近單一背景命令。
在應用程式碼中正確使用 QoS
如果輔助工作由自行開發的 Swift 工具執行,應直接在程式碼層使用佇列 QoS,而不是在啟動後才猜測行程的職責。由使用者發起且會等待結果的工作可使用 .userInitiated;日誌整理、快取盤點等不需立即完成的工作可使用 .utility。.background 只適合使用者不會等待,且延後完成也不影響目前結果的任務。
let queue = DispatchQueue(
label: "com.surevm.ci.artifact-audit",
qos: .utility
)
queue.async {
auditArtifacts()
}
不要為了追求「更快」而將所有佇列都設為 .userInteractive。過高的 QoS 會讓輔助任務與關鍵工作爭搶排程資源,還可能造成優先級反轉:高優先級任務等待由低優先級佇列持有的鎖。共享鎖、序列佇列與同步呼叫都應納入檢查範圍。
驗證效果並設定回復條件
調整後,至少要以相同的建置輸入重複執行三輪,比較中位耗時、失敗位置及資源取樣。單次變快並不能證明策略有效。同時也要檢查日誌壓縮、校驗和等背景任務是否完整結束,不能用「關鍵建置成功」掩蓋輔助任務失敗。
驗收時可依序確認:
- 編譯、連結與測試行程未繼承背景策略。
- 背景任務失敗時會傳回非零結束碼。
- Runner 同時執行的流水線數量未超過節點可承受的範圍。
- 調整前後使用相同的程式碼、相依套件鎖定檔與 Xcode 版本。
- 遠端桌面操作感受不是唯一判斷依據,必須保留行程與 I/O 取樣。
- 節點重新啟動後,優先級包裝仍會由流水線腳本自動生效。
如果降級後的輔助任務積壓到下一輪建置,表示問題已從資源爭用轉變為輸送量不足。此時應減少背景任務數量、縮小處理範圍,或將其移至建置完成後的獨立階段,而不是繼續提高 nice 值。SureVM 節點的具體可選設定應以控制台為準,但無論使用哪一檔設定,先釐清關鍵路徑,再調整排程策略,通常都比全域修改更容易驗證與回復。
常見問題
nice 與 taskpolicy 能限制行程最多使用多少 CPU 嗎?
不能。兩者調整的是排程傾向與背景策略,不是硬性資源配額;若要限制負載,仍須控制建置並行數、測試分片及背景工作數量。
哪些 CI 工作適合套用背景策略?
日誌壓縮、校驗和產生、舊產物整理與非阻塞索引通常適合降級;編譯、連結、測試及其直接相依工作不應預設降級。
選擇獨享實體 Mac 節點,滿足建置、開發與實驗需求
比較兩種 Mac mini M4 配置、四種租期與五個可訂購節點,依照實際工作流程做出合適選擇。