Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
419ddbf
Add NVTX ranges for inference loop stages (baseline commit)
lmcafee-nvidia Apr 16, 2026
3425d29
Move context bookkeeping tensors to CPU with ContextGPUView interface
lmcafee-nvidia Apr 14, 2026
4efda46
Defer Mamba GPU ops to transfer step, add transfer_samples_to_cpu()
lmcafee-nvidia Apr 14, 2026
3dbc4d4
Move deferred Mamba GPU ops to top of initialize_attention_state()
lmcafee-nvidia Apr 16, 2026
0271285
Move Mamba metadata computation to CPU, H2D copy via load_from_cpu()
lmcafee-nvidia Apr 16, 2026
0c755bb
Move MambaSlotAllocator intermediate tensors to CPU
lmcafee-nvidia Apr 16, 2026
2e5b588
Update run_dynamic_text_generation_server.py for PR #4225 args init s…
Apr 17, 2026
a5a1590
Remove redundant fill_(0) calls in MHAMetadata.reset()
Apr 21, 2026
7e715cd
Replace per-step NCCL AllReduce with ZMQ in EP batch-dim sync
Apr 21, 2026
e15a12a
Coalesce transfer_bookkeeping_to_gpu into one H2D copy
Apr 21, 2026
eab349e
Coalesce MHA metadata H2D into unified bookkeeping buffer
Apr 21, 2026
2f295c6
Coalesce Mamba metadata H2D into unified bookkeeping buffer
Apr 21, 2026
149b4f5
Gate logging-only work behind will_log_this_step in async_forward
Apr 22, 2026
39ffe4d
Return CPU-side sampled_tokens in step_result to avoid D2H sync
Apr 22, 2026
811925d
Enable NVTX range helper in inference server entry
Apr 22, 2026
300f799
Merge remote-tracking branch 'main/main' into context-cpu
lmcafee-nvidia Apr 29, 2026
5d5cb60
Move _rewind_kv_cache bookkeeping rewind to CPU
lmcafee-nvidia Apr 30, 2026
17d3238
Fix _execute_pending_mamba_ops docstring callsite reference
lmcafee-nvidia Apr 30, 2026
f54c52c
Merge remote-tracking branch 'main/main' into context-cpu
lmcafee-nvidia Apr 30, 2026
674955a
Bind MHA state_data and run H2D inside initialize_attention_state
lmcafee-nvidia May 1, 2026
423d8a7
Merge remote-tracking branch 'main/main' into context-cpu
lmcafee-nvidia May 1, 2026
34834c0
Restore smallest_non_decode_cuda_graph_size attribute
lmcafee-nvidia May 1, 2026
578e1f2
Clarify coalesced bookkeeping comments
lmcafee-nvidia May 1, 2026
4bcb61c
Fix dynamic inference CUDA graph replay
lmcafee-nvidia May 1, 2026
74d63b5
Fix docs and install CI failures
lmcafee-nvidia May 1, 2026
477d780
Fix PyTorch DeviceMesh docs link
lmcafee-nvidia May 1, 2026
a8ae94e
Merge remote-tracking branch 'main/main' into context-cpu
lmcafee-nvidia May 2, 2026
3429e7d
Restore EP graph matcher compatibility
lmcafee-nvidia May 2, 2026
8305c95
Avoid deadsnakes when Python packages are available
lmcafee-nvidia May 2, 2026
a6f6d04
Remove docker change from context CPU PR
lmcafee-nvidia May 2, 2026
8c91a68
Stabilize MTP CUDA graph eager comparison
lmcafee-nvidia May 3, 2026
1dda0ac
Merge remote-tracking branch 'main/main' into context-cpu
lmcafee-nvidia May 3, 2026
2bb6d5e
Fix rewind cache test device expectations
lmcafee-nvidia May 3, 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
4 changes: 2 additions & 2 deletions docs/user-guide/features/megatron_fsdp.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

### 🧩 Compatibility

