Bazel 的平行評估和成效增幅模型。
資料模型
資料模型由下列項目組成:
SkyValue
。也稱為節點。SkyValues
是不可變動的物件, 包含建構期間建立的所有資料,以及 建構過程例如輸入檔案、輸出檔案、目標和設定 目標。SkyKey
。參照SkyValue
的簡短不可變名稱,例如FILECONTENTS:/tmp/foo
或PACKAGE://foo
。SkyFunction
。依據金鑰和相依節點建構節點。- 節點圖表。內含依附元件關係的資料結構 節點。
Skyframe
。漸進式評估架構 Bazel 的程式碼名稱是 本產品
評估
藉由評估代表建構要求的節點來達成建構。
Bazel 會先找出與頂層金鑰對應的 SkyFunction
SkyKey
。然後,函式會要求評估需要的節點
評估頂層節點,進而產生其他 SkyFunction
呼叫
直到到達分葉節點為止節能綠葉節點通常是
輸入檔案最後,Bazel 會將
頂層 SkyValue
,有一些副作用 (例如檔案中的輸出檔案)
系統) 以及節點之間依附元件的有向非循環圖
和建構作業的關聯
如果 SkyFunction
無法在多張票證中要求 SkyKeys
,
然後推進工作需要的所有節點有一個簡單的例子是評估
變成符號連結的輸入檔案節點:函式會嘗試讀取
並得知這是符號連結,因此擷取檔案系統節點
代表符號連結的目標但這本身可能是符號連結
如此一來,原始函式也必須擷取本身的目標。
函式在程式碼中,會由介面 SkyFunction
和
由名為 SkyFunction.Environment
的介面提供給 API 的服務。這些
函式可執行的動作:
- 呼叫
env.getValue
來要求對其他節點的評估。如果 節點可用,且會傳回其值,否則會傳回null
函式本身應該會傳回null
。如果是後者 評估相依節點後,原始節點建構工具會 但這次相同的env.getValue
呼叫會傳回 非null
的值。 - 呼叫
env.getValues()
可要求評估其他多個節點。 上述操作基本上相同 只是相依節點 系統會同時評估多個項目 - 在叫用期間執行運算
- 有副作用,例如將檔案寫入檔案系統。醫療照護需求 是因為兩個不同函式都避免分步 腳印一般而言,寫入副作用 (資料從 Bazel 向外流出) 在沒有問題的情況下,讀取副作用 (也就是資料流入 Bazel 時 未註冊的依附元件) 則無效,因為這些是未註冊的依附元件。 以免造成錯誤的漸進式建構。
適度採行 SkyFunction
措施,避免以其他方式存取資料
要求依附元件 (例如直接讀取檔案系統)
這樣 Bazel 就不會在檔案上註冊資料依附元件
因此導致錯誤的漸進式建構作業
當函式累積足夠的資料可以執行工作後,應傳回非 null
。
這項評估策略有許多好處:
- 密封設計。如果函式只要求輸入資料, Bazel 就能保證輸入狀態相同時 會傳回相同的資料如果所有天體函數具有確定性,那麼 整個建構作業也會具有確定性
- 正確且完美的成效增幅。如果所有函式的所有輸入資料 Bazel 只能將所需節點 在輸入資料變更時失效。
- 平行處理工作數量由於函式只能透過 要求依附元件、不依附的函式 且 Bazel 可以保證結果會與 依序執行
成效增幅
由於函式只能依據其他節點存取輸入資料,因此 Bazel 可以建立從輸入檔案到輸出的 檔案,然後使用這項資訊,只重新建構實際需要的節點 即將重新建構:一組已變更的輸入檔案之反向遞移。
具體來說,有兩種可能的成效增幅策略:由下而上策略 和從上往下一探究竟依據依附關係圖的方式,判斷何者最為合適 就像這樣
在底標失效期間,建立圖表並變更組合後 所有節點都會失效,連帶使所有節點失效 已變更檔案。如果您打算建構相同的頂層節點,則這是最佳的做法 可以選取「重新建立」,再次生成新的提示請注意,如要撤銷底部該變更,則必須對所有用戶端執行
stat()
判斷檔案是否遭到變更。這個 可以使用inotify
或類似機制來學習 已變更檔案。在由上而下撤銷作業時,頂層節點的遞移性關閉 且只有這些節點能保留遞移性關閉內容。 如果節點圖較大,此選項會更好,但下一個版本只需要 一小部分: 與「由上而下無效」不同 第二個版本的圖表
Bazel 只會執行由上而上的撤銷作業。
為了進一步取得增量,Bazel 會使用「變更修剪」:如果節點 失效,但重新建構時發現新值相同 視為舊值,因這個節點變更而失效的節點 就會被「復活」。
這種做法非常實用,例如當有人變更 C++ 檔案中的註解時,就會產生以下效果:
因此產生的 .o
檔案會相同,因此不必呼叫
連結器。
增量連結 / 編譯
這個模型的主要限制在於撤銷節點時 全無關聯:當依附元件變更時,依附節點一律會變更 重新建構,即便有更好的演算法可以變更 變更節點的舊值以下舉例說明 有幫助:
- 增量連結
- 當 JAR 檔案中的單一類別檔案變更時, 在原地修改 JAR 檔案,而不是從頭開始建構。
Bazel 為何無法依據原則支援這些特性 是兩倍
- 獲得的成效提升有限。
- 難以驗證變異的結果 而且 Google 重視的是 可重複執行。
直到目前為止,只要分解 執行成本高昂的建構步驟及進行部分重新評估例如: 您可以在 Android 應用程式中將所有類別分割成多個群組,以及 dex 所以需要分開管理如此一來,當群組中的類別維持不變時 其實不必重做
對應 Bazel 概念
這是金鑰 SkyFunction
和 SkyValue
的概略摘要
Bazel 用來執行建構的實作:
- FileStateValue。
lstat()
的結果。如果是現有檔案, 函式也會計算其他資訊,以便偵測 檔案。這是 SkyFrame 圖表中最低層級的節點,因此沒有 依附元件 - FileValue。使用任何與實際內容無關的項目
檔案的解析路徑取決於對應的
FileStateValue
和 任何需要解析的符號連結 (例如a/b
的FileValue
需要a
的解析路徑和a/b
的解析路徑)。FileValue
與FileStateValue
之間的區別非常重要 後者可以在檔案內容沒有的情況下使用 但其實不然舉例來說,如果檔案內容不相關 評估檔案系統 glob (例如srcs=glob(["*/*.java"])
)。 - DirectoryListingStateValue。
readdir()
的結果。喜歡FileStateValue
,這是最低層級的節點,沒有依附元件。 - DirectoryListingValue。使用任何與項目相關的項目
檔案。視對應的
DirectoryListingStateValue
而定,: 以及與目錄相關聯的FileValue
- PackageValue。代表 BUILD 檔案的剖析版本。取決於
相關
BUILD
檔案的FileValue
,並間接用於任何 用於解析套件中的 glob 的DirectoryListingValue
(在內部代表BUILD
檔案內容的資料結構)。 - ConfiguredTargetValue。代表已設定的目標,也就是元組
一組動作產生的這組動作
提供給相依設定的目標取決於
PackageValue
:對應目標位於ConfiguredTargetValues
以及代表建構作業的特殊節點 此外還會從 0 自動調整資源配置 您完全不必調整資源調度設定 - ArtifactValue。代表建構中的檔案,可以是來源或
然後輸出構件構件幾乎等於檔案,可以
請參考實際執行建構步驟中的檔案。來源檔案
取決於相關聯節點的
FileValue
,以及輸出構件 取決於任何動作的ActionExecutionValue
構件。 - ActionExecutionValue。代表動作的執行。取決於
其輸入檔案的
ArtifactValues
。它執行的操作包含在 這與 SkyKeys 應該 小型請注意,如果符合以下條件,則不會使用ActionExecutionValue
和ArtifactValue
執行階段就不會執行
這張示意圖為視覺輔助,這張圖表展示了 在建構 Bazel 本身之後實作 SkyFunction: