Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
2d10336
perf(recipe): add Qwen3.5 text H100 recipe
yaoyu-33 Jul 26, 2026
a767ee3
docs(training): record Qwen3.5 performance learnings
yaoyu-33 Jul 26, 2026
9e12bbd
docs(skills): distill Qwen3.5 performance learnings
yaoyu-33 Jul 26, 2026
6eb5b7e
Attach NVSkills validation signatures
svc-nvskills-signing Jul 26, 2026
4dde871
docs(perf): record final Qwen3.5 benchmark evidence
yaoyu-33 Jul 26, 2026
6c088fb
Attach NVSkills validation signatures
svc-nvskills-signing Jul 26, 2026
3990c04
docs(skills): record HybridEP metadata constraints
yaoyu-33 Jul 26, 2026
4e161b7
Attach NVSkills validation signatures
svc-nvskills-signing Jul 26, 2026
228e9a3
docs(performance): record Qwen3.5 H100 learnings
yaoyu-33 Jul 27, 2026
4b1d25c
docs(performance): clarify NCCL EP build gates
yaoyu-33 Jul 27, 2026
46bf7ab
docs(performance): record dispatcher pipeline result
yaoyu-33 Jul 27, 2026
32f680c
docs(performance): record optimizer overlap gate
yaoyu-33 Jul 27, 2026
956845d
docs(performance): refine optimizer overlap gate
yaoyu-33 Jul 27, 2026
190603a
docs(performance): record topology and cold-cache gates
yaoyu-33 Jul 27, 2026
3482389
docs(skills): remove hidden bootstrap comments
yaoyu-33 Jul 27, 2026
ab5f69a
docs(performance): finalize Qwen3.5 H100 results
yaoyu-33 Jul 27, 2026
3763a69
perf(qwen): pin Qwen3.5 verification config
yaoyu-33 Jul 27, 2026
ff24fbb
fix(qwen): pin Qwen3.5 benchmark revisions
yaoyu-33 Jul 27, 2026
b221ec6
docs(performance): record Qwen3.5 public probe
yaoyu-33 Jul 27, 2026
0d9d9af
docs(performance): record Qwen3.5 50-step result
yaoyu-33 Jul 27, 2026
ee32da0
perf(qwen): port measured H100 expert runtime
yaoyu-33 Jul 27, 2026
3e72913
fix(qwen): match pinned weighted SwiGLU API
yaoyu-33 Jul 27, 2026
cfda8b3
perf(qwen): use native H100 HybridEP alignment
yaoyu-33 Jul 27, 2026
ba60253
perf(qwen): use FlashQLA for H100 GDN
yaoyu-33 Jul 27, 2026
4ec3467
perf(qwen): match measured HybridEP chunking
yaoyu-33 Jul 27, 2026
9a5b1b0
fix(qwen): keep fused HybridEP chunks consistent
yaoyu-33 Jul 27, 2026
246969b
revert(qwen): keep accepted HybridEP chunks
yaoyu-33 Jul 27, 2026
d43d312
fix(qwen): pin FlashQLA perf runtime
yaoyu-33 Jul 27, 2026
6641463
docs(qwen): record reproducible H100 probe
yaoyu-33 Jul 27, 2026
44f322e
fix(qwen): support H100 runtime across MCore versions
yaoyu-33 Jul 28, 2026
b58d38f
docs(qwen): record exact H100 compatibility probe
yaoyu-33 Jul 28, 2026
218c72f
docs(qwen): clarify H100 verification boundary
yaoyu-33 Jul 28, 2026
44a5a6e
perf(qwen): fuse Qwen3.5 gated RMSNorm
yaoyu-33 Jul 28, 2026
7c131dd
docs(qwen): record Qwen3.5 perf learnings
yaoyu-33 Jul 28, 2026
7d8be9b
docs(qwen): record exact H100 follow-up evidence
yaoyu-33 Jul 28, 2026
4e81c6f
perf(qwen): pin reproducible H100 GDN stack
yaoyu-33 Jul 28, 2026
f2338b0
test(qwen): model pinned GDN fields
yaoyu-33 Jul 28, 2026
f593125
chore: merge main into Qwen3.5 H100 perf
yaoyu-33 Jul 28, 2026
e5ff331
perf(qwen): pin fused norm dependency
yaoyu-33 Jul 28, 2026
26475b8
docs(perf): record reproducible Qwen kernel stack
yaoyu-33 Jul 28, 2026
59d00e5
docs(qwen): shorten H100 performance result
yaoyu-33 Jul 28, 2026
3f4fd23
perf(qwen): retain main MCore runtime contract
yaoyu-33 Jul 28, 2026
9935ccb
refactor(qwen): rename H100 runtime patch
yaoyu-33 Jul 29, 2026
4ea8a2d
perf(qwen): use baseline GDN dependency stack
yaoyu-33 Jul 29, 2026
639b3ae
docs(qwen): record exact EOS performance result
yaoyu-33 Jul 29, 2026
9552de9
chore: merge main into Qwen3.5 H100 perf
yaoyu-33 Aug 7, 2026
30fea35
docs(skills): include Qwen3.5 references in toctree
yaoyu-33 Aug 7, 2026
b7e6659
test(qwen): cover H100 runtime contracts
yaoyu-33 Aug 7, 2026
1cde0a6
chore: merge main into Qwen3.5 H100 perf
yaoyu-33 Aug 8, 2026
fae80a2
perf(qwen): reduce Qwen3.5 H100 memory overhead
yaoyu-33 Aug 8, 2026
b9df01a
chore: merge main into Qwen3.5 H100 perf
yaoyu-33 Aug 8, 2026
5c2ea29
docs(perf): redact private Qwen3.5 run metadata
yaoyu-33 Aug 8, 2026
b3b5c23
docs(qwen): record frozen H100 candidate
yaoyu-33 Aug 8, 2026
9e03ba2
build: merge Qwen3.5 H100 runtime pin
yaoyu-33 Aug 8, 2026
feff6df
perf(recipe): freeze Qwen3.5 H100 kernel path
yaoyu-33 Aug 8, 2026
761b7d6
Merge remote-tracking branch 'origin/build/qwen35-h100-runtime-pin' i…
yaoyu-33 Aug 8, 2026
c67fb71
fix(recipe): isolate HybridEP compile cache
yaoyu-33 Aug 8, 2026
fdc1ac3
Merge remote-tracking branch 'origin/build/qwen35-h100-runtime-pin' i…
yaoyu-33 Aug 8, 2026
3cc9061
perf(recipe): freeze Qwen3.5 H100 verification
yaoyu-33 Aug 9, 2026
da4c999
perf(qwen): keep HybridEP checks off hot path
yaoyu-33 Aug 9, 2026
12e43aa
perf(qwen): preserve measured HybridEP metadata path
yaoyu-33 Aug 9, 2026
d33f5b9
Merge Qwen3.5 H100 runtime pin update
yaoyu-33 Aug 9, 2026
e2e23bc
perf(qwen): restore verified H100 path
yaoyu-33 Aug 9, 2026
70811b7
perf(qwen): pin H100 CPU threading
yaoyu-33 Aug 9, 2026
6d2c01b
feat(training): add per-node torchrun launcher
yaoyu-33 Aug 9, 2026
f4a5959
fix(training): expand torchrun rank per node
yaoyu-33 Aug 9, 2026
f9c51ae
perf(qwen): freeze H100 launch resources
yaoyu-33 Aug 9, 2026
14fe1a7
perf(qwen): freeze verified H100 card path
yaoyu-33 Aug 9, 2026
1f21e3f
Merge Qwen3.5 H100 test pin update
yaoyu-33 Aug 9, 2026
07e7bca
Merge Qwen3.5 H100 CI pin update
yaoyu-33 Aug 9, 2026
d414104
fix(recipe): declare Qwen3.5 environment inline
yaoyu-33 Aug 9, 2026
7e4a6d1
Merge Qwen3.5 H100 functional pin update
yaoyu-33 Aug 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/skills-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ skills/nemo-mbridge-perf-activation-recompute/SKILL
```{toctree}
:hidden:

skills/nemo-mbridge-perf-moe-optimization-workflow/references/qwen35-h100-campaign
skills/nemo-mbridge-perf-moe-dispatcher-selection/references/qwen35-h100-hybridep
skills/nemo-mbridge-perf-expert-parallel-overlap/references/qwen35-h100-overlap
skills/nemo-mbridge-perf-nsys-analysis/references/pitfalls
skills/nemo-mbridge-perf-nsys-analysis/references/sql-recipes
```
Expand Down
219 changes: 219 additions & 0 deletions examples/model_verification_cards/qwen3.5-35b-a3b/card.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Agent-readable model verification card.
# status: unverified | verified | unsupported | not_applicable

title: qwen35_text_35b_a3b
summary: >
Performance scope: pretrain_performance.H100 uses the canonical text-only
Qwen3.5-35B-A3B performance recipe. Functional conversion, inference, and
training coverage remains unverified and is not implied by the performance
benchmark.
verification_index:
model_level:
unverified:
- hf_to_megatron_cpu
- hf_to_megatron_gpu
- megatron_to_hf_cpu
- megatron_to_hf_gpu
- manual_forward_pass
- inference
training:
H100:
unverified: [pretrain, sft, sft_export_inference, sft_long_context, peft, checkpoint_resume]
performance:
H100: verified
model:
hf_id: Qwen/Qwen3.5-35B-A3B
hf_revision: 59d61f3ce65a6d9863b86d2e96597125219dc754 # pragma: allowlist secret
architecture: Qwen3_5MoeForConditionalGeneration
min_transformers_version: "5.8.1"
verification_environment:
base_container: nvcr.io/nvidia/nemo:26.06
bridge_commit: fdc1ac339303330c1a73eaa3d1d9d4bab70c776b # pragma: allowlist secret

items:
hf_to_megatron_cpu:
status: unverified
precision: bf16
command: null
last_verified: null
expected_result: >
A pinned CPU import must exit successfully, persist iter_0000000, and
reload the complete text-only tensor set without missing, unexpected,
shape-mismatched, dtype-mismatched, or value-mismatched tensors.

