Element StableHLO został pierwotnie pobrany z dialektu MHLO i odziedziczył implementację MHLO wnioskowania typu. Postęp implementacji można śledzić w pliku status.md.
Poniższe wytyczne mają na celu zapewnienie implementacji wysokiej jakości weryfikatorów i funkcji kształtu w operacjach StableHLO.
Propozycja
Propozycje dotyczą zarówno sprawdzania istniejących wdrożeń, jak i przeprowadzania nowych operacji aż do momentu, gdy zostaną omówione w pełni.
(P1) Użyj specyfikacji StableHLO jako źródła danych
Parametr spec (specyfikacja) jest źródłem wiarygodnych danych dla wszystkich weryfikatorów i funkcji kształtu w operacjach StableHLO. Przeanalizuj ponownie obecne weryfikatory i funkcje kształtu w przypadku każdej operacji, aby były w pełni zgodne ze specyfikacją. Pamiętaj, że specyfikacja wciąż ewoluuje. Jeśli specyfikacja operacji nie jest dostępna, źródłem wiarygodnych danych powinna być implementacja XLA, np. xla/service/shape_inference.cc i xla/service/hlo_verifier.cc. Implementacja XLA nie obejmuje nieograniczonej dynamiki, dlatego w przypadku nieograniczonej dynamiki będziemy stosować zdrowy rozsądek, dopóki nie będzie dostępny RFC dynamicznego obiektu.
(P2) Pełne wykorzystanie ODS
Pliki ODS (np. StablehloOps.td) definiują operacje z cechami i typami dla każdego operandu, atrybutu lub wyniku i przeprowadzają weryfikację. Dlatego w mechanizmach weryfikacyjnych i funkcjach kształtowania usług, które już są gwarantowane przez ODS, nie jest wymagany żaden kod weryfikacyjny. Jeśli kod weryfikacyjny zostanie powielony w ODS, usuń go, ponieważ ten kod nigdy nie zostanie wywołany.
Czy trzeba dodać testy pod kątem ograniczeń z ODS? Więcej informacji znajdziesz w artykule o ustalaniu wytycznych dotyczących testowania.
(P3) Obsługa kodu weryfikacyjnego w funkcjach weryfikatorów i kształtowania
Obie możliwości:
- weryfikatory: zaimplementowane przez
Op::verify()
, - funkcje kształtów: zaimplementowane za pomocą elementów
InferTypeOpInterface
, takich jakOp::inferReturnTypes()
lubOp::inferReturnTypeComponents
może mieć kod weryfikacyjny do sprawdzania operandów, atrybutów i wyników. Wstępny podział może wyglądać tak: weryfikatorzy powinni sprawdzić operandy/atrybuty, a następnie pozwolić funkcjom kształtu tylko na obliczanie wywnioskowanych typów wyników i sprawdzanie zgodności z typami wyników rzeczywistych. Jednak w rzeczywistości podział ten ma jednak kilka problemów:
- Funkcja kształtu może zostać wywołana przez automatycznie wygenerowane funkcje
build()
bez wcześniejszego wywoływania weryfikatora. Wszystkie powiązane dane wejściowe muszą być więc zweryfikowane w funkcji kształtu. - Zduplikowany kod: na przykład w przypadku mechanizmów weryfikacji przeprowadzamy pewne przetwarzanie operandów, a potem weryfikujemy wyniki pośrednie. Następnie w funkcjach kształtujących wyniki pośrednie przydają się do wywnioskowania ostatecznych wyników. Te wyniki pośrednie muszą zostać obliczone dwukrotnie.
- Obciążenie techniczne: weryfikacje operacji są realizowane w ramach 2 różnych metod.
Rozwiązanie jest następujące:
W przypadku większości operacji bez regionów (takich jak
PadOp
): umieść cały kod weryfikacyjny w funkcjach kształtu i całkowicie odrzuć weryfikatory.W przypadku operacji z regionami (np.
ReduceOp/IfOp
; pełna lista znajduje się tutaj): automatycznie generowane kreatory nie przyjmują regionów jako parametrów, więc jeśli zawierają one wnioskowanie o typie, funkcja kształtu zostanie wywołana z pustymi regionami (zobacz ten przykład).Jeśli regiony nie są potrzebne do wnioskowania typu (np.
ReduceOp
), umieść w weryfikatorach logikę weryfikacji powiązaną z regionem, a nie funkcje kształtu. Jeśli to nieuniknione, zduplikuj jakiś kod.Jeśli regiony są potrzebne do wnioskowania typu (
IfOp/CaseOp/MapOp
), dodatkowo funkcja kształtu musi sprawdzać, czy regiony nie są wyraźnie puste, nawet jeśli ODS może już zagwarantować ich istnienie w definicji operacji.
(P4) Ustalenie wytycznych dotyczących testowania
Czy muszę dodawać lub utrzymywać testy w ramach weryfikacji objętych usługą ODS?
My nie. Testy powinny koncentrować się na weryfikatorach i funkcjach kształtu, a zmiany w ODS wymagają ponownej weryfikacji tej operacji.
Zachowaj jednak ostrożność w przypadku brakujących elementów: np. jeśli operacja zawiera cechę SameOperandsAndResultShape
, która sprawdza tylko kształty, ale nie typ elementu, weryfikacja typów operandów/wyników elementów nadal wymaga testów.
Gdzie przeprowadzamy testy na potrzeby weryfikatorów i wnioskowanie o typie?
Plik ops_stablehlo.mlir zawiera pozytywne przypadki operacji i co najmniej 1 negatywny test na każdy błąd weryfikacji. Funkcja ta może też sprawdzić, czy wywnioskowany zwracany typ jest zgodny z rzeczywistym typem wyniku (nie taki sam jak!).
infer_stablehlo.mlir sprawdza istnienie funkcji kształtu w przypadku operacji w wierszu z wartością hlo_test_infer.get_return_type_components"(%x):...
i sprawdza, czy wywnioskowany typ pasuje dokładnie do oczekiwanego typu. Ogólnie jeden pozytywny test na operację.
Co możesz zrobić
Podczas wdrażania lub ponownego sprawdzania funkcji weryfikatora lub funkcji kształtu:
Wszystkie przypadki pozytywne i negatywne umieść w ops_stablehlo.mlir.
Dodaj jeden pozytywny test w infer_stablehlo.mlir, aby przetestować interfejs.
(Opcjonalnie) Jeśli operacja jest skomplikowana i może obejmować wiele testów, rozważ dodanie osobnego pliku testowego o nazwie
verify_<op_name>.mlir
lubverify_<your_topic>.mlir
w tym samym folderze.