Element StableHLO został pierwotnie uruchomiony z dialektu MHLO i odziedziczył implementację MHLO wnioskowania typu. Postęp implementacji można śledzić w pliku status.md.
Proponowane poniżej wytyczne mają na celu wdrożenie wysokiej jakości weryfikatorów i funkcji kształtu w operacjach StableHLO.
Propozycja
Propozycje dotyczą zarówno ponownego sprawdzania istniejących implementacji, jak i realizowania nowych operacji aż do momentu ich kompleksowej obsługi.
(P1) Użyj specyfikacji StableHLO jako źródła danych
spec jest źródłem informacji o wszystkich weryfikatorach i funkcjach kształtu w operacjach StableHLO. Zweryfikuj ponownie istniejące weryfikatory i funkcje kształtujące każdej operacji, aby zapewnić pełną zgodność ze specyfikacją. Pamiętaj, że specyfikacja stale się rozwija. Jeśli specyfikacja operacji jest niedostępna, źródłem wiarygodnych danych powinna być implementacja XLA, w tym 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 znajdzie się RFC dynamiki.
(P2) Wykorzystaj pełnię możliwości ODS
Pliki ODS (np. StablehloOps.td) definiują operacje z wykorzystaniem cech i typów w przypadku każdego argumentu/atrybutu/wyniku i przeprowadzają weryfikację. Dlatego w przypadku usług, które już są gwarantowane przez ODS, nie trzeba stosować kodu weryfikacyjnego. Usuń kod weryfikacyjny, jeśli został on powielony w ODS, ponieważ nigdy nie zostanie wywołany.
Czy trzeba dodawać testy pod kątem ograniczeń ODS? Zapoznaj się z artykułem Ustal wytyczne dotyczące testowania.
(P3) Obsługa kodu weryfikacyjnego w weryfikatorach i kształtowanie funkcji
Obie możliwości:
- weryfikatory: zaimplementowane przez
Op::verify()
, - funkcje kształtu: implementowane przez obiekty
InferTypeOpInterface
, takie jakOp::inferReturnTypes()
lubOp::inferReturnTypeComponents
może mieć kod weryfikacyjny do sprawdzania operandów/atrybutów/wyników. Początkowy podział może wyglądać tak: weryfikatorzy powinni sprawdzić operandy/atrybuty, a następnie umożliwić funkcjom kształtowania tylko wywnioskowane typy wyników i sprawdzanie zgodności z typami wyników rzeczywistych. Jednak w rzeczywistości ten podział ma jednak kilka problemów:
- Funkcja kształtu może zostać wywołana przez automatycznie wygenerowane funkcje
build()
bez wcześniejszego wywoływania weryfikatora. Powiązane dane wejściowe również muszą być zweryfikowane w funkcji kształtu. - Zduplikowany kod: na przykład w mechanizmach weryfikacyjnych wykonujemy przetwarzanie argumentów, a potem weryfikujemy wyniki pośrednie. Następnie w funkcjach kształtujących wyniki pośrednie mogą być przydatne do wywnioskowania ostatecznych wyników. Wyniki pośrednie muszą zostać obliczone dwukrotnie.
- Obciążenia związane z konserwacją: 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 (np.
PadOp
): spróbuj umieścić cały kod weryfikacyjny w funkcjach kształtu i całkowicie odrzuć weryfikatory. Jeśli nie jest to możliwe z powodu braku możliwości określenia typów zwrotów (np. za pomocą funkcjiReshapeOp
lubBroadcastInDimOp
), utwórz weryfikatora, który będzie zawierał niezbędną logikę weryfikacji. Zwykle operacje możliwe do wywnioskowania, takie jakAddOp
, mogą nadal wymagać weryfikatora do przeprowadzenia dodatkowej weryfikacji, ponieważ weryfikuje on ograniczenia podanego typu zwrotu, który jest niedostępny w metodach wnioskowania na podstawie typu/kształtu.W przypadku operacji obejmujących regiony (np.
ReduceOp/IfOp
; pełna lista znajduje się tutaj): automatycznie generowane komponenty nie przyjmują regionów jako parametrów, więc jeśli w tych konstruktorach uwzględniono 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
), zamiast funkcji kształtu umieść w weryfikatorach logikę weryfikacji związaną z regionem. Powiel kod, jeśli jest to nieuniknione.Jeśli regiony są potrzebne do wnioskowania typu (
IfOp/CaseOp/MapOp
), dodatkowo funkcja kształtu musi sprawdzać, czy regiony nie są wyraźnie puste, mimo że ODS może już zagwarantować istnienie swojego działania w definicji operacji.
(P4) Opracowanie wytycznych dotyczących testowania
Czy musimy dodawać lub prowadzić testy w ramach weryfikacji objętych ODS?
My nie. Testy powinny koncentrować się na weryfikatorach i funkcjach kształtu, a zmiany w ODS wymagają ponownej weryfikacji.
Uważaj jednak na brakujące elementy. Jeśli na przykład operacja zawiera cechę SameOperandsAndResultShape
, która sprawdza tylko kształty, ale nie typ elementu, weryfikacja określonych typów operandów/wyników nadal wymaga testów.
Gdzie przeprowadzamy testy weryfikatorów i wnioskowania o typach?
Plik ops_stablehlo.mlir zawiera pozytywne przypadki użycia i (co najmniej) 1 ujemny test na każdy błąd weryfikacji. Umożliwia też sprawdzenie, czy wywnioskowany zwracany typ jest zgodny z rzeczywistym typem wyniku (innym niż!).
infer_stablehlo.mlir sprawdza istnienie funkcji kształtu operacji w wierszu z hlo_test_infer.get_return_type_components"(%x):...
i sprawdza, czy wywnioskowany typ pasuje dokładnie do oczekiwanego. Ogólnie na operację pozytywną.
Co możesz zrobić
Podczas implementowania lub ponownego sprawdzania weryfikatora albo funkcji kształtu:
Wszystkie przypadki pozytywne i negatywne umieść w pliku ops_stablehlo.mlir.
Dodaj jeden test w pliku infer_stablehlo.mlir, aby przetestować interfejs.
(Opcjonalnie) Jeśli operacja jest skomplikowana i może zawierać wiele testów, rozważ dodanie osobnego pliku testowego o nazwie
verify_<op_name>.mlir
lubverify_<your_topic>.mlir
w tym samym folderze.