永久工作站

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

本頁面說明如何使用永久工作站、優點、要求及 瞭解工作站對沙箱的影響。

持續性工作者是 Bazel 伺服器啟動的長時間執行程序,可做為實際工具 (通常是編譯器) 的包裝函式,或做為工具本身。為了讓持續性工作者發揮效益,工具必須支援執行一系列編譯作業,而包裝函式需要在工具的 API 和下方所述要求/回應格式之間進行轉譯。同一個 worker 可能會在相同版本中搭配或不搭配 --persistent_worker 標記呼叫,並負責適當地啟動並與工具對話,以及在退出時關閉 worker。每個工作站執行個體都會獲派 (並非獨立的工作目錄) <outputBase>/bazel-workers

使用永久工作站 執行策略, 啟動負擔、增加 JIT 編譯,並啟用快取 範例。這項策略會向長時間執行的程序傳送多個要求,進而達成這些改善。

永久工作站適用於多種語言,包括 Java、 ScalaKotlin 等等。

使用 NodeJS 執行階段的程式可使用 @bazel/worker 輔助程式庫來實作 worker 通訊協定。

使用持續性 worker

Bazel 0.27 以上版本預設會在執行建構作業時使用持續性工作站,但遠端執行作業會優先執行。針對不支援永久工作站的動作, Bazel 會改回為每個動作啟動工具執行個體。您可以為適用的工具助憶法設定 worker 策略,明確設定建構作業使用持續性工作站。為了提供最佳做法,這個範例會將 local 指定為 worker 策略的備用方案:

bazel build //my:target --strategy=Javac=worker,local

使用 worker 策略而非本機策略,可大幅提升編譯速度 (視實作方式而定)。對於 Java,建構作業的速度可加快 2 至 4 倍,漸進式編譯的速度則可能更快。使用 worker 編譯 Bazel 的速度約為 2.5 倍。詳情請參閱 「選擇工作站數量」專區。

您是否有與本機版本相符的遠端建構環境 您可以使用 動態策略, 它會影響遠端執行和工作站執行作業。如要啟用動態策略,請傳遞 --experimental_spawn_scheduler 標記。這項策略會自動啟用 worker,因此您不需要指定 worker 策略,但仍可使用 localsandboxed 做為備用。

選擇工作站數量

