- Dodawanie operacji asynchronicznych do HLO jest uciążliwe (np.
all-reduce-start
iall-reduce-done
). - Podział startu i zakończenia może być nieodpowiedni w przypadku części zastosowań asynchronicznych przypadków.
Aby dotrzeć do pierwszego niedociągnięcia, proponujemy wprowadzenie ostatniego zestawu nowych
asynchroniczne kody operacji: kAsyncStart
, kAsyncUpdate
i kAsyncDone
. Pomysł
jest utworzenie ogólnego asynchronicznego kodu operacji, który opakowuje dowolną instrukcję HLO.
Rzeczywista operacja, która zostanie wykonana asynchronicznie, zostanie zakodowana za pomocą
czyli tzw. obliczenia, w których pierwiastkiem jest tylko instrukcja
dla danych wejściowych. Obsługa bufora wejścia/wyjścia i stosowanie aliasów
który można udostępnić dla dowolnej operacji asynchronicznej. Instrukcje uruchamiania asynchronicznego
kształt wyjściowy będzie krotką operandów wejściowych, wartości wyjściowych i dowolnych
stan pośredni wymagany przez async-update
lub async-done
za instrukcje.
%async_op {
%param0 = f32[64] parameter(0)
ROOT %op = f32[32] op(f32[64] %param0), op_specific_attr=”foo”
}
%async-start = (f32[64], f32[32], s32[]) async-start(f32[64] %operand),
calls=%async_op
%async-done = f32[32] async-done((f32[64], f32[32], s32[]) %async-start)
W powyższej reprezentacji tylko async-start
wywołało funkcję obliczeń od
łatwo jest znaleźć działanie funkcji async-done
, podążając za jej argumentem
znajdź odpowiednią wartość async-start
, aby znaleźć tzw. obliczenia.
Pamiętaj też o tym
że pierwszy element w krotce wyjściowej async-start
zawiera alias z parametrem
operand, dzięki czemu bufor pozostanie aktywny do momentu wykonania instrukcji (co najmniej do czasu wykonania instrukcji asynchronicznej).
Podobnie drugi element aliasuje dane wyjściowe async-done
, a parametry
trzeci element to stan kontekstu służący do śledzenia
operacji asynchronicznej. Ta reprezentacja obsługuje również wiele tensorów w
dane wejściowe lub wyjściowe operacji asynchronicznej i aliasy działają tak samo
sposób:
%async_op {
%param0 = f32[64] parameter(0)
%param1 = f32[64] parameter(1)
ROOT %op = (f32[32], f32[32]) op(f32[64] %param0, f32[64] %param1),
op_specific_attr=”foo”
}
%async-start = ((f32[64], f32[64]), (f32[32], f32[32]), s32[])
async-start(f32[64] %operand0, f32[64] %operand1),
calls=%async_op
%async-done = (f32[32], f32[32]) async-done(%async-start)
Dodatkowo działanie można podzielić na zero lub więcej async-update
.
do wykonywania obliczeń pośrednich. Aliasy wejściowe/wyjściowe działają
w ten sam sposób z instrukcją async-update
i każdym elementem async-start
i
Instrukcje typu async-update
muszą należeć do jednego użytkownika
async-update
lub async-done
:
%async_op {
%param0 = f32[64] parameter(0)
ROOT %op = f32[32] op(f32[64] %param0), op_specific_attr=”foo”
}
%async-start = (f32[64], f32[32], s32[]) async-start(f32[64] %operand),
calls=%async_op
%async-update0 = (f32[64], f32[32], s32[]) async-update(
(f32[64], f32[32], s32[]) %async-start)
%async-update1 = (f32[64], f32[32], s32[]) async-update(
(f32[64], f32[32], s32[]) %async-update0)
%async-done = f32[32] async-done((f32[64], f32[32], s32[]) %async-update1)
Składnia cukru
Ponieważ istnieją oddzielne obliczenia w celu zdefiniowania operacji, która zostanie asynchronicznie jest uciążliwe, ale oferujemy też cukier składniowy, automatycznie drukuje i analizuje operacje asynchroniczne tak, jakby były to operacje pierwszej klasy opcodes. Chodzi o traktowanie sufiksów „-start”, „-update” i „-done” specjalnie przez automatyczne tworzenie obliczeń i instrukcji (bez sufiksu). Na przykład ten fragment kodu może być na następujące. Oba te współczynniki można interpretować jako takie same:
%op-start = (f32[64], f32[32], s32[]) op-start(f32[64] %operand),
op_specific_attr=”foo”
%op-update0 = (f32[64], f32[32], s32[]) op-update(
(f32[64], f32[32], s32[]) %op-start),
op_specific_attr=”foo”
%op-update1 = (f32[64], f32[32], s32[]) op-update(
(f32[64], f32[32], s32[]) %op-update0)
%op-done = f32[32] op-done((f32[64], f32[32], s32[]) %op-update1)
Aby uniknąć niejasności, weryfikator nie zezwoli na operację
powinna być zapakowana z parametrem async-start, jeśli jednoznacznie zdefiniowaliśmy kod operacji
z sufiksami „-start” i/lub „-done”. To też może być ucieczka
w razie potrzeby w razie potrzeby
nie mieści się w modelu opisanym powyżej (np. aliasy wejścia/wyjścia
bufory). Więc początkowo copy-start
/copy-done
,
collective-permute-start
/collective-permute-done
itp. będzie nadal używać
odpowiednie kody opcode pierwszej klasy zamiast nowego
Kody operacji async-start
/async-done
, dopóki nie pozbądźmy się kodu, aby je usunąć
Kody operacji „-start”/”-done”.