hf_to_megatron_gpu:
status: unverified
precision: bf16
command: null
last_verified: null
expected_result: >
A pinned distributed GPU import must exit successfully, persist
iter_0000000, and reload the complete text-only tensor set without
missing, unexpected, shape-mismatched, dtype-mismatched, or
value-mismatched tensors.

megatron_to_hf_cpu:
status: unverified
precision: bf16
command: null
last_verified: null
expected_result: >
A pinned CPU export must exit successfully and strictly reload as the
supported text-only Hugging Face model with the expected keys, shapes,
dtypes, and values.

megatron_to_hf_gpu:
status: unverified
precision: bf16
command: null
last_verified: null
expected_result: >
A pinned distributed GPU export must exit successfully and strictly
reload as the supported text-only Hugging Face model with the expected
keys, shapes, dtypes, and values.

manual_forward_pass:
status: unverified
precision: bf16
command: null
last_verified: null
expected_result: >
Verification requires a matching next-token prediction and cosine
similarity of at least 0.99 against the pinned Hugging Face revision,
with maximum and mean absolute logit differences recorded as diagnostics.

inference:
status: unverified
precision: bf16
command: null
last_verified: null
expected_result: >
A pinned Megatron checkpoint must complete deterministic greedy
generation for an exact token count and record the literal completion.

pretrain:
H100:
status: unverified
precision: bf16
enabled_features: {}
command: null
last_verified: null
metrics:
initial_loss: null
final_loss: null
last_10_steps_step_time_ms_avg: null
last_10_steps_model_tflops_per_gpu_avg: null
expected_result: >
A public H100 pretraining recipe must complete a bounded 100-step run
with finite loss, no skipped or NaN iterations, all four metrics, a
persisted post-setup configuration, and reloadable checkpoints.