每個記憶庫的預設工作站執行個體數量為 4 個,但可以調整 使用 worker_max_instances 旗標。兩者之間有何權衡取捨 並獲得 JIT 編譯和快取命中的數量。員工人數越多,就可獲得更多 目標將支付執行非 JIT 程式碼的啟動成本,以及冷到 快取。如果您要建構的目標數量不多,單一工作站可能會提供 在編譯速度和資源用量之間取得最佳取捨 (例如 請參閱問題 #8586worker_max_instances 旗標會設定每個專案的工作站執行個體數量上限 助憶鍵和旗幟 (請見下方) 因此,在混合的系統中,您最終可以使用 會大量記憶體。如果是漸進式建構作業 使用多個工作站執行個體 就能享有更小的優勢

此圖表顯示 Bazel (目標) 的從頭編譯時間 //src:bazel) (位於 6 核心超執行緒 Intel Xeon 3.5 GHz Linux 工作站) 搭載 64 GB RAM針對每個工作站設定,系統會執行五項簡潔建構作業,並 以及最後 4 格的平均分數

清理版本效能改善圖表

圖 1. 清理建構作業的效能改善圖表。

在這個設定中,兩個工作站可提供最快的編譯速度,但與一個工作站相比,只提升了 14%。如果您想節省記憶體,使用一個 worker 會是不錯的選擇。

漸進式編譯通常能更完善。乾淨的建構作業 但在編譯之間變更單一檔案很常見, 特別是以測試導向開發上述範例也包含一些非 Java 封裝動作,可能會影響增量編譯時間。

僅重新編譯 Java 來源 (//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar) 但每次變更 AbstractContainerizingSandboxedSpawn.java 速度可達 3 倍 (平均有 20 個暖身版本,執行版本平均為 20 個漸進式版本) 已捨棄):

漸進式版本效能改善圖表

圖 2. 漸進式建構的效能改善圖表。

速度提升幅度取決於所做的變更。在上述情況下,當常用的常數發生變化時,系統會以 6 倍的速度進行測量。

修改持續性 worker

您可以傳遞 --worker_extra_flag 標記,為工作站指定啟動標記,並以助憶法做為索引。舉例來說,傳遞 --worker_extra_flag=javac=--debug 只會為 Javac 啟用偵錯功能。每次使用此標記時,只能設定一個工作站旗標,而且一次只能設定一個記憶式。 系統不僅會為每個助憶法分別建立 worker,也會為其啟動旗標的變化建立 worker。每個助憶法和啟動旗標組合都會合併為 WorkerKey,且每個 WorkerKey 最多可建立 worker_max_instances 個 worker。請參閱下一節,瞭解動作設定如何指定設定標記。

您可以使用 --high_priority_workers 標記,指定要按一般優先度優先執行的記憶法 助憶鍵這有助於優先處理最重要的關鍵動作 路徑。如果有兩個以上高優先順序的工作站正在執行要求,則所有 導致其他工作站無法執行這個標記可重複使用。

傳送 --worker_sandboxing 標記時,每個 worker 要求都會針對其所有作業使用獨立的沙箱目錄 輸入內容設定沙箱需要一點時間, 尤其是在 macOS 上,但提供更好的正確性保證。

--worker_quit_after_build 標記主要用於偵錯和剖析。這個標記會強制所有工作站 而不需要在建構完成後結束您也可以傳遞 --worker_verbose,取得更多關於 worker 執行作業的輸出內容。這個標記反映在 WorkRequest 中的 verbosity 欄位,可讓工作站實作 更詳細。

工作站會將記錄儲存在 <outputBase>/bazel-workers 目錄中, 例子 /tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log。 檔案名稱包含工作站 ID 和記憶法。由於每個助憶法可能會有多個 WorkerKey,因此您可能會看到特定助憶法的 worker_max_instances 記錄檔。

如要進一步瞭解 Android 版本,請參閱 Android 版本效能頁面

實作永久工作站

如要進一步瞭解如何建立工作站,請參閱「建立持續性工作站」頁面。

下方範例為使用 JSON 的工作站,顯示 Starlark 設定:

args_file = ctx.actions.declare_file(ctx.label.name + "_args_file")
ctx.actions.write(
    output = args_file,
    content = "\n".join(["-g", "-source", "1.5"] + ctx.files.srcs),
)
ctx.actions.run(
    mnemonic = "SomeCompiler",
    executable = "bin/some_compiler_wrapper",
    inputs = inputs,
    outputs = outputs,
    arguments = [ "-max_mem=4G",  "@%s" % args_file.path],
    execution_requirements = {
        "supports-workers" : "1", "requires-worker-protocol" : "json" }
)

根據這項定義,這項動作的第一步是從執行 指令列 /bin/some_compiler -max_mem=4G --persistent_worker。要求 編譯 Foo.java 後看起來會像這樣:

注意:雖然通訊協定緩衝區規格使用「駝峰式大小寫」(request_id),但 JSON 通訊協定則使用「駝峰式大小寫」(requestId)。在本文件中,我們會在 JSON 範例中使用駝峰式大小寫,但在討論欄位時,無論通訊協定為何,都會使用駝峰式大小寫。

{
  "arguments": [ "-g", "-source", "1.5", "Foo.java" ]
  "inputs": [
    { "path": "symlinkfarm/input1", "digest": "d49a..." },
    { "path": "symlinkfarm/input2", "digest": "093d..." },
  ],
}

工作者會以 JSON (以換行符號分隔) 格式 (因為 requires-worker-protocol 已設為 JSON) 在 stdin 上接收此資料。接著,worker 會執行動作,並透過 stdout 將 JSON 格式的 WorkResponse 傳送至 Bazel。Bazel,然後 剖析此回應,然後手動將其轉換為 WorkResponse proto。目的地: 使用二進位編碼的 protobuf 來與相關聯的工作站通訊 JSON,requires-worker-protocol 會設為 proto,如下所示:

  execution_requirements = {
    "supports-workers" : "1" ,
    "requires-worker-protocol" : "proto"
  }

如果您未在執行要求中加入 requires-worker-protocol,Bazel 會預設使用 protobuf 的 worker 通訊。

Bazel 會從助憶鍵和共用標記衍生出 WorkerKey,因此如有 設定允許變更 max_mem 參數,其他工作站 。如果使用太多變化版本,可能會導致記憶體用量過多。

每個 worker 目前一次只能處理一個要求。實驗功能 多工工作站功能可讓您 執行緒,前提是基礎工具為多執行緒,且包裝函式設定為 瞭解這點

這個 GitHub 存放區中,您可以看到以 Java 和 Python 編寫的工作站包裝函式範例。如果發生以下情況: 是透過 JavaScript 或 TypeScript 運作 @bazel/worker 套件nodejs 工作站範例 可能會有幫助

工作站對沙箱有何影響?

根據預設,使用 worker 策略不會在沙箱中執行動作,這與 local 策略類似。您可以設定 --worker_sandboxing 標記,讓所有 worker 在沙箱中執行,確保每次執行工具時,只會看到應有的輸入檔案。但這項工具仍可能在要求之間洩漏資訊,例如透過快取。採用「dynamic」策略 「工作站必須受到沙箱保護」

為讓工作站正確使用編譯器快取,系統會將摘要與每個輸入檔案一併傳遞。因此,編譯器或包裝函式可以檢查輸入 而無需讀取檔案。

即使使用輸入摘要來防止不需要的快取,沙箱機制仍然有效 工作人員提供的沙盒技術比純沙盒來得低,因為這項工具 可保留先前要求所影響的其他內部狀態。

只有在 worker 實作支援的情況下,才能啟用多重廣告工作站的沙箱機制, 而且,您必須透過 --experimental_worker_multiplex_sandboxing 標記。詳情請參閱設計文件

延伸閱讀

如要進一步瞭解持續性工作站,請參閱: