GPU pour vos projets · paiement crypto sans KYC Comment louer
Français
Ouvrir la console
Guide pratique / KERNODECK

Profiler une étape PyTorch pour savoir où le temps se passe

Définissez d’abord l’étape à observer, séparez chargement, transfert, calcul et mise à jour, puis capturez une courte fenêtre représentative avec torch.profiler. Lisez ensemble la chronologie CPU et les activités GPU disponibles. Un temps de lancement Python n’est pas le temps d’exécution terminé sur l’accélérateur, et une trace instrumentée ne constitue pas à elle seule un benchmark.

7 min de lecture · Guide pour développeurs

Choisir une question avant d’ouvrir une trace

Une trace répond mieux à une question précise : le calcul attend-il les données, une copie est-elle répétée, ou un petit opérateur est-il appelé trop souvent ? Définissez l’entrée, la sortie et les frontières de votre étape. Pour l’entraînement, précisez si elle contient backward, mise à jour de l’optimiseur et lecture du batch. Pour l’inférence, séparez chargement du modèle et requête.

Gardez un scénario court dont vous connaissez la correction. Notez formes, batch, précision, mode du modèle, compilation éventuelle et source des données. Une entrée simplifiée peut aider à isoler un comportement, mais elle ne représente plus nécessairement la charge complète. Mentionnez cette différence dans le relevé.

Fixez l’unité finale : millisecondes par étape ou exemples traités par seconde. Les sommes d’événements ne remplacent pas le temps écoulé. Comparez des fenêtres ayant les mêmes frontières de lecture et transfert.

Distinguer temps CPU, travail GPU et attente

Le CPU prépare et lance des opérations ; le GPU peut les exécuter plus tard. Un intervalle Python peut donc contenir du lancement, de l’attente ou les deux. La documentation CUDA de PyTorch indique que les mesures précises doivent tenir compte de cet asynchronisme, notamment par synchronisation ou événements adaptés au périmètre.

Pour une mesure de durée GPU ciblée, les événements CUDA peuvent convenir. Pour une latence de bout en bout, attendez la fin du travail compris dans cette latence et mesurez l’ensemble de la demande. Ne mélangez pas ces deux unités. Une synchronisation ajoutée entre chaque opération peut supprimer un chevauchement réel et transformer le programme que vous cherchez à comprendre.

Dans le profileur, les temps propres d’un opérateur et ses temps incluant des sous-opérations répondent aussi à des questions différentes. Regardez la chronologie avant d’additionner les lignes du tableau. Des activités simultanées ou imbriquées ne représentent pas des portions de temps écoulé disjointes.

Réserver une phase de démarrage et une fenêtre active

Le premier passage peut inclure initialisation, chargement ou compilation. Décidez si votre question porte sur ce démarrage ou sur une phase déjà stabilisée. Conservez les deux observations lorsqu’elles comptent pour l’usage, au lieu d’effacer le coût initial d’une moyenne présentée comme globale.

La fonction schedule permet de distinguer attente, préparation de la collecte et fenêtre active. L’échauffement du profileur n’est pas une preuve que votre modèle a atteint son régime stable. Vérifiez aussi les formes rencontrées et l’état des caches. Une application à entrées variables peut rencontrer de nouveaux chemins après plusieurs itérations.

Le planning pédagogique proposé plus bas attend une étape, en prépare une et en capture deux. Quatre étapes suffisent à expliquer le mécanisme, pas à établir une distribution de performance. Dans une vraie campagne, choisissez une fenêtre justifiée et répétez la mesure hors profileur après l’analyse.

Exemple proposé : quatre étapes avec des frontières lisibles

Le fragment suivant n’a pas été exécuté. Il suppose que model, optimizer, loss_fn, loader et device existent, que le modèle est sur device et que le chargeur fournit au moins quatre batches de paires x, y. Il effectue des mises à jour : utilisez un état expérimental prévu pour cela, pas une session dont vous devez préserver les poids.

Les libellés séparent lecture CPU, transfert et entraînement. L’itérateur est créé avant la fenêtre, ce qui exclut une partie de son démarrage du périmètre. Le fichier est choisi neuf pour éviter d’écraser une trace. L’exemple refuse une collecte GPU indisponible au lieu de présenter discrètement une trace CPU comme une analyse GPU.

Le signal step() avance le planning après chaque étape. La recette officielle décrit ce lien entre itérations et collecte. Sur une pile ROCm, le périphérique PyTorch reste nommé cuda ; la disponibilité de la collecte accélérateur dépend néanmoins du build et de ses outils. Vérifiez les activités réellement présentes dans le résultat.

Capture pédagogique d’une courte fenêtre, non exécutée
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,
))

