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
- Définissez
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:Trued'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. - Commencez avec
recompute_granularity=selective,recompute_modules=[core_attn](souvent déjà par défaut dans les recettes). - Ajoutez
layernormaux modules recompute — calcul presque gratuit mais économies mémoire négligeables. N'aide que dans les cas extrêmement limites. - Ajoutez
mlpen dernier recours — économise ~3 GB mais coûte ~16% d'utilisation GPU sur les grands modèles denses (Llama3 70B). - Utilisez
recompute_granularity=fullseulement 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=selectivenécessite une listerecompute_modulesnon viderecompute_granularity=fullnécessiterecompute_methodetrecompute_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_V1définitcuda_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"
- utiliser le recompute sous-module (
distribute_saved_activations=Truene peut pas être combiné avecsequence_parallel=True- Combiner
mlp+ recomputecore_attnest légèrement pire quemlpseul 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
layernormest 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_attnest légèrement pire quemlpseul - 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
layernormne 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