Activation Recompute
Stable docs: @docs/training/activation-recomputation.md Card: @skills/perf-activation-recompute/card.yaml
What It Is
Activation recompute échange du calcul GPU contre de la mémoire en supprimant les activations intermédiaires lors de la forward pass et en les recalculant lors de la backward. Megatron Bridge supporte deux granularités :
| Granularité | Ce que tu spécifies | Ce qui est recalculé | Économie mémoire | Coût calcul |
|---|---|---|---|---|
selective |
liste recompute_modules (ex. core_attn, mlp) |
sous-modules spécifiques au sein de chaque couche | modérée (dépend du module) | basse à haute |
full |
recompute_num_layers + recompute_method |
couches transformer entières (N couches) | la plus importante | la plus haute |
Note : MCore les nomme « selective » (niveau sous-module) vs « full » (niveau couche).
« Full » signifie recalculer des couches complètes, pas le modèle complet — tu choisis toujours le nombre de couches via recompute_num_layers.
Décision rapide
- Définis
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:Trued'abord — la plupart des OOMs limites sont causés par la fragmentation mémoire, pas la capacité. Cela le résout à coût zéro. Voir @skills/perf-memory-tuning/SKILL.md. - Commence avec
recompute_granularity=selective,recompute_modules=[core_attn](souvent déjà par défaut dans les recettes). - Ajoute
layernormaux modules recompute — calcul presque gratuit mais économie mémoire négligeable. Aide seulement dans les cas extrêmement limites. - Ajoute
mlpen dernier recours — économise ~3 GB mais coûte ~16% d'utilisation GPU sur les grands modèles denses (Llama3 70B). - Utilise
recompute_granularity=fullseulement si le recompute selective ne tient toujours pas.
CPU offloading (cpu_offloading=True) est une alternative qui évite entièrement le coût de recompute, mais elle est incompatible avec PP > 1.
Activation
Selective recompute (défaut pour la plupart des recettes)
cfg.model.recompute_granularity = "selective"
cfg.model.recompute_modules = ["core_attn"]
Selective recompute avec modules supplémentaires
cfg.model.recompute_granularity = "selective"
cfg.model.recompute_modules = ["core_attn", "layernorm"] # ou ["mlp"] ou ["mlp", "core_attn"]
Recompute de couche complète
cfg.model.recompute_granularity = "full"
cfg.model.recompute_method = "uniform"
cfg.model.recompute_num_layers = 4
Modules recompute disponibles
| Module | Ce qu'il recalcule | Coût calcul | Économie mémoire |
|---|---|---|---|
core_attn |
attention softmax/dropout/produit QKV | basse (Flash Attention recalcule déjà en interne) | modérée |
layernorm |
normalisation de couche | négligeable (~0%) | négligeable |
mlp |
bloc FFN complet | haute (~16% sur Llama3 70B, hidden=28672) | ~3 GB |
moe |
dispatch expert MoE | variable | variable |
moe_act |
fonctions d'activation MoE | basse | petit |
shared_experts |
couches expert partagé | modérée | modérée |
mla_up_proj |
projection up Multi-Latent Attention | modérée | modérée |
CLI harnais de performance
python scripts/performance/run_performance_workload.py \
--recompute_granularity selective \
--recompute_modules core_attn layernorm \
...
Compatibilité et contraintes
recompute_granularity=selectiverequiert une listerecompute_modulesnon-viderecompute_granularity=fullrequiertrecompute_methodetrecompute_num_layers- Le recompute au niveau couche (
recompute_granularity="full"+recompute_num_layers) est incompatible avec les CUDA graphs en scope TE. MCore appelle cela la granularité « full » — le nom fait référence au recalcul de couches transformer complètes, pas du modèle complet. Même si tu sélectionnes le nombre de couches à recalculer, MCore le traite différemment du recompute de sous-module. Tout scope en scope TE (attn,mlp,moe_router, etc.) assertera. Cela touche couramment les configs FP8 qui activent les graphes en scope TE par défaut (ex.LLAMA3_70B_SFT_CONFIG_H100_FP8_CS_V1définitcuda_graph_impl="transformer_engine",cuda_graph_scope="mlp"). Options :- utilise le recompute de sous-module (
recompute_granularity="selective"+recompute_modules) — compatible avec les graphes en scope TE - désactive les CUDA graphs (
cuda_graph_impl="none") et utilise le recompute au niveau couche - bascule vers
cuda_graph_impl="local",cuda_graph_scope="full_iteration"
- utilise le recompute de sous-module (
distribute_saved_activations=Truene peut pas être combiné avecsequence_parallel=True- Combiner le recompute
mlp+core_attnest légèrement pire quemlpseul du fait d'une double surcharge recompute
Résultats mesurés
Llama3 70B SFT sur 32x H100 80GB, FP8 (Mise à l'échelle actuelle) :
- Baseline : TP=4, PP=4, VPP=5, DP=2, MBS=1, GBS=32, seq_len=4096
- Utilisation GPU dorée : 709,93 TFLOP/s/GPU
- Seuil de régression : 5%
| Expérience | recompute_modules | TFLOP/s/GPU | vs Golden | Mem pic (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 |
Éléments clés :
- le recompute
layernormest presque gratuit en calcul mais économise une mémoire négligeable - le recompute
mlpéconomise ~3 GB pic mais coûte ~16% parce que la FFN Llama3 70B (hidden=28672) est chère à recalculer - combiner
mlp+core_attnest légèrement pire quemlpseul - pour cette charge de travail, le vrai fix OOM était
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True(fragmentation mémoire, pas capacité). Voir @skills/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 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
Valeurs par défaut recette Llama3
# Memory saving (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é CPU offloading PP (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 des défaillances
| Symptôme | Cause | Confirmer | Corriger |
|---|---|---|---|
| Baisse >15% utilisation GPU | recompute mlp sur grande FFN | vérifier que recompute_modules inclut mlp |
vérifier que expandable_segments:True est défini ; envisager réduire MBS |
| Toujours OOM après ajout layernorm | les activations layernorm sont trop petites | comparer mémoire pic avant/après | ajouter recompute mlp ou vérifier 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 graphes en scope TE. Les configs FP8 CS défaultent à cuda_graph_impl=transformer_engine, scope=mlp. |
vérifier cuda_graph_impl et cuda_graph_scope |
utiliser recompute de sous-module (selective + recompute_modules), ou cuda_graph_impl=none, ou local + full_iteration |
| ValueError: PP + CPU offloading | cpu_offloading=True avec pipeline_model_parallel_size > 1 |
vérifier config PP | désactiver CPU offloading ou définir PP=1 |
| mlp+core_attn pire que mlp seul | double surcharge recompute | comparer Exp 1 vs Exp 2 | utiliser mlp seul |
Limitations connues
- L'économie mémoire par module varie significativement selon l'architecture du modèle et la dimension cachée
- Aucune sélection module automatique — les utilisateurs doivent choisir quels modules recalculer
- le recompute
layernormn'en vaut presque jamais la peine comme fix standalone - le CPU offloading (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 :
- Les tests unitaires passent pour la validation config recompute
- Aucune erreur assertion de validation config