sft:
H100:
status: unverified
precision: bf16
enabled_features: {}
command: null
last_verified: null
metrics:
initial_loss: null
final_loss: null
last_10_steps_step_time_ms_avg: null
last_10_steps_model_tflops_per_gpu_avg: null
expected_result: >
A public text-only H100 recipe must complete a pinned-data 100-step
full-SFT run with finite loss, no skipped or NaN iterations, all four
metrics, and a reloadable final checkpoint.

sft_export_inference:
H100:
status: unverified
precision: bf16
depends_on: sft
commands: null
last_verified: null
expected_result: >
The final full-SFT checkpoint must export to Hugging Face, reload
strictly, and complete deterministic greedy inference for an exact
token count.

sft_long_context:
H100:
status: unverified
precision: bf16
enabled_features: {}
command: null
last_verified: null
metrics:
initial_loss: null
final_loss: null
last_10_steps_step_time_ms_avg: null
last_10_steps_model_tflops_per_gpu_avg: null
expected_result: >
A public text-only H100 recipe must complete bounded long-context SFT
with finite loss, no skipped or NaN iterations, all four metrics, and
verified packing and context-parallel behavior.

peft:
H100:
status: unverified
precision: bf16
enabled_features: {}
command: null
last_verified: null
metrics:
initial_loss: null
final_loss: null
last_10_steps_step_time_ms_avg: null
last_10_steps_model_tflops_per_gpu_avg: null
expected_result: >
A public text-only H100 recipe must complete a pinned-data 100-step
PEFT run with finite loss, no skipped or NaN iterations, all four
metrics, and a reloadable final adapter checkpoint.

checkpoint_resume:
H100:
status: unverified
precision: bf16
depends_on: pretrain
command: null
last_verified: null
metrics:
initial_loss: null
final_loss: null
last_10_steps_step_time_ms_avg: null
last_10_steps_model_tflops_per_gpu_avg: null
resume_comparison:
reference_item: pretrain
sentinel_steps: [51, 100]
loss_relative_tolerance: 1.0e-2
loss_absolute_tolerance: 1.0e-6
sentinels_match: null
expected_result: >
A direct continuation must restore optimizer, scheduler, data-order,
and RNG state from the pretraining midpoint, complete the remaining
steps, and pass the declared loss-sentinel tolerance.

pretrain_performance:
H100:
status: verified
precision: bf16
command: >
./scripts/training/train.sh
--nodes 2 --gpus-per-node 8
--recipe qwen35_text_35b_a3b_pretrain_16gpu_h100_bf16_config
--mode pretrain --max_steps 50
logger.save_config_filepath=work/model-verification/qwen3.5-35b-a3b/h100-performance/resolved-config.yaml
last_verified: 2026-08-08
metrics:
initial_loss: 12.830670
final_loss: 0.008044560
last_10_steps_step_time_ms_avg: 20966.100
last_10_steps_model_tflops_per_gpu_avg: 280.970
expected_result: >
The exact command must complete all 50 steps with finite loss,
skipped=0, NaN=0, exit 0:0, and a persisted post-setup configuration.
The verified run averaged 20966.100 ms and 280.970 model TFLOP/s/GPU
over steps 41-50, passing the 280.000 model TFLOP/s/GPU gate. Mock
data and forced expert balancing make this performance-stability
evidence, not convergence evidence.
23 changes: 23 additions & 0 deletions skills/nemo-mbridge-multi-node-slurm/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,29 @@ uv run python -m torch.distributed.run --nproc_per_node=8 \
| `No space left on device` during `uv` or `pip` | Container's `/root/.cache/` is full | Redirect: `export UV_CACHE_DIR=<SHARED_FS>/uv_cache` |
| `ModuleNotFoundError: No module named 'megatron.core.activations'` | Container's pre-installed megatron-core conflicts with local `3rdparty/Megatron-LM` | Install local: `pip install -e 3rdparty/Megatron-LM --no-deps --no-build-isolation` |

### QoS fragmentation workaround for diagnostic A/B runs

If an exact 2-node interactive request remains pending under
`QOSGrpNodeLimit` while lower-priority 1-node jobs repeatedly consume newly
free nodes, a two-element Slurm array can remove that fragmentation for a
diagnostic run:

- request one exclusive node per array task with `#SBATCH --array=0-1`
- let task 0 atomically publish its hostname to a unique file on shared storage
- make both tasks wait for that file, then launch one
`torch.distributed.run` agent per node with `node_rank=$SLURM_ARRAY_TASK_ID`
- give the first task a bounded rendezvous timeout long enough for the second
task to start
- validate both array-task exit codes and both distributed-step exit codes

This intentionally lets task 0 hold one node while task 1 waits for the next
free node. Use it only for controlled diagnostics: partial array starts consume
allocation time, a missing partner must fail closed, and the result does not
replace a required final run in one atomic multi-node allocation. A matched
Qwen3.5 16-H100 A/B on 2026-07-27 used this pattern after an atomic 2-node
request was repeatedly fragmented; both array tasks and both distributed steps
completed `0:0`.

---

## Debugging Multi-Node Failures
Expand Down
62 changes: 58 additions & 4 deletions skills/nemo-mbridge-perf-activation-recompute/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
---
name: nemo-mbridge-perf-activation-recompute
description: Validate and use selective and full activation recompute in Megatron Bridge to reduce GPU memory usage at the cost of extra compute.
description: Validate and use selective and full activation recompute in Megatron Bridge to reduce GPU memory usage at the cost of extra compute. Use for GPU-memory reduction, recompute-related OOM or regression investigation, recompute_granularity, recompute_num_layers, recompute_modules, recompute_method, selective recompute, full recompute, or activation-memory OOM.
license: Apache-2.0
when_to_use: Reducing GPU memory via activation recompute, or investigating a commit that changed recompute settings and caused OOM or a regression; 'recompute_granularity', 'recompute_num_layers', 'recompute_modules', 'recompute_method', 'selective recompute', 'full recompute', 'activation memory OOM'.
---

# Activation Recompute

Stable docs: @docs/training/activation-recomputation.md
Card: @skills/nemo-mbridge-perf-activation-recompute/card.yaml

<!-- NVSkills CI refresh: 2026-06-15. No instruction changes. -->

## What It Is

Activation recompute trades GPU compute for memory by discarding intermediate
Expand All @@ -34,6 +31,10 @@ how many layers via `recompute_num_layers`.
@skills/nemo-mbridge-perf-memory-tuning/SKILL.md.
2. For activation pressure, start with selective recompute:
`recompute_granularity="selective"` and `recompute_modules=["core_attn"]`.
For a Gated Delta Net model, use `recompute_modules=["gdn"]` as a focused
alternative when GDN activations, rather than attention activations, own the
peak. Benchmark the exact model because this checkpoints the complete GDN
module, not only its recurrent kernel.
3. Add modules by cost: `"layernorm"` is cheap but saves little, while `"mlp"`
saves much more memory at a clear throughput cost.
4. Use full-layer recompute only when selective recompute does not fit, and set
Expand Down Expand Up @@ -74,6 +75,7 @@ cfg.model.recompute_num_layers = 4
| `moe_act` | MoE activation functions | low | small |
| `shared_experts` | shared expert layers | moderate | moderate |
| `mla_up_proj` | Multi-Latent Attention up projection | moderate | moderate |
| `gdn` | complete GatedDeltaNet module: projections, convolution, recurrent rule, normalization, CP exchange, and output projection | architecture-dependent | potentially material on GDN-heavy hybrids |

### Performance harness CLI

Expand Down Expand Up @@ -109,6 +111,15 @@ uv run python scripts/performance/run_script.py \
- `distribute_saved_activations=True` cannot be combined with `sequence_parallel=True`
- Combining `mlp` + `core_attn` recompute is slightly worse than `mlp` alone
due to double recompute overhead
- `gdn` requires
`experimental_attention_variant="gated_delta_net"`. It is selective
submodule recompute and is distinct from full-layer recompute.
- Fine-grained MoE expert-parallel overlap rejects full-layer recompute,
non-null `recompute_method`/`recompute_num_layers`, and
`recompute_modules` containing `moe`. On current MCore, selective non-MoE
modules such as `gdn` remain valid. This is a compatibility contract, not a
throughput guarantee; validate memory, step time, and numerical health on
the exact combined schedule.

