GPU do Twoich projektów · płatność krypto bez KYC Jak wynająć
Polski
Otwórz konsolę
Przewodnik praktyczny / KERNODECK

Profiluj krok PyTorch, aby wiedzieć, gdzie ucieka czas

Najpierw zdefiniuj krok do obserwacji, oddziel wczytywanie, transfer, obliczenia i aktualizację, a następnie uchwyć krótkie, reprezentatywne okno za pomocą torch.profiler. Czytaj razem chronologię CPU i dostępne aktywności GPU. Czas uruchomienia w Pythonie to nie czas zakończonego wykonania na akceleratorze, a instrumentowany ślad sam w sobie nie stanowi benchmarku.

6 min czytania · Przewodnik dla programistów

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.

Dydaktyczne przechwycenie krótkiego okna, niewykonane
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.
Dydaktyczne interpretacje do sprawdzenia we własnym śladzie
Możliwa obserwacjaHipotezaNastępna kontrola
GPU bez aktywności podczas odczytuPipeline wejściowy nie nadążaTo samo obliczenie z już przygotowanym batch'em
Powtarzające się kopie stałejNieodpowiednie rozmieszczenie lub czas życiaPrzenieść raz, sprawdzić wyniki
Wiele małych uruchomieńPo-fragmentowane zadanieZbadać grupowanie i koszt całkowity
Jeden długi operatorDecydujący kształt lub algorytmPoró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.

Twoje pytania

Czy największy czas CPU wskazuje najwolniejszy operator GPU?

Nie. Może obejmować uruchomienie, przetwarzanie po stronie hosta lub oczekiwanie. Patrz na zdarzenia GPU i ich chronologię, gdy zbieranie danych je udostępnia. Tabela posortowana według czasu CPU odpowiada tylko na tę perspektywę.

Czy powinienem zsumować wszystkie czasy CUDA z tabeli?

Nie, aby automatycznie uzyskać całkowity czas trwania. Aktywności mogą się nakładać, a zdarzenia mogą być zagnieżdżone. Wyznacz okno czasu, jaki upłynął, i użyj śladu, aby wyjaśnić jego zawartość.

Czy dwa aktywne kroki wystarczą, aby opublikować wynik wydajności?

Nie. Zaproponowany mały harmonogram ilustruje API. Użyteczny wynik wymaga reprezentatywnego okna, powtórzeń, jawnego rozgrzewania i końcowego pomiaru bez narzutu szczegółowego zbierania danych.

Czy ślad CPU może zdiagnozować ROCm?

Może naświetlić pracę po stronie hosta, ale nie zastępuje aktywności GPU. PyTorch używa też nazwy cuda w ROCm; sprawdź backend, obsługiwane aktywności oraz te faktycznie zarejestrowane, zanim zinterpretujesz ślad.