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
)。
此外,您可以透過傳遞 --experimental_collect_system_network_usage
旗標 (Bazel 6.0 的新功能),透過 JSON 追蹤記錄設定檔,在整個建構過程中查看系統層級的網路用量。
圖 3. 包含系統層級網路用量的設定檔。
使用遠端執行時,如果網路用量高且平穩,可能表示網路是建構的瓶頸;如果您尚未使用網路,請考慮透過傳遞 --remote_download_minimal
啟用不含位元的建構作業。這樣一來,系統就不會下載不必要的中繼構件,進而加快建構作業。
另一個做法是設定本機磁碟快取,以節省下載頻寬。