Bazel 相當複雜,在建構過程中會執行許多不同的工作,其中有些可能會對建構效能造成影響。本頁面會嘗試將部分這些 Bazel 概念對應到其對建構效能的影響。雖然我們另外提供了幾個範例,說明如何透過擷取指標偵測建構效能問題,以及修正這些問題。我們希望您在調查建構效能迴歸時 運用這些概念
乾淨與漸進式建構作業
乾淨建構作業能從頭開始建構所有內容,而漸進式建構會重複使用部分已完成的工作。
建議您分別查看乾淨的漸進式建構作業,特別是當您要收集 / 匯總依賴 Bazel 快取狀態的指標 (例如建構要求大小指標) 時,兩者也代表兩種不同的使用者體驗。相較於從頭開始啟動清理建構作業 (使用冷快取需要較長時間),隨著開發人員疊代程式碼,漸進式建構作業會更加頻繁 (因為快取通常已暖身,因此速度通常更快)。
您可以使用 BEP 中的 CumulativeMetrics.num_analyses
欄位將建構作業分類。如果為 num_analyses <= 1
,表示這是乾淨的建構;否則,我們可以廣泛將其分類為可能是漸進式的版本,因為使用者可能會切換至其他標記或不同目標,進而有效地清理建構作業。任何更嚴格的成效增幅定義都可能必須以經驗法則的形式呈現,例如查看載入的套件數量 (PackageMetrics.packages_loaded
)。
使用確定性建構指標做為建構效能的替代指標
由於某些指標 (例如 Bazel 的 CPU 作業時間或遠端叢集上的佇列時間) 具有非確定性性質,評估建構效能可能並不容易。因此,使用確定性指標做為 Bazel 所執行工作的 Proxy,這會影響其效能。
建構要求的大小會對建構效能產生重大影響。較大的建構可能代表在分析及建構建構圖方面有更多工作。建構作業的自然成長自然是開發出來的,因為會新增/建立更多依附元件,而這會因此日趨複雜,建構也越來越昂貴。
我們可以將這個問題分為不同的建構階段,並使用下列指標做為各階段工作的 Proxy 指標:
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 的記憶體用量,建議您將規則使用內建記憶體分析器。
永久工作站的記憶體剖析
雖然永久性工作站有助於大幅加快建構速度,但對於已解譯的語言而言,記憶體用量也可能有問題。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
proto 監控網路流量,藉此監控叫用期間的網路流量 (需要傳遞 --experimental_collect_system_network_usage
)。
此外,JSON 追蹤記錄設定檔可讓您傳送 --experimental_collect_system_network_usage
標記 (在 Bazel 6.0 中新增),藉此在建構作業期間查看整個系統的網路用量。
圖 3. 含有整個系統網路用量的設定檔。
使用遠端執行作業時,如果網路用量很高,但範圍很高,可能表示網路是建構作業中的瓶頸。如果尚未使用,請考慮傳遞 --remote_download_minimal
,在沒有位元組的情況下開啟 Build。這可避免下載不必要的中繼構件,以加快建構速度。
另一種做法是設定本機磁碟快取來節省下載頻寬。