工程指南

在雲端 Mac 上定位 Swift 編譯瓶頸

在雲端 Mac 上定位 Swift 編譯瓶頸

當一個 Swift 模組的編譯時間從數十秒逐漸拉長到數分鐘時,不要急著重寫程式碼或提高平行度。在遠端環境中,依賴解析、建置腳本、連結與型別檢查都會計入 Xcode 顯示的總耗時。有效的排查方式,是先固定雲端 Mac 的工具鏈與輸入,取得各階段的量測證據,再將問題縮小到具體的原始碼檔案與運算式。

先建立可重現的量測基準

選擇沒有其他建置工作執行的時段,固定程式碼提交、Scheme、Configuration、SDK 與目標架構。記錄 xcodebuild -version、目前提交,以及實際執行的命令。不要直接拿一次乾淨建置和一次增量建置比較,因為兩者回答的是不同問題。

mkdir -p "$HOME/build-audit"
xcodebuild -version > "$HOME/build-audit/toolchain.txt"
git rev-parse HEAD > "$HOME/build-audit/commit.txt"

set -o pipefail
/usr/bin/time -l xcodebuild \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -showBuildTimingSummary \
  build \
  2>&1 | tee "$HOME/build-audit/baseline.log"

先連續執行三次一般的增量建置,確認耗時波動是否穩定。需要量測乾淨建置時,應明確執行 clean,或使用獨立的 DerivedData 路徑,並確保最佳化前後採用完全相同的清理方式。

單次最快結果不能作為結論。應比較相同條件下多次執行的中位數,同時保留最慢一次的記錄,用來檢查偶發執行的腳本、網路請求或資源競爭。

從階段摘要判斷調查方向

Build Timing Summary 會列出編譯、連結、資源處理與腳本等階段。應先找出占比最高且反覆出現的項目,不要只看最後一行的總耗時。

現象 優先檢查 常見誤判
SwiftCompile 長時間占首位 型別檢查、單一檔案大小、批次編譯行為 誤以為連結速度慢
Run Script 每次都執行 輸入與輸出檔案、腳本內部的掃描範圍 只提高機器的平行度
依賴解析耗時不穩定 鎖定檔、儲存庫存取、是否重複解析 當成 Swift 編譯問題
Link 階段特別突出 連結輸入、除錯符號、重複程式庫 重寫業務邏輯運算式

也要檢查腳本是否宣告輸入與輸出。缺少依賴邊界的腳本,可能在每次增量建置時執行,掩蓋原始碼最佳化帶來的效果。若腳本包含下載操作,應分別量測網路等待與本機運算時間。

定位緩慢的函式與運算式

確認瓶頸位於 Swift 編譯後,再暫時加入前端診斷參數。可以在專用診斷 Configuration 的 OTHER_SWIFT_FLAGS 中設定,避免影響團隊日常使用的設定。

-Xfrontend -debug-time-function-bodies
-Xfrontend -debug-time-expression-type-checking

重新執行同一條建置命令,並一併儲存標準錯誤輸出。記錄通常會提供耗時、檔案位置,以及對應的函式或運算式。不同 Xcode 工具鏈的輸出格式與門檻可能改變,因此解析腳本不應假設欄位數量永遠固定。

應優先處理哪些項目

先依耗時排序,再按檔案彙整。一個會重複編譯數百次的中度熱點,可能比單一極慢函式更值得優先處理。常見的高成本程式碼包括過長的泛型鏈、巢狀閉包、包含大量分支的單一運算式,以及迫使編譯器同時推斷多個中間型別的集合轉換。

修改時,每次只處理一類問題,例如拆分運算式、為中間值補上明確型別,或將大型函式拆成邊界清楚的小函式。不要同時變更編譯設定與原始碼,否則無法判斷改善究竟來自哪裡。

用最小變更驗證原因

假設某段程式碼將篩選、映射、字典建立與可選值處理串在同一個運算式中,可以先為中間結果命名並標註型別。目的不是讓原始碼更短,而是減少型別檢查器一次必須求解的約束。

let validItems: [Item] = items.filter { $0.isValid }
let identifiers: [String] = validItems.map(\.identifier)
let result: [String: Item] = Dictionary(
    uniqueKeysWithValues: zip(identifiers, validItems)
)

完成修改後,使用一個相較於基準提交只包含該項變更的分支重新量測。至少核對三點:熱點記錄中該運算式的耗時是否下降、SwiftCompile 階段是否縮短,以及總建置時間是否在多次執行中穩定改善。若只有總時間改變而熱點沒有變化,應繼續檢查快取或背景工作,不要將結果歸因於原始碼調整。

將診斷納入可維護的驗收流程

診斷參數不適合長期加入所有建置。較穩妥的做法,是建立獨立的效能檢查工作,按需執行,並封存工具鏈、提交、命令與摘要。記錄可能包含本機路徑、儲存庫結構,或環境變數展開後的內容,上傳前應先去除敏感資訊。

建議每次調查都保存以下內容:

  1. Xcode 與 Swift 版本。
  2. Git 提交與建置設定。
  3. 清楚標示乾淨建置或增量建置。
  4. 三次以上執行的原始耗時。
  5. 階段摘要與主要原始碼熱點。
  6. 修改前後的唯一差異與回復方式。

如果結果在雲端 Mac 上波動明顯,應先確認是否有平行建置、索引工作或殘留腳本正在執行,再重新量測。MiniRent 提供的是獨享實體節點,但同一台裝置內由使用者啟動的工作,仍會競爭 CPU、記憶體與磁碟資源。可靠的結論來自受控的輸入、可重現的命令與完整保留的證據,而不是一次看起來比較快的建置。

常見問題

為什麼不能只比較兩次 Xcode 的總建置時間?

總時間同時包含相依套件解析、快取狀態、腳本與連結。應先固定提交、Xcode 版本和參數,再比較各階段及 Swift 原始碼熱點。

Swift 編譯時間診斷參數適合長期保留嗎?

不適合。這些參數會產生大量記錄,行為也可能隨工具鏈改變。完成取證後應移除,只在效能回歸調查時按需啟用。

專用雲端 Mac

在獨享實體節點上執行建置任務

按天、週、月或季選擇 MiniRents M4 與 MiniRents M4 Pro,並依團隊與程式碼儲存庫位置選擇節點。設備為獨享實體機,並非虛擬機。

選擇機型並訂購