Wybierz pytanie, zanim otworzysz ślad
Ślad lepiej odpowiada na precyzyjne pytanie: czy obliczenia czekają na dane, czy kopia jest powtarzana, albo czy mały operator jest wywoływany zbyt często? Zdefiniuj wejście, wyjście i granice swojego kroku. W przypadku treningu określ, czy obejmuje backward, aktualizację optymalizatora i odczyt batcha. W przypadku wnioskowania oddziel wczytywanie modelu od zapytania.
Utrzymuj krótki scenariusz, którego poprawność znasz. Zapisz kształty, batch, precyzję, tryb modelu, ewentualną kompilację i źródło danych. Uproszczone wejście może pomóc wyizolować zachowanie, ale nie musi już reprezentować pełnego obciążenia. Wspomnij o tej różnicy w zapisie.
Ustal jednostkę końcową: milisekundy na krok lub przetworzone przykłady na sekundę. Sumy zdarzeń nie zastępują czasu, który upłynął. Porównuj okna o tych samych granicach odczytu i transferu.
Rozróżnij czas CPU, pracę GPU i oczekiwanie
CPU przygotowuje i uruchamia operacje; GPU może je wykonać później. Interwał w Pythonie może więc zawierać uruchamianie, oczekiwanie albo jedno i drugie. Dokumentacja CUDA w PyTorch wskazuje, że precyzyjne pomiary muszą uwzględniać ten asynchronizm, zwłaszcza przez synchronizację lub zdarzenia dostosowane do zakresu.
Do ukierunkowanego pomiaru czasu GPU mogą posłużyć zdarzenia CUDA. Do opóźnienia end-to-end poczekaj na zakończenie pracy objętej tym opóźnieniem i zmierz całe żądanie. Nie mieszaj tych dwóch jednostek. Synchronizacja dodana między każdą operacją może usunąć rzeczywiste nakładanie się i przekształcić program, który próbujesz zrozumieć.
W profilerze czasy własne operatora i czasy obejmujące podoperacje również odpowiadają na różne pytania. Spójrz na chronologię, zanim zsumujesz wiersze tabeli. Jednoczesne lub zagnieżdżone aktywności nie reprezentują rozłącznych części czasu, który upłynął.
Zarezerwuj fazę startową i aktywne okno
Pierwsze przejście może obejmować inicjalizację, wczytywanie lub kompilację. Zdecyduj, czy twoje pytanie dotyczy tego startu, czy już ustabilizowanej fazy. Zachowaj obie obserwacje, gdy mają znaczenie dla użycia, zamiast usuwać koszt początkowy z średniej przedstawianej jako ogólna.
Funkcja schedule pozwala rozróżnić oczekiwanie, przygotowanie zbierania i aktywne okno. Rozgrzewka profilera nie dowodzi, że twój model osiągnął stabilny tryb pracy. Sprawdź też napotkane kształty i stan pamięci podręcznych. Aplikacja ze zmiennymi wejściami może napotkać nowe ścieżki po kilku iteracjach.
Proponowany poniżej plan dydaktyczny czeka na jeden krok, przygotowuje jeden i przechwytuje dwa. Cztery kroki wystarczą, by wyjaśnić mechanizm, ale nie by ustalić rozkładu wydajności. W rzeczywistej kampanii wybierz uzasadnione okno i po analizie powtórz pomiar poza profilerem.
Proponowany przykład: cztery kroki z czytelnymi granicami
Poniższy fragment nie został wykonany. Zakłada, że istnieją model, optimizer, loss_fn, loader i device, że model znajduje się na device, a loader dostarcza co najmniej cztery batch'e par x, y. Wykonuje aktualizacje: użyj stanu eksperymentalnego przeznaczonego do tego, a nie sesji, której wagi musisz zachować.
Etykiety rozdzielają odczyt CPU, transfer i trening. Iterator jest tworzony przed oknem, co wyklucza część jego startu z zakresu. Plik jest wybierany jako nowy, aby uniknąć nadpisania śladu. Przykład odmawia wykonania, gdy zbieranie danych GPU jest niedostępne, zamiast dyskretnie przedstawiać ślad CPU jako analizę GPU.
Sygnał step() przesuwa harmonogram po każdym kroku. Oficjalny przepis opisuje ten związek między iteracjami a zbieraniem danych. Na stosie ROCm urządzenie PyTorch nadal nazywa się cuda; dostępność zbierania danych akceleratora zależy jednak od builda i jego narzędzi. Sprawdź, jakie aktywności faktycznie występują w wyniku.
from pathlib import Path
import torch
from torch.profiler import (
profile, schedule, record_function,
ProfilerActivity, supported_activities,
)
trace = Path("trace-etape.json")
if trace.exists():
raise FileExistsError("Choisissez un nouveau nom de trace")
activities = [ProfilerActivity.CPU]
if device.type == "cuda":
if ProfilerActivity.CUDA not in supported_activities():
raise RuntimeError("Collecte GPU indisponible dans ce build")
activities.append(ProfilerActivity.CUDA)
iterator = iter(loader)
model.train()
with profile(
activities=activities,
schedule=schedule(wait=1, warmup=1, active=2, repeat=1),
record_shapes=False, profile_memory=False, with_stack=False,
on_trace_ready=lambda p: p.export_chrome_trace(str(trace)),
) as prof:
for _ in range(4):
with record_function("lecture_batch_cpu"):
x, y = next(iterator)
with record_function("transfert"):
x, y = x.to(device), y.to(device)
with record_function("entrainement"):
optimizer.zero_grad(set_to_none=True)
loss = loss_fn(model(x), y)
loss.backward()
optimizer.step()
prof.step()
print(prof.key_averages().table(
sort_by="self_cpu_time_total", row_limit=8,
))Jak przekształcić obserwację w weryfikowalną hipotezę
Zacznij od aktywnego okna i sprawdź, czy pojawiają się oczekiwane etykiety. Następnie przeanalizuj przerwy między aktywnościami, kopie i powtórzenia. Długie oczekiwanie w lecture_batch_cpu wskazuje na pipeline wejściowy; nie mierzy bezpośrednio pamięci masowej. Częsta kopia skłania do zbadania rozmieszczenia tensorów, nie dowodząc jednak, że jest zbędna.
Sformułuj jedną hipotezę, a następnie zaproponuj kontrolowaną modyfikację. Na przykład, jeśli stała jest odtwarzana i transferowana przy każdym kroku, sprawdź, czy jej czas życia może obejmować kilka kroków bez zmiany wyniku. Jeśli jakiś operator wydaje się dominujący, zbadaj jego kształty i liczbę wywołań, zanim zaczniesz szukać zamiennika.
Poniższe wiersze to możliwe odczyty, a nie ustalenia wyciągnięte z wykonanego śladu. Nie ogłasza się żadnych czasów ani przyspieszeń. Użytecznym wnioskiem jest kolejny eksperyment, który może potwierdzić lub obalić proponowaną przyczynę.
Przewiń tabelę, aby zobaczyć wszystkie kolumny.| Możliwa obserwacja | Hipoteza | Następna kontrola |
|---|---|---|
| GPU bez aktywności podczas odczytu | Pipeline wejściowy nie nadąża | To samo obliczenie z już przygotowanym batch'em |
| Powtarzające się kopie stałej | Nieodpowiednie rozmieszczenie lub czas życia | Przenieść raz, sprawdzić wyniki |
| Wiele małych uruchomień | Po-fragmentowane zadanie | Zbadać grupowanie i koszt całkowity |
| Jeden długi operator | Decydujący kształt lub algorytm | Porównać ten sam operator i jego dane wejściowe |
Ograniczyć koszt i ilość informacji z instrumentacji
Zacznij od krótkiego zbierania danych i niewielu opcji. Włączaj kształty, stosy lub pamięć tylko wtedy, gdy wymaga tego pytanie. API PyTorch precyzuje, że te informacje zwiększają koszt; zbieranie kształtów może nawet przechowywać referencje do tensorów. Szczegółowy profil może więc zmienić czasy lub zajętość pamięci programu.
Ślad może zawierać nazwy operatorów, kształty, a w zależności od opcji również ścieżki kodu. Sprawdź go przed udostępnieniem. Etykieta strefy powinna opisywać krok bez zawierania adresu e-mail, tokenu, prywatnej ścieżki ani zawartości wejściowej. Wybierz czytnik śladu dopasowany do swojego środowiska i utrzymuj zbieranie danych pod własną kontrolą.
Jeśli oczekiwane zdarzenia GPU są nieobecne, nie wypełniaj ich czasów zerem. Wskaż, że nie zostały zaobserwowane, i sprawdź obsługę zbierania danych. Brak zdarzenia w narzędziu nie jest dowodem braku obliczeń.
Walidacja optymalizacji poza profilerem
Wyjdź od tego samego istotnego stanu i porównaj poprawność przed i po modyfikacji. W przypadku uczenia dodatkowy krok zmienia wagi; dwa kolejne przechwycenia niekoniecznie stanowią równoważne porównanie. Zachowaj kod, parametry i punkt startowy, aby móc wyjaśnić różnicę.
Następnie zmierz scenariusz bez szczegółowego zbierania danych, z tym samym rozgrzewaniem i wieloma przebiegami. Podaj zakres, surowe wartości i ich rozrzut. Lokalna poprawa może zniknąć w całej pętli lub pogorszyć jakość: oba te czynniki muszą pozostać częścią decyzji.
Na koniec wykorzystaj obserwacje do doprecyzowania faktycznie potrzebnych zasobów. Ślad z twojej stacji nie szereguje ofert Kernodeck i nie dowodzi ani procesora hosta, ani sieci wynajmowanego serwera. Porównanie GPU wymaga porównywalnego obciążenia, warunków i wyników na danych zasobach.