## Measured Results

Expand All @@ -133,6 +144,34 @@ Key takeaways:
- For this workload, the actual OOM fix was `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True`
(memory fragmentation, not capacity). See @skills/nemo-mbridge-perf-memory-tuning/SKILL.md.

### Qwen3.5 GDN-heavy EP-overlap result

On 2026-07-26, an exact 2-node/16-H100 Qwen3.5-35B-A3B run paired
fine-grained EP overlap with `recompute_modules=["gdn"]`. The matched
owner-stream-release/connections=1 overlap control averaged 44.4646 seconds
over steps 2-3 and reached 72.028 GiB rank-0 peak allocated memory.

Selective GDN recompute completed all three steps with finite loss and
gradients and zero skipped/NaN iterations. Steps 2-3 were 25.5641 and 25.1589
seconds, a 25.3615-second mean (about 232.27 model TFLOP/s/GPU), while
iteration-2 rank-0 peak allocated/reserved memory fell to 59.516/65.123 GiB.
This removed about 12.5 GiB of allocated peak and contracted the pathological
overlap schedule by 42.96%.

The result still ran 13.52% slower by step time than the accepted no-EP-overlap
control at 22.340925 seconds, so it was rejected as a performance recipe.
Treat architecture-specific selective recompute as a way to recover a
memory-bound schedule, not as a guarantee that recompute plus overlap beats
the simpler schedule.

The recovered headroom can enable another memory feature without making the
combination faster. Adding active MoE paged stash to the same selective-GDN
run completed with finite numerics and held iteration-2 rank-0 peak allocation
to 65.891 GiB, but steps 2-3 averaged 29.98755 seconds. That was 18.24% slower
than GDN recompute alone because full-schedule stash pack/unpack work outweighed
the allocator benefit. Validate stacked memory features end to end; capacity
compatibility does not imply additive throughput benefit.

## Code Anchors

### Recompute modules enum and selective checkpoint logic
Expand Down Expand Up @@ -169,6 +208,19 @@ Key takeaways:
], "full recompute is only supported with full iteration CUDA graph."
```

### GDN selective recompute and EP-overlap validation (MCore)

```text
3rdparty/Megatron-LM/megatron/core/transformer/transformer_config.py
3rdparty/Megatron-LM/megatron/core/ssm/gated_delta_net.py
src/megatron/bridge/training/comm_overlap.py
```

`TransformerConfig` accepts `gdn` only for the gated-delta-net attention
variant, and `GatedDeltaNet` uses it to checkpoint the whole module. Bridge and
MCore EP-overlap validation forbid full/MoE recompute but do not forbid this
selective GDN scope.

### CPU offloading PP incompatibility (MCore)

```1303:1306:3rdparty/Megatron-LM/megatron/core/transformer/transformer_config.py
Expand All @@ -187,6 +239,8 @@ Key takeaways:
| `AssertionError: full recompute is only supported with full iteration CUDA graph` | layer-level recompute with TE-scoped graph capture | check `cuda_graph_impl` and `cuda_graph_scope` | use `selective`, set `cuda_graph_impl=none`, or use `local` + `full_iteration` |
| ValueError: PP + CPU offloading | `cpu_offloading=True` with `pipeline_model_parallel_size > 1` | check PP config | disable CPU offloading or set PP=1 |
| mlp+core_attn worse than mlp alone | double recompute overhead | compare Exp 1 vs Exp 2 | use mlp alone |
| `gdn` rejected during config validation | the model is not using the gated-delta-net attention variant | inspect `experimental_attention_variant` | remove `gdn` or select a module implemented by that architecture |
| EP-overlap recompute assertion | full-layer recompute, `recompute_method`/`recompute_num_layers`, or full `moe` recompute was enabled | inspect all four recompute fields after overrides | use selective non-MoE scope such as `gdn` and leave method/count null |

## Known Limitations

Expand Down
Loading
Loading