資料模型
StableHLO 程式是張量的運算
(目前模型中是使用
Tensor
類別。Tensor
物件的基礎儲存空間級別
detail::Buffer
,會儲存張量的 mlir::ShapedType
以及
mlir::HeapAsmResourceBlob
物件,代表張量的可變動 blob
資料以連續位元組陣列的形式
大到小的順序。
detail::Buffer
物件會進行參考計數,以簡化記憶體管理。
張量的個別元素是以 Element
類別表示,
使用的聯集具有以下其中一項 APInt
、APFloat
或
pair<APFloat,APFloat>
的儲存空間。最後一個用途是儲存元素
較複雜的類型
Tensor
具有下列 API,可與其個別元素互動:
Element Tensor::get(llvm::ArrayRef<int64_t> index)
:如要擷取 多維度索引index
的個別張量元素作為Element
物件。void Tensor::set(llvm::ArrayRef<int64_t> index, Element element);
: 如何將Element
物件element
更新為多維度的張量 索引index
。
翻譯模式的運作方式
解譯器的項目函式是
SmallVector<Tensor> eval(func::FuncOp func, ArrayRef<Tensor> args);
這會執行以下動作:
- 追蹤
func
的 SSA 引數及其相關執行階段Tensor
值 (以args
格式提供,並使用符號表對應,M. - 針對
func
內的每項運算,按 SSACFG 順序:- 針對運算叫用
eval
。為運算的每個 SSA 運算元,擷取其 M 的執行階段值,要提供做為eval
叫用的引數。 - 追蹤運算的 SSA 結果以及 M 中經評估的值。
- 針對運算叫用
上述 (2) 中提到的運算層級 eval
負責實作
運算的執行語意。以下是 stablehlo::AddOp
的範例。
在此範例中,lhs
和 rhs
張量的個別元素是成對的
擷取為 Element
物件,然後加入這些物件新增之後
Element
物件,會儲存在最終的 result
張量中。
Tensor eval(AddOp op, const Tensor &lhs, const Tensor &rhs) {
Tensor result(op.getType());
for (auto it = result.index_begin(); it != result.index_end(); ++it)
result.set(*it, lhs.get(*it) + rhs.get(*it));
return result;
}
整體而言,翻譯器的設計經過最佳化調整,
為個別作業實作 eval
函式,以便用於個別作業
做為 StableHLO 的參考實作。舉例來說
將 eval
定義為範本函式,並使用元素類型參數化。
我們整理了在
Element::operator+
等,簡化 eval
的實作。
使用翻譯器持續折疊
我們可以使用直譯器機制,以常數運算元摺疊運算
輕鬆分配獎金下列程式碼片段示範實作方式
適用於使用浮點類型的運算元摺疊 stablehlo::AddOp
:
OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
auto attrs = adaptor.getOperands();
DenseElementsAttr lhsData = dyn_cast<DenseElementsAttr>(attrs[0]);
DenseElementsAttr rhsData = dyn_cast<DenseElementsAttr>(attrs[1]);
if (!lhsData || !rhsData) return {};
auto lhs = Tensor(lhsData);
auto rhs = Tensor(rhsData);
auto result = eval(*this, lhs, rhs);
SmallVector<APFloat> values;
for (auto i = 0; i < result.getNumElements(); ++i) {
Element element = result.get(i);
values.push_back(cast<FloatAttr>(element.getValue()).getValue());
}
return DenseElementsAttr::get(result.getType(), values);
}
我們目前不會主動將翻譯模式整合到
持續折疊,因為我們不打算為 StableHLO 實作資料夾。
不過,我們將在未來利用解譯器
您將在 MHLO 中折疊,這樣我們就會改善程式碼片段的人體工學
以上 (例如,我們可以使用輔助函式將常數運算元封裝為
Tensor
物件並將 Tensor
結果解壓縮為 OpFoldResult
)。
測試 StableHLO 解譯器
解譯器將輸入 (A) StableHLO 程式,以及 (B) 資料值
並產生輸出資料值
比對使用者提供的資料資料值 (B) 為
在程式本身使用 stablehlo.constant
作業進行硬式編碼。
解譯器會評估輸入程式受測試的作業輸出內容
通過檢查 (例如 check.expect_eq
、check.expect_almost_eq
),
如下所示。check.expect_eq
和 check.expect_eq_const
會檢查位元
任何支援的類型與 check.expect_almost_eq
的相等
check.expect_almost_eq_const
在容忍度內檢查是否有接近相等的情形。
中說明瞭浮點和複雜類型的測試準則 (G6)。
// CHECK-LABEL: Evaluated results of function: add_op_test_ui4
func.func @add_op_test_ui4() {
%0 = stablehlo.constant dense<[0, 2]> : tensor<2xui4>
%1 = stablehlo.constant dense<[15, 3]> : tensor<2xui4>
%2 = stablehlo.add %0, %1 : tensor<2xui4>
check.expect_eq_const %2, [15, 5] : tensor<2xui4>
func.return
}
測試公用程式 stablehlo-translate --interpret
(程式碼)
負責剖析程式,並解讀所有函式,包括
構成函式的作業。我們有專門的測試套件
分別針對每項 StableHLO 運算作業執行不同執行階段行為的多項測試。
您可以在這裡找到測試項目。
檢測指引
(G1) 是否需要針對每項運算支援所有支援的類型進行測試?
我們可以透過組合下列規則來決定:
實作運算時,如果對應的
eval
中有程式碼 函式處理特定類型的檔案,就一定要執行測試 來蓋住該類型舉例來說,add
運算有一個專屬程式碼 處理整數、布林值、浮點和複雜型別, 每個類型都需要執行一項測試如果系統在對應的
eval
函式中統一處理一組類型, 那麼只要針對所有類型執行單一測試即可。舉例來說 對於add
運算,所有整數類型的變數 (si4
、u4
、si8
、u8
依此類推) 使用llvm::APInt
API 進行處理,因此可以略過 為每個變化版本新增測試 代表性測試。為避免釐清選定代表的模糊程度, 使用下列指南:- 如果所有型別皆以一致的方式處理,系統會使用相同的原始類型 (也就是說,如果所有類型都是整數、浮點或複雜類型),則: 請選擇位元寬度上限的叢集
- 如果所有型別皆以一致的方式處理,同時含有多種原始型別,則 選擇下列原始類型 (以遞減方式排序) 偏好:整數、浮點數、布林值、複雜性。
(G2) 我們如何決定特定作業所需的測試次數 行為?
目的在於全面涵蓋 O 運算的翻譯邏輯 (也就是導入的所有極端情況) 搭配最少的測試數量。 請務必盡量減少測試次數,以利維護。測試數量越少 使用起來會更加容易 完全涵蓋重點因此,我們預期 作業最後只會執行一次測試如果出於某種原因全面 覆蓋範圍不實際,只要 >= 90% 以上即可停止這將決定 在提取要求審查期間依個案情況提交。
(G3) 如何為翻譯基礎架構新增測試?
翻譯基礎架構大致上相當簡單,而且可新增至
Google 的信任基礎唯一的步驟是每種類型的封裝方式
從基礎翻譯器儲存空間解壓縮如 (G1) 所述
只會測試這兩種運算方式不同的運算類型。取代為
封裝/解壓縮程式碼可能對應至
整數/浮點類型的變數在測試期間可能無法完整涵蓋
進行測試。為確保完整報導,我們可以選擇像 constant
這樣的運算,
支援所有 StableHLO 元素類型,並編寫詳盡的測試。
(G4) 如果作業的實作取決於其他作業,我們應該撰寫 進行後者?
否。例如,batch_norm_grad
的實作方式可依據
divide
、subtract
、multiply
和其他來源。我們應該避免測試
對 A/B 測試
(G5) 是否應該撰寫測試,以便執行導入定義 / 未定義的 行為?
我們不應撰寫會行使實作定義或 運算的未定義行為。執行實作定義行為的測試 表現出翻譯器的當地行為, 一般化未定義行為的測試不會計入 運算行為的理解程度
(G6) 編寫浮點類型的測試時,以及 是否需要在檢查中指明結果?
基本運算 (加法、減法、乘法、除法和
表示,採用 IEEE 規格的導入作業必須能夠提供
計算數學結果的 0.5 ULP 範圍內,算出的四捨五入結果。儘管如此
即可安全地想像處理這些作業的預期結果
最多相隔 1 個 ULP。但可能不適用於跨函式
(sine
、cosine
等),
定義 (原因)。
目前的實作方式採用「一體適用的」容許值設為 0.0001 以下範例示範上述容許程度的實際運作情形。
func.func @check_tolerance() {
%0 = stablehlo.constant dense<0.2> : tensor<f32>
// The following check succeeds as %0 is almost equal to the provided
// constant modulo the tolerance, mentioned above.
check.expect_almost_eq_const %0, dense<0.19999> : tensor<f32>
// The following check fails as %0 is not bitwise equal to the provided
// constant.
check.expect_eq_const %0, dense<0.19999> : tensor<f32>
func.return
}
這只是測試 StableHLO 運算數值準確率的第一步。 目前是 StableHLO 規格的少數特定區域, 持續努力找出這個目標 #1156 我們根據實際使用 StableHLO 的經驗, 利害關係人在接下來的架構中, 。
(G7) 測試的程式設計樣式是否有任何問題?
- 請務必使用輸入/輸出的實際名稱,而非預設值 改為 SSA 值 (例如 %0、%1 等)
- 請確定測試使用美版列印 (如果有的話)。
(G8) 是否應該加入規格中提供的範例? 是 (為測試完整性)。