nemo-mbridge-perf-activation-recompute

Par nvidia · skills

Validez et utilisez la recomputation d'activation sélective et complète dans Megatron Bridge pour réduire l'utilisation de la mémoire GPU au prix d'un calcul supplémentaire.

npx skills add https://github.com/nvidia/skills --skill nemo-mbridge-perf-activation-recompute

Recompute des activations

Docs stables : @docs/training/activation-recomputation.md Fiche : @skills/nemo-mbridge-perf-activation-recompute/card.yaml

Ce que c'est

Le recompute des activations échange du calcul GPU pour de la mémoire en supprimant les activations intermédiaires lors de la passe forward et en les recalculant lors de la backward. Megatron Bridge supporte deux granularités :

Granularité Ce que vous spécifiez Ce qui est recalculé Économies mémoire Coût calcul
selective liste recompute_modules (ex. core_attn, mlp) sous-modules spécifiques dans chaque couche modérée (dépend du module) faible à élevé
full recompute_num_layers + recompute_method couches transformer entières (N couches) la plus importante la plus élevée

Note : MCore nomme cela « selective » (niveau sous-module) vs « full » (niveau couche). « Full » signifie recalculer des couches complètes, pas le modèle entier — vous choisissez toujours combien de couches via recompute_num_layers.

Décision rapide

  1. Définissez PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True d'abord — la plupart des OOM limites sont causés par la fragmentation mémoire, pas la capacité. Cela le règle sans coût. Voir @skills/nemo-mbridge-perf-memory-tuning/SKILL.md.
  2. Commencez avec recompute_granularity=selective, recompute_modules=[core_attn] (souvent déjà par défaut dans les recettes).
  3. Ajoutez layernorm aux modules recompute — calcul presque gratuit mais économies mémoire négligeables. N'aide que dans les cas extrêmement limites.
  4. Ajoutez mlp en dernier recours — économise ~3 GB mais coûte ~16% d'utilisation GPU sur les grands modèles denses (Llama3 70B).
  5. Utilisez recompute_granularity=full seulement quand le recompute selective ne rentre toujours pas.

Le offloading CPU (cpu_offloading=True) est une alternative qui évite entièrement le coût recompute, mais il est incompatible avec PP > 1.

Activation

Recompute selective (défaut pour la plupart des recettes)

cfg.model.recompute_granularity = "selective"
cfg.model.recompute_modules = ["core_attn"]

Recompute selective avec modules supplémentaires

cfg.model.recompute_granularity = "selective"
cfg.model.recompute_modules = ["core_attn", "layernorm"]  # ou ["mlp"] ou ["mlp", "core_attn"]

Recompute par couche complète

cfg.model.recompute_granularity = "full"
cfg.model.recompute_method = "uniform"
cfg.model.recompute_num_layers = 4

recompute_modules disponibles

Module Ce qu'il recalcule Coût calcul Économies mémoire
core_attn softmax attention/dropout/produit dot QKV faible (Flash Attention recalcule déjà en interne) modérée
layernorm normalisation de couche négligeable (~0%) négligeable
mlp bloc FFN complet élevé (~16% sur Llama3 70B, hidden=28672) ~3 GB
moe dispatch expert MoE varie varie
moe_act fonctions activation MoE faible petite
shared_experts couches expert partagées modérée modérée
mla_up_proj projection up Multi-Latent Attention modérée modérée

CLI du performance harness

python scripts/performance/run_performance_workload.py \
  --recompute_granularity selective \
  --recompute_modules core_attn layernorm \
  ...

Compatibilité et contraintes

  • recompute_granularity=selective nécessite une liste recompute_modules non vide
  • recompute_granularity=full nécessite recompute_method et recompute_num_layers
  • Le recompute au niveau couche (recompute_granularity="full" + recompute_num_layers) est incompatible avec les CUDA graphs scopés TE. MCore appelle cela la granularité « full » — le nom fait référence au recalcul de couches transformer complètes, pas du modèle entier. Même si vous sélectionnez combien de couches recalculer, MCore le traite différemment du recompute sous-module. Tout scope scopé TE (attn, mlp, moe_router, etc.) génèrera une assertion. Cela frappe couramment les configs FP8 qui activent les graphs scopés TE par défaut (ex. LLAMA3_70B_SFT_CONFIG_H100_FP8_CS_V1 définit cuda_graph_impl="transformer_engine", cuda_graph_scope="mlp"). Options :
    • utiliser le recompute sous-module (recompute_granularity="selective" + recompute_modules) — compatible avec les graphs scopés TE
    • désactiver les CUDA graphs (cuda_graph_impl="none") et utiliser le recompute au niveau couche
    • passer à cuda_graph_impl="local", cuda_graph_scope="full_iteration"
  • distribute_saved_activations=True ne peut pas être combiné avec sequence_parallel=True
  • Combiner mlp + recompute core_attn est légèrement pire que mlp seul en raison du surcoût double recompute

