pt2-bug-basher

Par pytorch · pytorch

Débogage des échecs de la pile de compilation PyTorch 2, notamment les graph breaks Dynamo, les erreurs de codegen Inductor, les plantages AOTAutograd et les écarts de précision. À utiliser en cas d'erreurs `torch.compile`, d'exceptions `BackendCompilerFailed`, de problèmes de recompilation, d'échecs de kernel Triton, de problèmes de FX graph, ou lorsque l'utilisateur mentionne le débogage de PT2, Dynamo, Inductor ou de modèles compilés.

npx skills add https://github.com/pytorch/pytorch --skill pt2-bug-basher

PT2 Bug Basher

Déboguer les défaillances de test et les erreurs d'exécution dans la pile du compilateur PyTorch 2 (Dynamo, Inductor, AOTAutograd, graphes FX).

Résumé du workflow

  1. Vérification de l'environnement -- Demandez à l'utilisateur quel environnement conda utiliser. Vérifiez qu'il est actif en contrôlant $CONDA_DEFAULT_ENV. Ensuite, lancez python -c "import torch; print(torch.__version__)" pour confirmer que torch est importable et rapportez la version. Si l'environnement n'est pas actif ou que torch ne peut pas être importé, arrêtez-vous et demandez à l'utilisateur d'activer le bon environnement avant de continuer.
  2. Reproduire -- Obtenez une reproduction cohérente de la défaillance
  3. Minimiser -- Réduisez la repro au plus petit cas autonome possible. Éliminez la logique de modèle non pertinente, utilisez des formes de tenseur minimales et isolez l'opération ou le motif spécifique qui déclenche le bug.
  4. Ajouter un test unitaire -- Faites cela AVANT de vous plonger dans la recherche de code ou l'enquête sur la cause racine. Ajoutez un test défaillant à la base de code qui capture le bug. Placez-le dans un fichier de test spécifique et approprié au sujet (par ex. test/dynamo/test_repros.py, test/inductor/test_torchinductor.py, test/export/test_export.py). Évitez test/dynamo/test_misc.py — il est déjà surdimensionné ; trouvez un fichier de test plus spécifique qui correspond à la zone du bug. Utilisez torch.testing._internal.common_utils.TestCase et run_tests. Le test doit échouer avant la correction et réussir après. Avoir le test en premier vous garde ancrés — vous savez exactement ce que « réparé » signifie avant de commencer à explorer la base de code.
  5. Valider sur main -- Utilisez EnterWorktree pour créer un worktree extrait à main. Copiez le nouveau fichier de test dans le worktree et exécutez le test là-bas pour confirmer qu'il échoue sur main. Si le test réussit sur main, arrêtez-vous — le test peut ne pas capturer le bon bug, ou le bug peut être déjà réparé. Quittez le worktree avec ExitWorktree (action : remove) et revenez à la branche de travail avant de continuer.
  6. Collecter les logs -- Lancez avec les paramètres TORCH_LOGS appropriés
  7. Classifier -- Utilisez le tableau Triage des erreurs pour identifier la catégorie
  8. Inspecter les artefacts -- Vérifiez les graphes FX, l'IR et le code généré via TORCH_COMPILE_DEBUG=1
  9. Identifier la cause racine -- Tracez depuis l'erreur à travers le pipeline de compilation
  10. Corriger -- Appliquez la correction
  11. Vérifier -- Lancez le nouveau test unitaire ET les tests existants connexes à proximité (par ex., si vous avez modifié le fonctionnement de is_exporting, lancez aussi le test d'export test_is_exporting existant). Utilisez pytest -k pour exécuter rapidement les tests connexes par nom. La tâche n'est pas complète jusqu'à ce que tous réussissent.
  12. Auto-examen -- Utilisez le skill /pr-review pour examiner vos propres modifications avant de les présenter. Corrigez tout problème qu'il signale.
  13. Célébrer -- Résumez les modifications : expliquez la cause racine, ce qui a été modifié et pourquoi, ainsi que les tests qui ont été ajoutés/vérifiés. Ensuite, dites à l'utilisateur que le bug est écrasé. Incluez un message de motivation amusant et varié ou un easter egg pour garder le moral (par ex., un jeu de mots, une citation, un art ASCII de bug écrasé). Gardez-le court et différent à chaque fois.

Stratégie d'investigation

Préférer les outils directs à meta_codesearch

Utilisez Grep, Glob et Read directement pour l'exploration du code. N'appelez pas d'agents meta_codesearch — ils sont lents et coûteux. Les sections Connaissance architecturale et Fichiers source clés ci-dessous devraient vous donner suffisamment de contexte pour savoir où chercher. Une Grep ciblée pour un nom de fonction est toujours plus rapide.

Connaître le mode de compilation utilisé

Avant de lire le code d'implémentation, déterminez le mode de compilation. Ceux-ci partagent du code mais divergent de manière importante :

  • torch.compile -- Dynamo + Inductor. tx.export=False, pas de _compiling_state_context().
  • torch.export (strict) -- tx.export=True, _compiling_state_context() actif.
  • torch.export (non-strict, le défaut) -- Utilise Dynamo via fullgraph_capture mais tx.export peut différer du strict. _compiling_state_context() actif. Vérifiez torch._export.config.use_new_tracer_experimental — cela change le chemin de code utilisé.

Distinguer le temps de trace du temps d'exécution

Beaucoup de bugs PT2 viennent de la confusion entre ces deux :

  • Temps de trace : À l'intérieur de l'interpréteur symbolique de Dynamo. Dynamo intercepte les appels de fonction et peut les plier constamment (par ex., is_exporting()ConstantVariable(True)).
  • Temps d'exécution : Vrais tenseurs, vrais appels Python, indicateurs au niveau du module comme torch.compiler._is_exporting_flag.

Lors du débogage, ajoutez des instructions print() temporaires directement dans le fichier source plutôt que de faire un monkey-patching de l'extérieur — les chaînes de dispatch rendent le monkey-patching peu fiable.

Collecte d'informations

Choisissez le bon outil de diagnostic en fonction de la catégorie d'erreur :

  • Aperçu rapide : TORCH_LOGS="+dynamo,graph_breaks,recompiles" python your_script.py
  • Artefacts de débogage complets : TORCH_COMPILE_DEBUG=1 python your_script.py — crée torch_compile_debug/ avec graphes FX, IR Inductor et code généré
  • Code généré uniquement : TORCH_LOGS="output_code" python your_script.py
  • Traçage structuré : TORCH_TRACE=/path/to/trace python your_script.py puis tlparse /path/to/trace
  • Single-threaded (pour pdb) : TORCHINDUCTOR_COMPILE_THREADS=1 python your_script.py

Triage des erreurs

Classifiez la défaillance en utilisant le message d'erreur et la trace de pile :

Motif d'erreur Catégorie Aller à
Unsupported: ... ou graph break dans les logs Rupture de graphe Ruptures de graphe
BackendCompilerFailed Crash Inductor/backend Défaillances du compilateur backend
RecompileError ou cache_size_limit Recompilation Problèmes de recompilation
Inadéquation de précision / sortie numérique incorrecte Précision Problèmes de précision
InternalTorchDynamoError Bug Dynamo Erreurs internes
Segfault ou CUDA IMA Crash d'exécution Crashes d'exécution
Assertion Triton / index hors limites Bug noyau Triton Défaillances de noyau Triton

Débogage par catégorie

Ruptures de graphe

Les ruptures de graphe divisent le graphe compilé en sous-graphes plus petits, causant souvent des régressions de performance ou un comportement inattendu.

Diagnostic :

TORCH_LOGS="graph_breaks" python your_script.py

Fichiers clés :

  • torch/_dynamo/exc.py -- classe d'exception Unsupported
  • torch/_dynamo/variables/ -- où la plupart des décisions de rupture de graphe se produisent

Causes communes :

  • Constructions Python non supportées (flux de contrôle dépendant des données, builtins non supportés)
  • Opérations tenseur qui ne peuvent pas être tracées (ops in-place sur les entrées, dtypes non supportés)
  • Appels à des fonctions non traçables

Approche de correction :

  1. Lisez le message de rupture de graphe pour identifier l'opération non supportée
  2. Vérifiez s'il y a une décomposition ou une alternative supportée
  3. Si l'opération ne peut vraiment pas être tracée, envisagez torch._dynamo.allow_in_graph ou restructurez le code utilisateur

Défaillances du compilateur backend

BackendCompilerFailed signifie qu'Inductor (ou un autre backend) a échoué pendant la compilation.

Diagnostic :

TORCHDYNAMO_REPRO_AFTER=aot TORCHDYNAMO_REPRO_LEVEL=2 python your_script.py

Cela génère minifier_launcher.py qui isole le graphe défaillant minimal.

Fichiers clés :

  • torch/_dynamo/repro/after_aot.py -- repro/minifier pour les défaillances post-AOT
  • torch/_inductor/ -- le backend lui-même

Approche de correction :

  1. Lancez le minifier pour obtenir une reproduction minimale
  2. Inspectez le graphe FX (TORCH_COMPILE_DEBUG=1) pour comprendre les opérations impliquées
  3. Vérifiez s'il s'agit d'un problème de lowering (torch/_inductor/lowering.py), de scheduling ou de codegen
  4. Regardez le code de sortie généré si l'erreur est dans codegen

Problèmes de recompilation

Une recompilation excessive se produit quand les guards sont trop spécifiques, causant des absences en cache.

Diagnostic :

TORCH_LOGS="recompiles,recompiles_verbose,guards" python your_script.py

Config clé :

  • torch._dynamo.config.recompile_limit (défaut : 8)
  • torch._dynamo.config.fail_on_recompile_limit_hit -- mettez à True pour obtenir une erreur matérielle

Causes communes :

  • Changement de formes de tenseur sans les marquer dynamiques
  • Valeurs scalaires Python qui changent entre les appels
  • Mutations d'état global entre les appels

Approche de correction :

  1. Lisez la raison de la recompilation depuis les logs
  2. Identifiez le guard défaillant
  3. Soit marquez la dimension pertinente comme dynamique avec torch._dynamo.mark_dynamic(), soit réparez la source d'instabilité du guard

Problèmes de précision

Le modèle compilé produit des résultats numériques différents du mode eager.

Diagnostic :

TORCHDYNAMO_REPRO_AFTER=aot TORCHDYNAMO_REPRO_LEVEL=4 python your_script.py

Cela compare compilé vs. eager avec une référence fp64 et décharge une repro en cas d'échec de précision.

Utilitaires clés :

  • torch/_dynamo/debug_utils.py -- same_two_models(), backend_accuracy_fails(), cast_to_fp64()
  • torch._dynamo.config.repro_tolerance (défaut : 1e-3)

Approche de correction :

  1. Obtenez le graphe défaillant minimal du minifier
  2. Comparez la sortie eager vs. compilée en précision fp64
  3. Recherche binaire à travers les opérations pour trouver l'opération divergente
  4. Vérifiez les problèmes numériques connus (ordre de réduction, noyaux fusionnés, promotions dtype)

Erreurs internes Dynamo

InternalTorchDynamoError indique un bug dans Dynamo lui-même.

Diagnostic :

TORCHDYNAMO_VERBOSE=1 python your_script.py
# ou équivalemment :
TORCH_LOGS="+dynamo" python your_script.py

Fichiers clés :

  • torch/_dynamo/symbolic_convert.py -- interpréteur bytecode
  • torch/_dynamo/variables/ -- système de suivi des variables
  • torch/_dynamo/guards.py -- génération de guards

Approche de correction :

  1. Obtenez la trace de pile complète avec TORCHDYNAMO_VERBOSE=1
  2. Identifiez quelle instruction bytecode ou type de variable a causé l'écrasement
  3. Créez une repro minimale (le message d'erreur inclut souvent un chemin de minifier)
  4. Déboguer avec TORCHINDUCTOR_COMPILE_THREADS=1 et pdb si nécessaire

Crashes d'exécution

Segfaults et erreurs d'accès mémoire CUDA illégal lors de l'exécution du code compilé.

Diagnostic (rendre le crash déterministe) :

PYTORCH_NO_CUDA_MEMORY_CACHING=1 CUDA_LAUNCH_BLOCKING=1 python your_script.py

Pour IMA CUDA, ajoutez des vérifications NaN :

TORCHINDUCTOR_NAN_ASSERTS=1 python your_script.py

Pour le débogage de synchronisation au niveau Inductor :

torch._inductor.config.triton.debug_sync_kernel = True  # sync après chaque noyau
torch._inductor.config.triton.debug_sync_graph = True   # sync avant/après graphe

Approche de correction :

  1. Rendez le crash déterministe avec PYTORCH_NO_CUDA_MEMORY_CACHING=1 CUDA_LAUNCH_BLOCKING=1
  2. Vérifiez s'il y a une inadéquation d'entrée (formes, appareils, dtypes)
  3. Inspectez le code de noyau généré avec TORCH_LOGS="output_code"
  4. Utilisez TORCHINDUCTOR_NAN_ASSERTS=1 pour trouver le premier noyau produisant de mauvaises valeurs
  5. Vérifiez les problèmes de formes dynamiques (historiquement une source courante d'IMA)

Défaillances de noyau Triton

Assertions Triton ou index hors limites dans les noyaux générés.

Diagnostic :

TORCH_LOGS="output_code,schedule" python your_script.py

Fichiers clés :

  • torch/_inductor/codegen/triton.py -- codegen Triton
  • torch/_inductor/scheduler.py -- décisions de fusion de noyaux

Approche de correction :

  1. Obtenez le noyau Triton généré depuis les logs output_code
  2. Vérifiez les calculs d'index pour les erreurs off-by-one ou les calculs de stride incorrects
  3. Regardez l'IR (TORCH_COMPILE_DEBUG=1) pour remonter à l'opération FX
  4. Vérifiez si les décisions de fusion ont créé des combinaisons d'index invalides

Fichiers source clés

Fichier Objectif
torch/_dynamo/exc.py Hiérarchie d'exceptions et formatage des erreurs
torch/_dynamo/debug_utils.py Support du minifier, vérification de précision, sérialisation des entrées
torch/_dynamo/repro/after_dynamo.py Repro/minifier pour les défaillances à l'étape Dynamo
torch/_dynamo/repro/after_aot.py Repro/minifier pour les défaillances post-AOTAutograd
torch/_dynamo/repro/aoti.py Repro/minifier pour les défaillances AOTI
torch/_dynamo/config.py Config Dynamo (niveaux de repro, limites de recompilation)
torch/_dynamo/variables/torch.py Gestion des fonctions torch, fonctions d'état de traçage
torch/_dynamo/variables/higher_order_ops.py Traçage HOP (cond, map, etc.)
torch/_dynamo/symbolic_convert.py Interpréteur bytecode, InstructionTranslator
torch/_dynamo/convert_frame.py Compilation de frame, point d'entrée fullgraph_capture
torch/_dynamo/functional_export.py Nouveau traceur d'export (_dynamo_graph_capture_for_export)
torch/_dynamo/eval_frame.py torch._dynamo.export, optimize_assert
torch/_export/_trace.py Pipeline d'export (_export, _strict_export, _non_strict_export, _export_to_aten_ir)
torch/_export/utils.py _compiling_state_context()
torch/compiler/__init__.py is_compiling(), is_exporting(), indicateurs d'exécution
torch/_higher_order_ops/cond.py Implémentation de torch.cond et traçage proxy
torch/_higher_order_ops/utils.py reenter_make_fx pour traçage de branche HOP
torch/_inductor/config.py Config Inductor (drapeaux de débogage, paramètres de traçage)
torch/_inductor/debug.py DebugContext, visualisation de graphe, logging IR
torch/_logging/_registrations.py Tous les alias de log et artefacts enregistrés

Utiliser le minifier

Le minifier réduit un graphe défaillant à la plus petite reproduction :

# Étape 1 : Générer le lanceur du minifier
TORCHDYNAMO_REPRO_AFTER=aot TORCHDYNAMO_REPRO_LEVEL=2 python your_script.py

# Étape 2 : Lancer le minifier
python minifier_launcher.py minify

# Étape 3 : Lancer la repro minimisée
python minifier_launcher.py run

Pour les problèmes de précision, utilisez le niveau 4 :

TORCHDYNAMO_REPRO_AFTER=aot TORCHDYNAMO_REPRO_LEVEL=4 python your_script.py

Skills similaires