- PyTorch **[DeviceMesh](https://docs.pytorch.org/docs/stable/distributed.html#devicemesh)**, **[DTensor](https://docs.pytorch.org/docs/stable/distributed.tensor.html)**, and **[Distributed Checkpoint (DCP)](https://docs.pytorch.org/docs/stable/distributed.checkpoint.html)**
- PyTorch **[DeviceMesh](https://docs.pytorch.org/docs/2.11/distributed.html#torch.distributed.device_mesh.DeviceMesh)**, **[DTensor](https://docs.pytorch.org/docs/stable/distributed.tensor.html)**, and **[Distributed Checkpoint (DCP)](https://docs.pytorch.org/docs/stable/distributed.checkpoint.html)**
- **[Megatron Core](https://github.com/NVIDIA/Megatron-LM)**
- **[TransformerEngine](https://github.com/NVIDIA/TransformerEngine)**
- **[NVIDIA NeMo Framework Container](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/nemo)**
Expand Down Expand Up @@ -605,4 +605,4 @@ NCCL (`v2.27+`) supports symmetric allocation or registration for communicators
- **Copy-Engine (CE) Collectives**: Instead of using SMs (or CTAs) for common non-computational collectives like AG in Megatron-FSDP, copy engines are instead used to perform all-gather collectives, dedicating SM resources to compute and reduction during FSDP. Requires NCCL `v2.28+`.
- **High-Precision Reduction**: When training large models, high-precision gradient reduction and accumulation is desired for accuracy and convergence, but communicating FP32 gradients is expensive. With symmetric registration, FP32 accumulators enable gradients to be reduced in FP32 but communicated in BF16, which decreases gradient RS communication latency while maintaining high accuracy during training. Megatron-FSDP supports FP32 main gradient accumulation but BF16 gradient communication, customizable through `megatron_fsdp.MixedPrecisionPolicy`.

These optimizations significantly reduce SM resource contention for overlapped compute and communication kernels in FSDP. Symmetric registration, allocation, and pooling is also supported in PyTorch: [`torch.distributed._symmetric_memory`](https://docs.pytorch.org/docs/stable/symmetric_memory.html).
These optimizations significantly reduce SM resource contention for overlapped compute and communication kernels in FSDP. Symmetric registration, allocation, and pooling is also supported in PyTorch: [`torch.distributed._symmetric_memory`](https://docs.pytorch.org/docs/stable/symmetric_memory.html).
120 changes: 105 additions & 15 deletions megatron/core/inference/batch_dimensions_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,13 @@ def req_count(self) -> int:

@staticmethod
def adjust_batch_dims_for_expert_parallelism(
local_batch_dims, ep_group: Optional[torch.distributed.ProcessGroup] = None
local_batch_dims,
strict: bool = False,
decode_only_cuda_graphs: bool = True,
smallest_non_decode_cuda_graph_size: int = 0,
ep_group: Optional[torch.distributed.ProcessGroup] = None,
num_speculative_tokens: int = 0,
ep_zmq_communicator=None,
) -> Optional["InferenceBatchDimensions"]:
"""Adjust CUDA graph batch dimensions for expert parallelism.

Expand All @@ -152,7 +158,16 @@ def adjust_batch_dims_for_expert_parallelism(

Args:
local_batch_dims: The local batch dimensions to adjust.
ep_group: Expert parallel process group.
strict: Whether to use strict matching for batch dimensions.
decode_only_cuda_graphs: Whether CUDA graphs are only used for decode steps.
ep_group: Optional expert parallel process group. If None, uses global parallel state.
When using different EP sizes for inference vs training, pass the
inference EP group explicitly.
ep_zmq_communicator: Optional AsyncZMQCommunicator over the EP group. When
provided, the cross-rank MAX reduction runs on the CPU via ZMQ
(no GPU kernel, no H2D/D2H), avoiding a per-step NCCL AllReduce
on the compute stream. When absent, falls back to
torch.distributed.all_reduce on a GPU tensor.

Returns:
InferenceBatchDimensions with max token count, or None for eager mode.
Expand All @@ -162,23 +177,77 @@ def adjust_batch_dims_for_expert_parallelism(
return local_batch_dims

is_non_decode = local_batch_dims.prefill_req_count > 0
sync_tensor = torch.tensor(
[local_batch_dims.token_count, int(is_non_decode)],
dtype=torch.int32,
device=torch.cuda.current_device(),
)
torch.distributed.all_reduce(sync_tensor, op=torch.distributed.ReduceOp.MAX, group=ep_group)
sync_tensor = sync_tensor.cpu()

if sync_tensor[1].item() == 1:
if ep_zmq_communicator is not None:
# CPU-only sync via ZMQ: avoids a NCCL AllReduce kernel on the
# compute stream plus the H2D/D2H pair that sandwiches it.
(max_token_count, max_is_non_decode, max_prefill_count, max_decode_count) = (
ep_zmq_communicator.sync_all_reduce_max(
local_batch_dims.token_count,
int(is_non_decode),
local_batch_dims.prefill_req_count,
local_batch_dims.decode_req_count,
)
)
else:
sync_tensor = torch.tensor(
[
local_batch_dims.token_count,
int(is_non_decode),
local_batch_dims.prefill_req_count,
local_batch_dims.decode_req_count,
],
dtype=torch.int32,
device=torch.cuda.current_device(),
)
torch.distributed.all_reduce(
sync_tensor, op=torch.distributed.ReduceOp.MAX, group=ep_group
)
sync_tensor = sync_tensor.cpu()
max_token_count = int(sync_tensor[0].item())
max_is_non_decode = int(sync_tensor[1].item())
max_prefill_count = int(sync_tensor[2].item())
max_decode_count = int(sync_tensor[3].item())

is_any_ep_rank_in_non_decode = max_is_non_decode == 1

if is_any_ep_rank_in_non_decode and decode_only_cuda_graphs:
return None # any rank has prefill → eager mode

return InferenceBatchDimensions(
token_count=int(sync_tensor[0].item()),
prefill_req_count=local_batch_dims.prefill_req_count,
decode_req_count=local_batch_dims.decode_req_count,
adjusted_token_count = max_token_count

# Sync request counts across EP ranks when strict matching is enabled
# or when speculative tokens are used. With speculative tokens,
# decode-only graphs have token counts of decode_req_count * (spec+1)
# which creates a different granularity than mixed graphs (raw sizes).
# Without syncing, decode-only ranks and prefill ranks search different
# graph pools and may pick graphs with different token counts.
sync_request_counts = strict or (
is_any_ep_rank_in_non_decode and num_speculative_tokens > 0
)
adjusted_prefill_req_count = (
max_prefill_count if sync_request_counts else local_batch_dims.prefill_req_count
)
adjusted_decode_req_count = (
max_decode_count if sync_request_counts else local_batch_dims.decode_req_count
)

# When any EP rank has prefill requests (non-strict mode), elevate
# the token count to be >= the smallest prefill/mixed cuda graph.
# This ensures decode-only ranks don't match a fine-grained decode
# graph while prefill ranks match a coarser mixed graph, which would
# produce inconsistent token counts across EP ranks.
if is_any_ep_rank_in_non_decode and not strict:
adjusted_token_count = max(adjusted_token_count, smallest_non_decode_cuda_graph_size)

adjusted_batch_dim = InferenceBatchDimensions(
token_count=adjusted_token_count,
prefill_req_count=adjusted_prefill_req_count,
decode_req_count=adjusted_decode_req_count,
)

return adjusted_batch_dim


class CUDAGraphBatchDimensionBuilder:
"""Builder for creating and managing CUDA graph batch dimensions.
Expand Down Expand Up @@ -460,8 +529,12 @@ def add_if_valid(token_count: int, prefill_req_count: int, decode_req_count: int
def match_graph_config(
real_batch_dim: InferenceBatchDimensions,
cuda_graph_batch_dimensions_list: List[InferenceBatchDimensions],
smallest_non_decode_cuda_graph_size: int = 0,
strict: bool = False,
decode_only_cuda_graphs: bool = True,
ep_group: Optional[torch.distributed.ProcessGroup] = None,
num_speculative_tokens: int = 0,
ep_zmq_communicator=None,
match_ep_token_counts: bool = True,
) -> Optional[InferenceBatchDimensions]:
"""
Expand All @@ -472,9 +545,16 @@ def match_graph_config(
cuda_graph_batch_dimensions_list: List of available CUDA graph batch dimensions
strict: If False, prefill slots can be used for prefill or decode requests.
If True, prefill slots can only be used for prefill requests.
decode_only_cuda_graphs: Used by expert parallel matching. If this is true,
and one of the EP ranks is running a non-decode step, we elect to run in
eager mode instead of matching a decode-only cuda graph.
ep_group: Optional expert parallel process group. If None, uses global parallel state.
When using different EP sizes for inference vs training, pass the
inference EP group explicitly.
ep_zmq_communicator: Optional AsyncZMQCommunicator over the EP group. When
provided, batch-dimension MAX reduction uses a CPU-only ZMQ sync
instead of a GPU NCCL AllReduce. Forwarded to
adjust_batch_dims_for_expert_parallelism.
match_ep_token_counts: If True (default), token counts are synced across EP ranks via
all-reduce-max so all ranks select the same CUDA graph. Set to False when the
dispatcher handles per-rank token variation internally (e.g. AGV/RSV in the NVLS
Expand All @@ -491,9 +571,19 @@ def match_graph_config(
# NCCL dispatcher: all EP ranks must select the same CUDA graph. Sync batch dims
# across the EP group so graph selection is consistent.
adjusted_batch_dim = InferenceBatchDimensions.adjust_batch_dims_for_expert_parallelism(
real_batch_dim, ep_group=ep_group
real_batch_dim,
strict=strict,
decode_only_cuda_graphs=decode_only_cuda_graphs,
ep_group=ep_group,
smallest_non_decode_cuda_graph_size=smallest_non_decode_cuda_graph_size,
num_speculative_tokens=num_speculative_tokens,
ep_zmq_communicator=ep_zmq_communicator,
)

if adjusted_batch_dim is None:
# we hit this scenario if decode_only_cuda_graphs is true,
# and one of the EP ranks is running a non-decode step
# in that case, all ranks have to run in eager mode
return None
else:
adjusted_batch_dim = real_batch_dim
Expand Down
Loading
Loading