細分建構效能

回報問題 查看原始碼 Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bazel 相當複雜,在建構期間會執行許多不同的動作,其中有些動作可能會影響建構效能。本頁面會嘗試將部分 Bazel 概念對應至其對建構效能的影響。雖然不是很多,但我們提供一些範例,說明如何透過擷取指標來偵測版本效能問題,以及如何修正這些問題。我們希望您在調查建構作業效能回歸時,能運用這些概念。

清除與漸進式建構

全新建構作業會從頭開始建構所有內容,而增量建構作業則會重複使用部分已完成的工作。

建議您分別查看簡潔和漸進式的建構作業,特別是在收集 / 匯總依賴 Bazel 快取狀態的指標 (例如建構要求大小指標) 時。它們也代表了兩種不同的使用者體驗。與從頭開始執行簡潔建構作業相比 (由於快取為冷快取,因此耗時較久),當開發人員重複執行程式碼時,增量建構作業會更頻繁地發生 (由於快取通常已為熱快取,因此通常會更快)。

您可以使用 BEP 中的 CumulativeMetrics.num_analyses 欄位來分類版本。如果是 num_analyses <= 1,表示這是簡潔的建構;否則,我們大範圍可以將其分類可能是漸進式的建構,因為使用者可能會改用不同的標記或不同的目標,從而可以有效地清理建構。任何更嚴格的增量定義都可能以經驗法則的形式出現,例如查看載入的套件數量 (PackageMetrics.packages_loaded)。

將決定性的建構指標做為建構效能的代理值

由於某些指標具有非確定性 (例如 Bazel 的 CPU 作業時間或遠端叢集上的佇列時間),因此在測量建構效能時可能會遇到困難。因此,使用確定性指標做為 Bazel 完成的工作量代理值,可能會對其效能造成影響。

建構要求的大小可能會對建構效能造成重大影響。版本越大,在分析及建構建構圖形方面的工作越多。隨著新增/建立更多依附元件,建構作業的複雜度也會隨之增加,因此建構作業的成本也會增加,這就是建構作業自然成長的過程。

我們可以將這個問題切割成不同的建構階段,並使用下列指標做為各階段完成工作的代理指標:

  1. PackageMetrics.packages_loaded:成功載入的套件數量。這裡的回歸代表需要在載入階段讀取及剖析每個額外的 BUILD 檔案,因此需要進行更多工作。

    • 這通常是因為新增了依附元件,且必須載入其傳遞關閉項目。
    • 使用 query / cquery 找出可能新增依附元件的來源。
  2. TargetMetrics.targets_configured:代表建構中設定的目標和切面數量。回歸代表建構及瀏覽已設定的目標圖表需要更多工作。

    • 這通常是因為新增了依附元件,並且必須建構其轉換閉包的圖表。
    • 使用 cquery 找出可能新增依附元件的來源。
  3. ActionSummary.actions_created:代表在建構中建立的動作,而迴歸則代表在建構動作圖時需要更多工作。請注意,這也包含可能尚未執行且未使用的動作。

  4. ActionSummary.actions_executed:已執行的動作數量,直接迴歸代表在執行這些動作中的工作更多。

    • BEP 會寫出動作統計資料 ActionData,顯示執行次數最多的動作類型。根據預設,這項服務會收集前 20 種動作類型,但您可以傳入 --experimental_record_metrics_for_all_mnemonics,收集已執行的所有動作類型這類資料。
    • 這應該有助於您瞭解執行了哪些額外的動作。
  5. BuildGraphSummary.outputArtifactCount:執行動作所建立的構件數量。

    • 如果執行的動作沒有增加,表示規則實作可能已變更。

這些指標都受到本機快取狀態的影響,因此您應確保從當中擷取這些指標的建構作業是「乾淨的建構作業」

我們發現,任何一項指標的回歸都可能伴隨系統時間、CPU 時間和記憶體用量的回歸。

使用本機資源

Bazel 會在本機電腦上使用各種資源 (用於分析建構圖表和驅動執行作業,以及執行本機動作),這可能會影響機器執行建構作業和其他工作時的效能 / 可用性。

使用時間

或許最容易受到雜訊影響的指標 (從建構到建構的可能差異很大) 是時間,尤其是實際時間、CPU 時間和系統時間。您可以使用 bazel-bench 取得這些指標的基準,並利用足夠數量的 --runs 提升測量結果的統計顯著程度。

  • 壁鐘時間是指實際經過的時間。

    • 如果只有實際時間回歸,建議您收集 JSON 追蹤記錄設定檔,並查看差異。否則,調查其他迴歸指標可能會影響實際時間,因此會更有效率。
  • CPU 作業時間是指 CPU 執行使用者程式碼花費的時間。

    • 如果兩個專案提交作業的 CPU 時間出現倒退情形,建議您收集 Starlark CPU 設定檔。您可能也應該使用 --nobuild,將建構作業限制在分析階段,因為大部分的 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 infoBEP

  • bazel info used-heap-size-after-gc:呼叫 System.gc() 後,已用記憶體的大小 (以位元組為單位)。

    • Bazel bench 也會提供這項指標的基準。
    • 此外還有 peak-heap-sizemax-heap-sizeused-heap-sizecommitted-heap-size (請參閱說明文件),但不太相關。
  • BEPMemoryMetrics.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 開啟「不含位元組的建構」功能。這樣一來,系統就不會下載不必要的中繼構件,進而加快建構作業。

另一個選項是設定本機磁碟快取,以節省下載頻寬。