Bazel 相當複雜,在建構期間會執行許多不同的動作,其中有些動作可能會影響建構效能。本頁面會嘗試將部分 Bazel 概念對應至其對建構效能的影響。雖然不是很多,但我們提供一些範例,說明如何透過擷取指標來偵測版本效能問題,以及如何修正這些問題。我們希望您在調查建構作業效能回歸時,能運用這些概念。
清除與漸進式建構
全新建構作業會從頭開始建構所有內容,而增量建構作業則會重複使用部分已完成的工作。
建議您分別查看簡潔和漸進式的建構作業,特別是在收集 / 匯總依賴 Bazel 快取狀態的指標 (例如建構要求大小指標) 時。它們也代表了兩種不同的使用者體驗。與從頭開始執行簡潔建構作業相比 (由於快取為冷快取,因此耗時較久),當開發人員重複執行程式碼時,增量建構作業會更頻繁地發生 (由於快取通常已為熱快取,因此通常會更快)。
您可以使用 BEP 中的 CumulativeMetrics.num_analyses
欄位來分類版本。如果是 num_analyses <= 1
,表示這是簡潔的建構;否則,我們大範圍可以將其分類可能是漸進式的建構,因為使用者可能會改用不同的標記或不同的目標,從而可以有效地清理建構。任何更嚴格的增量定義都可能以經驗法則的形式出現,例如查看載入的套件數量 (PackageMetrics.packages_loaded
)。
將決定性的建構指標做為建構效能的代理值
由於某些指標具有非確定性 (例如 Bazel 的 CPU 作業時間或遠端叢集上的佇列時間),因此在測量建構效能時可能會遇到困難。因此,使用確定性指標做為 Bazel 完成的工作量代理值,可能會對其效能造成影響。
建構要求的大小可能會對建構效能造成重大影響。版本越大,在分析及建構建構圖形方面的工作越多。隨著新增/建立更多依附元件,建構作業的複雜度也會隨之增加,因此建構作業的成本也會增加,這就是建構作業自然成長的過程。
我們可以將這個問題切割成不同的建構階段,並使用下列指標做為各階段完成工作的代理指標:
PackageMetrics.packages_loaded
:成功載入的套件數量。這裡的回歸代表需要在載入階段讀取及剖析每個額外的 BUILD 檔案,因此需要進行更多工作。TargetMetrics.targets_configured
:代表建構中設定的目標和切面數量。回歸代表建構及瀏覽已設定的目標圖表需要更多工作。- 這通常是因為新增了依附元件,並且必須建構其轉換閉包的圖表。
- 使用 cquery 找出可能新增依附元件的來源。
ActionSummary.actions_created
:代表在建構中建立的動作,而迴歸則代表在建構動作圖時需要更多工作。請注意,這也包含可能尚未執行且未使用的動作。- 請使用 aquery 偵錯回歸問題;建議您先從
--output=summary
開始,再進一步深入分析--skyframe_state
。
- 請使用 aquery 偵錯回歸問題;建議您先從
ActionSummary.actions_executed
:已執行的動作數量,直接迴歸代表在執行這些動作中的工作更多。- BEP 會寫出動作統計資料
ActionData
,顯示執行次數最多的動作類型。根據預設,這項服務會收集前 20 種動作類型,但您可以傳入--experimental_record_metrics_for_all_mnemonics
,收集已執行的所有動作類型這類資料。 - 這應該有助於您瞭解執行了哪些額外的動作。
- BEP 會寫出動作統計資料
BuildGraphSummary.outputArtifactCount
:執行動作所建立的構件數量。- 如果執行的動作沒有增加,表示規則實作可能已變更。
這些指標都受到本機快取狀態的影響,因此您應確保從當中擷取這些指標的建構作業是「乾淨的建構作業」。
我們發現,任何一項指標的回歸都可能伴隨系統時間、CPU 時間和記憶體用量的回歸。
使用本機資源
Bazel 會在本機電腦上使用各種資源 (用於分析建構圖表和驅動執行作業,以及執行本機動作),這可能會影響機器執行建構作業和其他工作時的效能 / 可用性。
使用時間
或許最容易受到雜訊影響的指標 (從建構到建構的可能差異很大) 是時間,尤其是實際時間、CPU 時間和系統時間。您可以使用 bazel-bench 取得這些指標的基準,並利用足夠數量的 --runs
提升測量結果的統計顯著程度。
壁鐘時間是指實際經過的時間。
- 如果只有實際時間回歸,建議您收集 JSON 追蹤記錄設定檔,並查看差異。否則,調查其他迴歸指標可能會影響實際時間,因此會更有效率。
CPU 作業時間是指 CPU 執行使用者程式碼花費的時間。
- 如果兩個專案提交作業的 CPU 時間出現倒退情形,建議您收集 Starlark CPU 設定檔。您可能也應該使用
--nobuild
,將建構作業限制在分析階段,因為大部分的 CPU 密集工作都是在這個階段完成。
- 如果兩個專案提交作業的 CPU 時間出現倒退情形,建議您收集 Starlark CPU 設定檔。您可能也應該使用
系統時間是指 CPU 在核心中耗費的時間。
- 如果系統時間回溯,則大多與 Bazel 從檔案系統讀取檔案時的 I/O 相關。
系統層級負載剖析
只要使用 Bazel 6.0 中導入的 --experimental_collect_load_average_in_profiler
標記,JSON 追蹤分析器就會在叫用期間收集系統負載平均值。
圖 1. 包含系統平均負載的設定檔。
Bazel 叫用期間的負載偏高,可能表示 Bazel 會為機器同時安排過多的本機動作。建議您調整 --local_cpu_resources
和 --local_ram_resources
,特別是在容器環境中 (至少在 #16512 合併前)。
監控 Bazel 記憶體用量
如要取得 Bazel 的記憶體用量,有兩種主要來源:Bazel info
和 BEP。
bazel info used-heap-size-after-gc
:呼叫System.gc()
後,已用記憶體的大小 (以位元組為單位)。- Bazel bench 也會提供這項指標的基準。
- 此外還有
peak-heap-size
、max-heap-size
、used-heap-size
和committed-heap-size
(請參閱說明文件),但不太相關。
BEP 的
MemoryMetrics.peak_post_gc_heap_size
:GC 後 JVM 堆積的峰值大小 (需要設定--memory_profile
,才能強制執行完整 GC)。
記憶體用量出現回歸的情況通常是因為建構要求大小指標出現回歸,而這通常是因為新增了依附元件或規則實作發生變更。
如要更精細地分析 Bazel 的記憶體占用空間,建議您使用規則的內建記憶體分析器。
對持久性 worker 進行記憶體分析
雖然持續性工作者可大幅加快建構作業 (尤其是針對解譯語言),但其記憶體用量可能會造成問題。Bazel 會收集工作站的指標,特別是 WorkerMetrics.WorkerStats.worker_memory_in_kb
欄位會指出工作站使用多少記憶體 (透過助憶法)。
JSON 追蹤記錄分析器也會在叫用期間傳入 --experimental_collect_system_network_usage
標記 (Bazel 6.0 的新功能),收集持續性工作站記憶體用量。
圖 2. 包含工作站記憶體用量的設定檔。
降低 --worker_max_instances
的值 (預設為 4) 可能有助於減少持續性工作站使用的記憶體量。我們正積極努力讓 Bazel 的資源管理工具和排程器更聰明,以便日後減少這類精細調整的需求。
監控遠端版本的網路流量
在遠端執行作業中,Bazel 會下載因執行動作而建構的成果。因此,網路頻寬可能會影響版本效能。
如果您使用遠端執行功能建構,建議您在呼叫期間使用 BEP 中的 NetworkMetrics.SystemNetworkStats
前置詞監控網路流量 (需要傳遞 --experimental_collect_system_network_usage
)。
此外,JSON 追蹤記錄設定檔可讓您傳送 --experimental_collect_system_network_usage
標記 (在 Bazel 6.0 中推出),藉此在建構期間查看整個系統的網路用量。
圖 3. 包含整個系統網路用量的設定檔。
使用遠端執行作業時,如果網路用量高且平穩,可能表示網路是建構作業的瓶頸;如果您尚未使用網路,請考慮透過傳遞 --remote_download_minimal
開啟「不含位元組的建構」功能。這樣一來,系統就不會下載不必要的中繼構件,進而加快建構作業。
另一個選項是設定本機磁碟快取,以節省下載頻寬。