CUDA Graphs pour PyTorch
Les CUDA Graphs capturent une séquence d'opérations GPU une seule fois et la rejouent avec une surcharge CPU minimale. Cette compétence guide l'application des CUDA Graphs aux charges de travail d'entraînement et d'inférence PyTorch en utilisant les APIs PyTorch natives, Transformer Engine et Megatron-LM.
Quand l'utiliser
Recourez à cette compétence quand vous rencontrez :
- Déclencheurs : l'utilisateur souhaite optimiser avec CUDA Graphs, réduire la surcharge de lancement de kernel, ou accélérer les boucles d'entraînement/inférence
- Symptômes : faible utilisation GPU (<80%), nombreux petits lancements de kernel (<50 µs chacun), entraînement limité par le CPU, latence élevée de lancement de kernel visible dans les profils Nsight Systems
- Mots-clés : « CUDA graph », « torch.cuda.graph », « make_graphed_callables », « reduce-overhead », « graph capture », « graph replay », « kernel launch overhead », « CudaGraphManager », « FullCudaGraphWrapper », « full-iteration graph », « stream capture »
N'utilisez PAS cette compétence pour :
- Le tuning de performance PyTorch général non lié à la surcharge de lancement de kernel
- Le développement de kernels CUDA ou code CUDA C++ personnalisé
- L'élimination de synchronisation host-device uniquement (utilisez la compétence perf-torch-sync-free à la place)
- Le profilage Nsight Systems (utilisez la compétence perf-nsight-systems)
- La compilation de graphes TensorFlow/JAX (APIs complètement différentes)
Pré-requis
| Dépendance | Version | Notes |
|---|---|---|
| PyTorch | >= 1.10 | torch.cuda.graph() disponible |
| CUDA | >= 11.0 | APIs de mise à jour de graphes |
| GPU | NVIDIA (n'importe lequel) | Requis pour CUDA |
| Nsight Systems | n'importe quelle | Optionnel, pour le profilage |
| APEX | n'importe quelle | Optionnel, pour les optimiseurs capturables |
| Transformer Engine | >= 2.2 | Optionnel, pour les graphes conscients de FP8 |
| Megatron-LM | core >= 0.14.0 | Optionnel, pour CudaGraphManager / FullCudaGraphWrapper |
Guide de sélection d'API
Choisissez l'API en fonction de votre framework et de vos besoins de performance.
| Situation | API | Flux de travail |
|---|---|---|
| Expérience rapide, limites de graphe inconnues | torch.compile(mode="reduce-overhead") |
Flux 2 |
| Entraînement, besoin d'autograd, pas de FP8/PP | torch.cuda.make_graphed_callables() |
Flux 3 |
| N'importe quel modèle PyTorch, support FP8 ou PP | TE make_graphed_callables |
Flux 4 |
| Megatron-LM, par couche, automatique | MCore CudaGraphManager |
Flux 5 |
| Perf maximale, capture d'itération complète | MCore FullCudaGraphWrapper |
Flux 6 |
| Contrôle manuel complet, pipelines personnalisés | torch.cuda.graph() |
Flux 7 |
Diagramme de décision :
- Utilisez-vous Megatron-LM avec FP8/PP ?
- Oui, voulez performance maximale avec charge de travail statique --> Flux 6 (FullCudaGraphWrapper)
- Oui, voulez graphing automatique par couche --> Flux 5 (CudaGraphManager)
- Oui, voulez contrôle manuel sur ce qui est graphé --> Flux 4 (TE make_graphed_callables)
- Utilisez-vous Transformer Engine sans Megatron ?
- Oui, besoin de FP8 ou PP --> Flux 4 (TE make_graphed_callables)
- PyTorch générique ?
- Voulez zéro effort, OK avec graphes fragmentés --> Flux 2 (torch.compile)
- Voulez support d'autograd, boucle d'entraînement --> Flux 3 (PyTorch make_graphed_callables)
- Voulez contrôle manuel complet --> Flux 7 (torch.cuda.graph)
Stratégie : Commencez par l'API de plus haut niveau disponible pour votre framework. Passez à des APIs de bas niveau uniquement si vous avez besoin de plus de contrôle, rencontrez des limitations, ou n'atteignez pas l'amélioration de performance attendue.
Flux de travail
Flux 1 : Profiler et décider si les graphes aident
Objectif : déterminer si les CUDA Graphs bénéficieront à votre charge de travail avant d'investir des efforts.
- Profilez avec Nsight Systems :
nsys profile --cuda-graph-trace=graph python train.py - Vérifiez l'utilisation GPU -- si déjà >95%, les graphes n'aideront pas beaucoup.
- Recherchez des écarts entre les lancements de kernel (surcharge CPU) et de nombreux petits kernels (<50 µs chacun). Ce sont les cibles pour le graphing.
- Annotez les régions d'intérêt pour corréler le temps d'inactivité du GPU avec le code :
with torch.cuda.nvtx.range("forward"): output = model(input) - Estimez le bénéfice : comptez les kernels par itération. Les charges de travail avec des centaines de petits kernels et <80% d'utilisation GPU sont de bons candidats.
Résultat attendu : régions de goulot d'étranglement identifiées avec faible occupation GPU entre les kernels. Passez au flux de travail approprié du Guide de sélection d'API.
Flux 2 : torch.compile(mode="reduce-overhead")
Objectif : capture automatique de CUDA Graph sans effort manuel.
Quand l'utiliser : expérience rapide, limites de graphe inconnues, déjà en utilisant torch.compile.
Étapes :
- Décorrez l'étape d'entraînement avec
@torch.compile(mode="reduce-overhead"):@torch.compile(mode="reduce-overhead") def train_step(model, x, target, criterion): output = model(x) loss = criterion(output, target) loss.backward() return loss - Exécutez la boucle d'entraînement normalement -- les graphes sont capturés automatiquement.
- Profilez avec Nsight Systems pour voir les graphes capturés :
nsys profile --cuda-graph-trace=graph python train.py - Si vous voyez trop de petits graphes (fragmentation de graphe), vérifiez les ruptures de graphe :
.item(),print(), contrôle de flux dépendant des données. Corrigez-les ou passez à Flux 3+.
Compromis :
- Zéro effort, mais peut créer de petits graphes fragmentés.
- Contrôle limité sur ce qui est graphé.
- La fragmentation de graphe limite les gains de performance par rapport aux approches manuelles.
Flux 3 : torch.cuda.make_graphed_callables()
Objectif : entraînement avec support d'autograd. Graphes forward/backward séparés.
Quand l'utiliser : entraînement avec boucles personnalisées, non-FP8, besoin d'autograd.
Étapes :
-
Préparez des entrées d'exemple correspondant à la forme du batch d'entraînement :
sample_input = torch.randn(batch_size, seq_len, hidden_size, device="cuda") -
Créez le modèle graphé :
graphed_model = torch.cuda.make_graphed_callables( model, (sample_input,), num_warmup_iters=3 ) -
Utilisez
graphed_modelcomme remplacement clé en main dans la boucle d'entraînement :for data, target in dataloader: optimizer.zero_grad() output = graphed_model(data) loss = criterion(output, target) loss.backward() optimizer.step() -
Si vous utilisez AMP, définissez
cache_enabled=False:for data, target in dataloader: optimizer.zero_grad() with torch.amp.autocast("cuda", cache_enabled=False): output = graphed_model(data) loss = criterion(output, target) loss.backward() optimizer.step() -
Si vous utilisez DDP, construisez DDP sur un flux latéral et utilisez 11 itérations de warmup :
os.environ["TORCH_NCCL_ASYNC_ERROR_HANDLING"] = "0" s = torch.cuda.Stream() with torch.cuda.stream(s): model = DistributedDataParallel(model) torch.cuda.current_stream().wait_stream(s) graphed_model = torch.cuda.make_graphed_callables( model, (sample_input,), num_warmup_iters=11 )
Limitations :
- Pas de backward double (gradients d'ordre supérieur).
- Pas de hooks de module durant la capture.
- La structure du module est figée après le graphing (pas d'ajout/suppression de paramètres).
- La signature de l'argument doit correspondre exactement à
sample_args.
Flux 4 : TE make_graphed_callables
Objectif : graphing par callable avec support FP8 et parallélisme de pipeline.
Quand l'utiliser : entraînement FP8, PP avec planification manuelle, modèles non-Megatron nécessitant FP8, ou tout modèle PyTorch nécessitant CUDA Graphs conscients de FP8.
Étapes :
- Importez et configurez :
from transformer_engine.pytorch.graph import make_graphed_callables from transformer_engine.pytorch.fp8 import fp8_autocast - Préparez les entrées d'exemple (une par callable par microbatch par chunk) :
sample_args = tuple( (torch.randn(batch_size, seq_len, hidden_size, device="cuda"),) for _ in range(num_callables * num_microbatches) ) - Définissez le calendrier de pipeline si vous utilisez PP (IDs de chunk indexés à partir de 1, positif=fwd, négatif=bwd) :
# Exemple : 2 chunks, 3 microbatches layer_order = [1, 2, 1, 2, 1, 2, -2, -1, -2, -1, -2, -1] - Enveloppez les couches dans CUDA Graphs :
graphed_layers = make_graphed_callables( tuple(layers), sample_args=sample_args, fp8_enabled=True, fp8_recipe=fp8_recipe, fp8_weight_caching=True, _order=layer_order, # None pour pas de PP ) - Boucle d'entraînement -- enveloppez avec
fp8_autocastdurant la rejeu :with fp8_autocast(enabled=True, fp8_recipe=fp8_recipe): for layer in graphed_layers[start:end]: x = layer(x, is_first_microbatch=(mb_idx == 0)) # FP8 scaling auto-mise à jour à la sortie de fp8_autocast optimizer.step()
Points clés :
- Capture AOT : graphes capturés avant la boucle d'entraînement quand vous appelez
make_graphed_callables(). - L'ordre de rejeu doit correspondre à
_order: la boucle d'entraînement doit exécuter les graphes dans le même ordre entrelacé que celui spécifié durant la capture. fp8_autocastrequis durant la rejeu : sans lui, l'état FP8 n'est pas correctement configuré.- Mise en cache des poids :
fp8_weight_caching=Truemet en cache la quantification des poids FP8 entre microbatches ; passez l'argument kwargis_first_microbatchpour contrôler quand les poids sont requantifiés.
Pour les détails complets de l'API, consultez references/api-te-megatron.md.
Flux 5 : MCore CudaGraphManager (Par-couche)
Objectif : graphing automatique par couche pour l'entraînement Megatron-LM.
Quand l'utiliser : entraînement Megatron-LM, en particulier avec PP > 1. Choix par défaut pour les utilisateurs de Megatron.
Étapes :
- Activez via les drapeaux CLI (pas de changements de code nécessaires) :
python pretrain_gpt.py \ --enable-cuda-graph \ --cuda-graph-num-warmup-steps 3 - Ou activez via la config Python :
config = TransformerConfig( enable_cuda_graph=True, cuda_graph_num_warmup_steps=3, ) - La boucle d'entraînement inchangée -- les graphes sont capturés automatiquement après les itérations de warmup.
Points clés :
- Couches Megatron uniquement : fonctionne avec
TransformerLayeretMambaLayer. - Capture JIT : enregistre l'ordre d'exécution durant le warmup, capture les graphes après le warmup, puis rejoue sur les itérations suivantes.
- Gestion FP8 automatique : utilise
fp8_autocast(..., _graph=True)pour sauter la réduction amax par couche ; la réduction se fait une seule fois après tous les graphes backward. - Support PP automatique : gère automatiquement l'entrelacement de microbatch.
- Économies de mémoire : définissez
cuda_graph_share_io_buffers=Truepour partager les buffers I/O entre couches (nécessite pas d'opérations entre couches). - Stratégie de pool mémoire : par défaut utilise des pools séparés par microbatch pour la réutilisation de graphes. Définissez
cuda_graph_use_single_mempool=Truepour un pool partagé (nombre de graphes plus élevé mais peut réduire la fragmentation).
Flux 6 : MCore FullCudaGraphWrapper (Itération complète)
Objectif : performance maximale. Capture forward+backward pour tous les microbatches en un seul graphe.
Quand l'utiliser : priorité de performance maximale, charges de travail statiques, entraînement Megatron-LM.
Étapes :
- Activez via les drapeaux CLI :
python pretrain_gpt.py \ --enable-cuda-graph \ --cuda-graph-scope full_iteration \ --cuda-graph-warmup-steps 1 \ --te-rng-tracker \ --no-check-for-nan-in-loss-and-grad - Assurez-vous que tout le code forward+backward est capturable (pas de
.item(), pas de vérification de NaN, pas de contrôle de flux dynamique). - L'optimiseur reste en mode eager par défaut (en dehors du graphe). Peut être inclus dans le graphe pour une performance maximale.
Points clés :
- Seulement 2 graphes au total : un pour l'entraînement, un pour la validation.
--te-rng-trackerrequis : la RNG standard utilise des scalaires CPU qui ne peuvent pas être capturés ; la RNG TE utilise des tenseurs device compatibles avec les graphes.--no-check-for-nan-in-loss-and-gradobligatoire : la vérification de NaN utilise.item()qui nécessite une synchronisation CPU-GPU, interdite durant la capture.- StaticBufferLoader : pré-alloue les buffers d'entrée pour tous les microbatches durant le warmup.
- Optimiseur dans/en dehors du graphe : dans = performance maximale (tous les kernels d'optimiseur capturés). En dehors = plus flexible (peut changer l'optimiseur/LR sans recapture).
- Capture JIT : graphe capturé durant l'entraînement à l'itération
warmup_steps + 1.
Flux 7 : torch.cuda.graph() (Manuel)
Objectif : contrôle complet sur la capture et la rejeu. Pipelines personnalisés, capture d'itération complète sans Megatron.
Quand l'utiliser : besoin de contrôle fin, capture d'itération complète non-Megatron, pipelines personnalisés.
Motif d'inférence :
- Pré-allouez les tenseurs d'entrée/sortie statiques :
static_input = torch.randn(batch_size, *shape, device="cuda") - Warmup sur un flux latéral (3 itérations, 11 pour DDP) :
s = torch.cuda.Stream() with torch.cuda.stream(s): for _ in range(3): _ = model(static_input) torch.cuda.current_stream().wait_stream(s) - Capturez le graphe :
g = torch.cuda.CUDAGraph() with torch.cuda.graph(g): static_output = model(static_input) - Boucle de rejeu -- mettez à jour les entrées via
.copy_(), clonez les sorties :for data in loader: static_input.copy_(data) g.replay() result = static_output.clone()
Motif d'entraînement complet (fwd+bwd+optimiseur en un seul graphe) :
model = MyModel().cuda()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = torch.nn.CrossEntropyLoss()
static_input = torch.randn(batch_size, *shape, device="cuda")
static_target = torch.randint(0, num_classes, (batch_size,), device="cuda")
# Warmup
s = torch.cuda.Stream()
with torch.cuda.stream(s):
for _ in range(3):
optimizer.zero_grad()
with torch.amp.autocast("cuda", cache_enabled=False):
out = model(static_input)
loss = criterion(out, static_target)
loss.backward()
torch.cuda.current_stream().wait_stream(s)
# Capture
g = torch.cuda.CUDAGraph()
with torch.cuda.graph(g):
optimizer.zero_grad()
with torch.amp.autocast("cuda", cache_enabled=False):
static_output = model(static_input)
static_loss = criterion(static_output, static_target)
static_loss.backward()
# Boucle de rejeu
for data, target in loader:
static_input.copy_(data)
static_target.copy_(target)
g.replay()
optimizer.step()
Configuration DDP :
os.environ["TORCH_NCCL_ASYNC_ERROR_HANDLING"] = "0"
s = torch.cuda.Stream()
with torch.cuda.stream(s):
model = DistributedDataParallel(model)
# 11 itérations de warmup pour DDP
with torch.cuda.stream(s):
for _ in range(11):
out = model(static_input)
out.sum().backward()
torch.cuda.current_stream().wait_stream(s)
# Capture sur le même flux latéral
with torch.cuda.graph(g):
static_output = model(static_input)
Partage de pool mémoire pour plusieurs graphes :
g1 = torch.cuda.CUDAGraph()
with torch.cuda.graph(g1):
out1 = model_a(static_in_a)
# Le deuxième graphe partage le pool mémoire du premier graphe
g2 = torch.cuda.CUDAGraph()
with torch.cuda.graph(g2, pool=g1.pool()):
out2 = model_b(static_in_b)
Enregistrement RNG personnalisé :
gen = torch.cuda.default_generators[0]
g = torch.cuda.CUDAGraph()
g.register_generator_state(gen)
with torch.cuda.graph(g):
out = model(static_input) # État RNG correctement capturé
Navigation entre les flux de travail
- torch.compile donne une accélération insuffisante --> escaladez à
make_graphed_callables(Flux 3) pour des graphes plus grands et moins nombreux. - make_graphed_callables ne peut pas gérer FP8/PP --> TE
make_graphed_callables(Flux 4). - Besoin de Megatron par-couche automatique --> CudaGraphManager (Flux 5).
- Voulez performance maximale --> FullCudaGraphWrapper (Flux 6) ou capture manuelle d'itération complète (Flux 7).
- Quelque chose trop difficile à grapher --> capture partielle (graphez ce que vous pouvez, laissez le reste en mode eager).
- L'utilisateur veut la meilleure perf absolue --> passez directement à Flux 6 (Megatron) ou Flux 7 (manuel).
- Commencez petit, expandez progressivement : commencez avec un module/couche. Vérifiez la correctitude. Puis expandez à plus de couches, la passe forward complète, ajoutez backward, et finalement l'itération complète avec l'optimiseur.
Rendre le code compatible avec les graphes
Ces principes s'appliquent à tous les flux de travail. Le code dans la région capturée doit satisfaire trois contraintes.
Principe 1 : GPU uniquement
Seules les opérations GPU sont capturées. Le code côté CPU (logique Python, I/O, logging) s'exécute durant la capture mais est éliminé durant la rejeu.
Violations :
- I/O fichier :
data = torch.load("file.pt")ne rechargera pas lors de la rejeu - Prétraitement CPU :
tokens = tokenizer.encode(text)ne re-tokénisera pas - Logging :
print(f"Step {i}")n'imprimera pas durant la rejeu - RNG CPU :
random.randint(0, 10)ne régénérera pas - Comptabilité CPU :
buffer.append(tensor)ne remplira pas durant la rejeu
Fix : déplacez toutes les opérations côté CPU en dehors de la région graphée.
Principe 2 : Sans synchronisation
Pas de synchronisation CPU-GPU dans le graphe. Le CPU met le travail en queue continuellement sans attendre les résultats du GPU.
Violations :
.item()pour obtenir les valeurs scalaires.cpu()pour déplacer les tenseurs pour l'inspectiontorch.cuda.synchronize()oustream.synchronize()print(tensor)(synchronise implicitement)
Fix : invoquez la compétence perf-torch-sync-free pour la détection et l'élimination systématiques des points de synchronisation. Utilisez torch.cuda.set_sync_debug_mode("warn") pour trouver les syncs cachées.
Principe 3 : Statique
Toutes les opérations, le contrôle de flux, les adresses mémoire et les formes doivent être fixes dans toutes les rejeus.
Violations et fixes :
| Aspect dynamique | Fix |
|---|---|
if loss > threshold: |
torch.where(condition, a, b) |
input = new_tensor (l'adresse change) |
Pré-allouez + .copy_() |
| Scalaires Python (lr, température) | Tenseur GPU + .fill_() |
| Taille de batch/longueur de séquence variable | Padding ou bucketing |
| MoE / routage dynamique | Graphing partiel |
Pour les motifs détaillés, consultez references/patterns-dynamic.md.
Checklist de compatibilité
Vérifiez chaque élément avant de tenter la capture :
- [ ] Pas de
.item(),.cpu(),.numpy(),print(tensor)dans le graphe - [ ] Pas de
torch.cuda.synchronize()oustream.synchronize() - [ ] Pas de
if tensor_value:-- utiliseztorch.where()à la place - [ ] Toutes les entrées pré-allouées, mises à jour via
.copy_() - [ ] Toutes les formes fixes (utilisez padding ou bucketing pour les tailles variables)
- [ ] Scalaires Python --> tenseurs GPU avec
.fill_() - [ ] Tenseurs de sortie clonés avant la prochaine rejeu
- [ ]
cache_enabled=Falseavectorch.amp.autocast - [ ] Générateurs RNG personnalisés enregistrés avec
graph.register_generator_state() - [ ] Utilisez
graphsafe_get_state()/graphsafe_set_state()pour la RNG - [ ] Warmup complété (3 standard, 11 pour DDP)
- [ ] DDP :
TORCH_NCCL_ASYNC_ERROR_HANDLING=0, construisez sur le flux latéral - [ ] DDP : NCCL >= 2.9.6 pour capture de graphe complète
- [ ] Les libraries/extensions utilisent
torch.cuda.current_stream(), pas le flux par défaut - [ ] Pas d'allocation de mémoire pinned durant la capture (déclenche une requête d'événement cachée)
- [ ]
activation_checkpointing:preserve_rng_state=False - [ ] Les tenseurs globaux utilisés dans le graphe restent vivants (ne sont pas supprimés/réassignés)
- [ ] Pas de fonctions
torch.compiledans la capture manuelle sans warmup préalable - [ ] Le clipping de gradients utilise
clip_grad_norm_sans synchronisation (PyTorch >= 1.13)
Pour la checklist complète avec références, consultez references/patterns-compatibility.md.
Formats de sortie
Indicateurs de succès :
g.replay()se complète sans erreur- Les sorties correspondent au mode eager dans la tolérance (
torch.allclose) - Le profil Nsight Systems montre un seul lancement de graphe remplaçant de nombreux kernels
- L'utilisation GPU augmente, la latence d'entraînement/inférence diminue
Métriques clés :
| Métrique | Comment vérifier |
|---|---|
| Correctitude | torch.allclose(eager, graphed, rtol=1e-5) |
| Accélération | Comparaison du temps mur-à-mur |
| Utilisation GPU | nvidia-smi ou timeline Nsight Systems |
| Surcharge mémoire | torch.cuda.memory_summary() |
Gestion des erreurs
| Erreur | Cause | Fix |
|---|---|---|
StreamCaptureUnsupported (900) |
Opération de sync durant la capture (.item(), .cpu()) |
Déplacez la sync en dehors du graphe |
StreamCaptureInvalidated (901) |
Thread en arrière-plan (ex. pin_memory) | capture_error_mode="thread_local" |
StreamCaptureUnjoined (904) |
Le flux latéral n'a pas rejoint le flux de capture | capture_stream.wait_stream(side_stream) |
StreamCaptureImplicit (906) |
AccumulateGrad sur le flux par défaut | Warmup sur flux latéral avant capture |
| Accès mémoire illégal | Tenseur d'entrée libéré/réassigné | Gardez une référence persistante, utilisez .copy_() |
| Résultats numériques incorrects | Comportement dynamique figé à la capture | Consultez references/patterns-compatibility.md |
| OOM avec plusieurs graphes | Les pools ne peuvent pas partager la mémoire | pool=g1.pool() pour les graphes séquentiels |
| Pas d'accélération | Déjà limité par GPU ou mauvaise portée de capture | Profilez avec nsys en premier (Flux 1) |
| Corruption FP8 scaling | TE sans fp8_autocast durant la rejeu |
Enveloppez avec fp8_autocast(enabled=True) |
| PP ordre de rejeu mal apparié | Ordre d'exécution incorrect durant la rejeu | Correspondez exactement à _order / séquence de capture |
| Échec de capture FullCudaGraphWrapper | Vérification NaN ou sync activée | --no-check-for-nan-in-loss-and-grad |
| Échec RNG avec FullCudaGraphWrapper | RNG standard non capturable | --te-rng-tracker |
| Échec de capture DDP | Watchdog de gestion d'erreur asynchrone | TORCH_NCCL_ASYNC_ERROR_HANDLING=0 |
| DDP AccumulateGrad sur flux par défaut | DDP construit sur flux par défaut | Construisez DDP dans le contexte de flux latéral |
| Invalidation du cache Autocast | Tenseurs de cast cachés libérés à la sortie | cache_enabled=False |
Pour le dépannage détaillé, consultez references/troubleshooting.md.
Trouver plus d'informations
Utilisez cette hiérarchie de recherche en 3 tiers -- commencez au Tier 1 et escaladez uniquement si nécessaire.
Tier 1 : Ce fichier (SKILL.md)
Vous le lisez maintenant. Les flux de travail, la checklist de compatibilité et le tableau d'erreurs ci-dessus couvrent les tâches les plus courantes. Cherchez d'abord dans ce fichier avant d'aller plus profond.
Tier 2 : Répertoire references/
Le répertoire references/ à côté de ce fichier contient du matériel de référence distillé -- détails d'API, motifs et pages de dépannage.
Comment chercher :
- Grep votre mot-clé dans
references/-- les en-têtes sont conçus pour être grep-friendly. - Lisez seulement le fichier que grep vous pointe. Ne lisez pas chaque fichier.
Références disponibles :
references/api-pytorch.md-- APIs PyTorch CUDA Graph (torch.cuda.graph,make_graphed_callables,torch.compile reduce-overhead)references/api-te-megatron.md-- implémentations TEmake_graphed_callables, CudaGraphManager, FullCudaGraphWrapperreferences/patterns-compatibility.md-- Principes GPU-only, sync-free et static avec checklist complètereferences/patterns-dynamic.md-- Contrôle de flux dynamique, tenseurs, scalaires, formes : contournements et motifsreferences/troubleshooting.md-- Échecs de capture, erreurs numériques, problèmes mémoire, problèmes de performance
Tier 3 : Documentation originale
Si les Tiers 1-2 ne répondent pas à la question, consultez les sources originales :
- Guide NVIDIA :
https://docs.nvidia.com/dl-cuda-graph/latest/index.html - Docs PyTorch :
https://docs.pytorch.org/docs/stable/notes/cuda.html(section CUDA Graphs) - Docs TE :
https://docs.nvidia.com/deeplearning/transformer-engine/user-guide/index.html - Docs Megatron Core :
https://docs.nvidia.com/megatron-core/developer-guide/latest/index.html
Retournez au Tier 2 après et considérez si la réponse devrait être distillée dans le répertoire references pour la prochaine fois.