Transformer une observation en hypothèse vérifiable

Commencez par la fenêtre active et vérifiez que les libellés attendus apparaissent. Examinez ensuite les espaces entre activités, les copies et les répétitions. Une longue attente dans lecture_batch_cpu oriente vers le pipeline d’entrée ; elle ne mesure pas directement le stockage. Une copie fréquente invite à examiner le placement des tenseurs, sans prouver qu’elle est inutile.

Formulez une seule hypothèse puis proposez une modification contrôlée. Par exemple, si une constante est reconstruite et transférée à chaque étape, vérifiez si sa durée de vie peut couvrir plusieurs étapes sans changer le résultat. Si un opérateur semble dominant, inspectez ses formes et le nombre d’appels avant d’en chercher un remplacement.

Les lignes ci-dessous sont des lectures possibles, pas des constats tirés d’une trace exécutée. Aucune durée ni accélération n’est annoncée. La conclusion utile est une expérience suivante qui peut confirmer ou réfuter la cause proposée.

Faites défiler le tableau pour lire toutes les colonnes.
Interprétations pédagogiques à vérifier dans votre propre trace
Observation possibleHypothèseContrôle suivant
GPU sans activité pendant la lectureLe pipeline d’entrée ne suit pasMême calcul avec batch déjà préparé
Copies récurrentes d’une constantePlacement ou durée de vie inadaptésDéplacer une fois, vérifier les sorties
Nombreux petits lancementsTravail fragmentéExaminer regroupement et coût global
Un opérateur longForme ou algorithme déterminantComparer le même opérateur et ses entrées

Limiter le coût et les informations de l’instrumentation

Commencez avec une collecte courte et peu d’options. Activez les formes, les piles ou la mémoire seulement si une question le demande. L’API PyTorch précise que ces informations ajoutent un coût ; la collecte des formes peut même retenir des références aux tenseurs. Un profil détaillé peut donc changer les durées ou l’occupation mémoire du programme.

Une trace peut contenir des noms d’opérateurs, des formes et, selon les options, des chemins de code. Inspectez-la avant de la partager. Un libellé de zone doit décrire l’étape sans embarquer d’email, de token, de chemin privé ou de contenu d’entrée. Choisissez un lecteur de trace adapté à votre environnement et gardez la collecte sous votre contrôle.

Si les événements GPU attendus sont absents, ne remplissez pas leurs temps avec zéro. Indiquez qu’ils n’ont pas été observés et examinez le support de la collecte. Une absence d’événement dans l’outil n’est pas une preuve d’absence de calcul.

Valider l’optimisation hors profileur

Repartez du même état pertinent et comparez la correction avant et après modification. Pour l’entraînement, une étape supplémentaire change les poids ; deux captures lancées successivement ne constituent pas nécessairement une comparaison équivalente. Conservez code, paramètres et point de départ afin de pouvoir expliquer la différence.

Mesurez ensuite le scénario sans collecte détaillée, avec le même échauffement et plusieurs passages. Rapportez le périmètre, les valeurs brutes et leur dispersion. Une amélioration locale peut disparaître dans la boucle entière ou dégrader la qualité : les deux doivent rester dans la décision.

Utilisez enfin les observations pour préciser les ressources réellement nécessaires. Une trace de votre poste ne classe pas les offres Kernodeck et ne prouve ni le processeur hôte ni le réseau d’un serveur loué. La comparaison de GPU exige une charge, des conditions et des résultats comparables sur les ressources concernées.

Vos questions

Le plus gros temps CPU indique-t-il l’opérateur GPU le plus lent ?

Non. Il peut inclure lancement, traitement côté hôte ou attente. Regardez les événements GPU et leur chronologie lorsque la collecte les fournit. Le tableau trié par temps CPU répond seulement à cette perspective.

Dois-je additionner tous les temps CUDA du tableau ?

Pas pour obtenir automatiquement la durée totale. Des activités peuvent se chevaucher, et des événements peuvent être imbriqués. Définissez une fenêtre de temps écoulé et utilisez la trace pour expliquer son contenu.

Deux étapes actives suffisent-elles à publier une performance ?

Non. Le petit planning proposé illustre l’API. Un résultat exploitable demande une fenêtre représentative, des répétitions, un échauffement explicite et une mesure finale sans le surcoût de la collecte détaillée.

Une trace CPU peut-elle diagnostiquer ROCm ?

Elle peut éclairer le travail côté hôte, mais ne remplace pas les activités GPU. PyTorch emploie aussi le nom cuda sur ROCm ; vérifiez le backend, les activités prises en charge et celles réellement enregistrées avant d’interpréter la trace.