Veri Modeli
StableHLO programları, tensörler üzerinden yapılan hesaplamalardır
(n boyutlu diziler) içerir. Geçerli modelde
Tensor
sınıfı. Bir Tensor
nesnesinin temel depolama sınıfı,
detail::Buffer
, tensörün mlir::ShapedType
değerini bir
Değişebilir tensör blobunu temsil eden mlir::HeapAsmResourceBlob
nesnesi
bitişik bayt dizisi olarak yerleştirilmiş veriler
anaden alta sıra.
Bellek yönetimini basitleştirmek için detail::Buffer
nesne referans sayılır.
Bir tensörün bağımsız öğeleri, Element
sınıfı kullanılarak temsil edilir.
APInt
, APFloat
veya
Depolama alanı için pair<APFloat,APFloat>
. Sonuncusu ise öğeleri saklamak için
birlikte çalışır.
Tensor
, bağımsız öğeleriyle etkileşim kurmak için aşağıdaki API'lere sahiptir:
Element Tensor::get(llvm::ArrayRef<int64_t> index)
: Birindex
çok boyutlu dizinindeki tek tensör öğesi (Element
) nesnesini tanımlayın.void Tensor::set(llvm::ArrayRef<int64_t> index, Element element);
:Element
nesnesinielement
çok boyutlu bir tensöre güncellemek içinindex
dizini.
Çevirmenin çalışma şekli
Çevirmene giriş işlevi,
SmallVector<Tensor> eval(func::FuncOp func, ArrayRef<Tensor> args);
Bu da şunları yapar:
func
SSA bağımsız değişkenlerini ve ilişkili çalışma zamanını (Tensor
) izler değerleri, simge tablosu haritası kullanılarakargs
biçiminde sağlanır, M.- SSACFG sırasına göre
func
içindeki her işlem için:- Operasyonda
eval
çağırır. İşlemin her SSA işleneni içineval
çağrısına bağımsız değişken olarak sağlanacak M'deki çalışma zamanı değeri. - İşlemin SSA sonuçlarını ve değerlendirilen değeri M cinsinden izler.
- Operasyonda
(2) fıkrasında belirtilen işlem düzeyi eval
,
anlamına gelir. Aşağıda stablehlo::AddOp
için bir örnek verilmiştir.
Örnekte, lhs
ve rhs
tensörlerinin bağımsız öğeleri ikilidir
Element
nesne olarak ayıklandı ve bunlar daha sonra eklenir. Eklemenin sonucu olarak,
bir Element
nesnesi tanımlanır ve son result
tensöründe depolanır.
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;
}
Genel olarak, çevirmenin tasarımı
okunabilirlik açısından optimize edilmiştir.
Bağımsız operasyonlar için eval
işlevlerinin
StableHLO için bir referans uygulama işlevi görür. Örneğin,
eval
öğesini bir şablon işlevi olarak tanımlama ve öğe türleriyle parametreleştirme,
farklı öğe türlerinin nasıl işlendiğine ilişkin ayrıntıları
Element::operator+
vb., eval
uygulanmasını basitleştirir.
Sabit katlama için çevirmeni kullanma
Sabit işlem gören işlemleri katlamak için çevirmen mekanizmasını kullanabiliriz.
değerler. Aşağıdaki kod snippet'i, tablodaki
kayan nokta türünde işlenenlerle stablehlo::AddOp
katlama için:
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);
}
Şu anda çevirmeni Google Haberler'e entegre etmek için
devamlı katlamaya odaklanıyoruz. Çünkü StableHLO için klasör uygulamayı
Ancak ileride, çevirileri otomatik olarak yapan ve
Bu noktada kod snippet'inin ergonomisini geliştireceğiz,
(örneğin, sabit işlenenleri
Tensor
nesne yapar ve Tensor
sonucunu OpFoldResult
hedefine açar.
StableHLO çevirmenini test etme
Çevirmen (A) StableHLO programına ve (B) veri değerlerini
çalıştırılmasını sağlar ve aşağıdakilerle eşleşen çıkış veri değerleri oluşturur:
verileri karşılaştırabilirsiniz. Veri değerleri (B)
stablehlo.constant
işlemleri kullanılarak programın kendisinde sabit kodlanır. İlgili içeriği oluşturmak için kullanılan
çevirmen giriş programını değerlendirir. Test edilen işlemin çıktıları
kontrollerle (ör.check.expect_eq
, check.expect_almost_eq
) kontrol edilir.
aşağıda gösterilmiştir. check.expect_eq
ve check.expect_eq_const
bit tabanlı veri kullanımını kontrol eder
desteklenen tüm türler için eşittir ve check.expect_almost_eq
ve
check.expect_almost_eq_const
, bir tolerans dahilinde yakın eşitliği kontrol eder.
test kılavuzunda (G6) açıklanmıştır.
// 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
}
Test yardımcı programı stablehlo-translate --interpret
(kod)
programı ayrıştırmak,
fonksiyon içeren işlemlerdir. Özel bir test paketimiz var.
çeşitli çalışma zamanı davranışlarını uygulayan birkaç testtir. Her StableHLO Op.
Testlere buradan ulaşabilirsiniz.
Test yönergeleri
(G1) Her işlem için desteklenen tüm türleri test etmemiz gerekir mi?
Karar vermek için aşağıdaki kuralların bir kombinasyonunu kullanabiliriz:
Bir işlemi uygularken, karşılık gelen
eval
içinde kod varsa fonksiyonunu kullanıyorsanız test veya denemenizin çok kolaylaşır. Örneğin,add
işlemi için özel bir kod vardır. tamsayı, boole, kayan nokta ve karmaşık türleri ele alacağız. Dolayısıyla, Her tür kategorisi için bir test gerekiyor.Bir tür grubu, karşılık gelen
eval
işlevinde eşit şekilde işlenir. bu türler için tek bir test yeterli olacaktır. Örneğin,add
işlemi için tam sayı türlerinin tüm varyantları (si4
,u4
,si8
,u8
) vb.)llvm::APInt
API'ler kullanılarak benzer şekilde işlenir ve bu nedenle her bir varyant için test ekleme ve bunun yerine tek bir test ekleme temsili bir testtir. Temsilci seçiminde karışıklığa yol açmamak için aşağıdaki yönergelere uygun olmalıdır:- Eşit şekilde işlenen tüm türler aynı temel türdeyse (yani tümü tam sayı, kayan nokta veya karmaşık türler içeriyorsa) maksimum bit genişliğine sahip olanı seçin.
- Eşit şekilde işlenen tüm türlerde temel türlerin bir karışımı varsa azalan düzende, aşağıdaki temel türde olanı seçin tercih: tam sayı, kayan nokta, boole, karmaşık.
(G2) Bir operasyonu ele almak için gereken test sayısına nasıl karar nasıl?
Amaç, operasyonla ilgili çevirmenin mantığını kapsamlı bir şekilde ele almaktır. (ör. uygulamanın tüm köşesi) minimum sayıda testle. Test sayısını en aza indirmek sürdürülebilirlik açısından önemlidir. Ne kadar az test yapılır? onları gözden geçirmek ve nihai karara varmalarının kapsamlı bir şekilde ele almak Sonuç olarak, daha basit işlemlerin çoğunun tek bir test yapılır. Herhangi bir nedenle kapsamlı kapsam pratik değildir, o zaman >= %90'da durabilirsiniz. Buna karar verilecek istek bazında değerlendirilir.
(G3) Çevirmen altyapısı için testler eklemeye ne dersiniz?
Çevirmen altyapısı çoğunlukla basittir ve
güven tabanımıza da bakabilirsiniz. Önemsiz olan tek bölüm, farklı türlerin her bir
temel çevirmen deposundan ambalajından çıkarılır. (G1) kapsamında ele aldığımız gibi,
yalnızca farklı şekilde ele alınan işlem türlerini test eder. Entegre
paketleme/ambalaj açma kodunun farklı pakete karşılık gelen
tam sayı/kayan nokta türlerinin varyantları,
teşvik etmek anlamına gelir. Tam kapsamı sağlamak için constant
gibi bir operasyon
tüm StableHLO öğe türlerini destekler ve kapsamlı testler yazar.
(G4) Bir işlemin uygulanması diğer operasyonlara bağlıysa ne anlama geliyor?
Hayır. Örneğin, batch_norm_grad
aşağıdaki ölçütlere göre kullanılabilir:
divide
, subtract
, multiply
ve diğerleri. İkinci seçeneği test etmekten kaçınmalıyız.
diğer işlemleri de yapabilirsiniz.
(G5) Tanımlanmış / tanımlanmamış değişiklikleri uygulamak için testler yazmalı mıyız? nelerdir?
Uygulama tarafından belirlenen ya da işlemidir. Uygulama tanımlı davranışları uygulamayı test eder çevirmenin yerel bir davranış sergilemesi, bir sonuç elde etti. Tanımlanmamış davranış kullanan testlerin anlamak için çok önemlidir.
(G6) Kayan nokta türleri için testleri yazarken, kontrollerde belirtilmesi gerekir.
Temel işlemler (toplama, çıkarma, çarpma, bölme ve
kare), IEEE spesifikasyonuna uyan bir uygulamanın
matematiksel tam sonucun 0,5 ULP'si içinde yuvarlanan sonuç. Bununla birlikte,
bu işlemlerden beklenen sonucun
en fazla 1 ULP olmalıdır. Ancak bu, transandantal işlevlerde işe yaramayabilir.
(sine
, cosine
vb.)
(gerekçe) ifade eder.
Mevcut uygulama, tolerans değerini girin. Aşağıdaki örnekte, yukarıdaki toleransın işleyiş şekli gösterilmektedir.
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
}
Bu, StableHLO işlemlerinin sayısal doğruluğunu test etmenin ilk adımıdır. Şu anda bu StableHLO spesifikasyonunda az belirtilmiş bir alandır ve anlamak için devam eden çalışmalar #1156 pratikte StableHLO kullanma deneyimimiz ve yardımcı olmaktır. Bu çalışmalar devam ederken altyapıyı güncelleyeceğiz. buna göre hazırlar.
(G7) Testlerin kodlama stiliyle ilgili bir şey var mı?
- Varsayılan değer belirlemek yerine girişlerin/çıkışların gerçek adını kullandığınızdan emin olun değeri SSA değerlerine (ör. %0, %1 vb.)
- Varsa, testlerin okunaklı biçim kullandığından emin olun.
(G8) Spesifikasyonda halihazırda sağlanan örneği dahil etmeli miyiz? Evet (testin eksiksiz olması için).