Résultats mesurés

Llama3 70B SFT sur 32x H100 80GB, FP8 (Scaling actuel) :

  • Baseline : TP=4, PP=4, VPP=5, DP=2, MBS=1, GBS=32, seq_len=4096
  • Utilisation GPU golden : 709,93 TFLOP/s/GPU
  • Seuil régression : 5%
Expérience recompute_modules TFLOP/s/GPU vs Golden Pic Mém (GB) Résultat
Baseline [core_attn] ~704 -0,8% 58,8 (OOM rank0) OOM
Exp 1 [mlp] 593,6 -16,4% 55,6 Régression perf
Exp 2 [mlp, core_attn] 586,8 -17,3% 55,6 Régression perf
Exp 3 [core_attn, layernorm] ~702 -1,1% 59,6 (OOM rank0) OOM

Points clés :

  • Le recompute layernorm est presque gratuit en calcul mais économise mémoire négligeable
  • Le recompute mlp économise ~3 GB pic mais coûte ~16% parce que le FFN Llama3 70B (hidden=28672) est coûteux à recalculer
  • Combiner mlp + core_attn est légèrement pire que mlp seul
  • Pour cette charge, le vrai correctif OOM était PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True (fragmentation mémoire, pas capacité). Voir @skills/nemo-mbridge-perf-memory-tuning/SKILL.md.

Ancres de code

Enum modules recompute et logique checkpoint selective

# 3rdparty/Megatron-LM/megatron/core/transformer/transformer_block.py
# _checkpointed_forward() applique le recompute selective basé sur recompute_modules

Validation config recompute

# 3rdparty/Megatron-LM/megatron/core/transformer/transformer_config.py
# Valide recompute_granularity, recompute_method, recompute_num_layers

Défauts recette Llama3

    # Économie mémoire (recompute & offloading)
    cfg.model.recompute_granularity = None
    cfg.model.recompute_modules = None
    cfg.model.fine_grained_activation_offloading = False
    cfg.model.offload_modules = None

Recompute full + assertion CUDA graph (MCore)

            if self.recompute_granularity:
                if self.recompute_granularity != "selective":
                    assert self.cuda_graph_scope == [
                        CudaGraphScope.full_iteration
                    ], "full recompute is only supported with full iteration CUDA graph."

Incompatibilité PP offloading CPU (MCore)

        if self.cpu_offloading and self.pipeline_model_parallel_size > 1:
            raise ValueError(
                "Currently there is no support for Pipeline parallelism with CPU offloading"
            )

Diagnostic de défaillance

Symptôme Cause Confirmer Correctif
Chute >15% utilisation GPU recompute mlp sur grand FFN vérifiez que recompute_modules inclut mlp vérifiez que expandable_segments:True est défini ; envisagez réduire MBS
Toujours OOM après ajout layernorm activations layernorm trop petites comparez pic mémoire avant/après ajoutez recompute mlp ou vérifiez expandable_segments:True
AssertionError: full recompute is only supported with full iteration CUDA graph recompute au niveau couche (recompute_granularity=full + recompute_num_layers) avec graphs scopés TE. Configs FP8 CS défaut à cuda_graph_impl=transformer_engine, scope=mlp. vérifiez cuda_graph_impl et cuda_graph_scope utilisez recompute sous-module (selective + recompute_modules), ou cuda_graph_impl=none, ou local + full_iteration
ValueError: PP + offloading CPU cpu_offloading=True avec pipeline_model_parallel_size > 1 vérifiez config PP désactivez offloading CPU ou définissez PP=1
mlp+core_attn pire que mlp seul surcoût double recompute comparez Exp 1 vs Exp 2 utilisez mlp seul

Limitations connues

  • Les économies mémoire par module varient significativement selon l'architecture modèle et la dimension cachée
  • Aucune sélection module automatique — les utilisateurs doivent choisir quels modules recalculer
  • Le recompute layernorm ne vaut presque jamais le coup en tant que correctif autonome
  • Le offloading CPU (l'alternative à coût zéro calcul) est bloqué quand PP > 1

Vérification

uv run python -m pytest \
  tests/unit_tests/training/test_config.py -k "recompute" -q

Critères de succès :

  • Tests unitaires passent pour validation config recompute
  • Aucune erreur assertion de validation config

Skills similaires