Skip to content
Merged
Show file tree
Hide file tree
Changes from 103 commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
bde9685
Add nvtx ranges for mtp
santhnm2 Apr 10, 2026
7587f2f
perf benchmarking
santhnm2 Apr 10, 2026
b8bdd12
Remove simpy dependency
santhnm2 Apr 10, 2026
e948bc9
cuda graphs for mtp
santhnm2 Apr 10, 2026
6c60f34
mtp cuda graphs
santhnm2 Apr 10, 2026
90ffe8a
fix cuda graphs
santhnm2 Apr 10, 2026
8ce0a4b
cuda graph fix
santhnm2 Apr 10, 2026
84129e7
Fix dummy_position_ids dtype
santhnm2 Apr 10, 2026
9bfa0fc
Try to compile more
santhnm2 Apr 10, 2026
cbc1270
remove extra ep sync
santhnm2 Apr 10, 2026
7687743
Add nvtx ranges
santhnm2 Apr 11, 2026
407e12d
Remove cpu sync
santhnm2 Apr 11, 2026
e54458c
Avoid h2d syncs
santhnm2 Apr 11, 2026
70afe4c
Linting
santhnm2 Apr 11, 2026
5e84f85
compile rewind_kv_cache
santhnm2 Apr 11, 2026
e2020b2
Linting
santhnm2 Apr 11, 2026
56b93ff
Fix verify_speculative_tokens
santhnm2 Apr 11, 2026
98a32ef
More fixes
santhnm2 Apr 11, 2026
3fefb93
Add torch.compile back
santhnm2 Apr 11, 2026
96668d6
Linting
santhnm2 Apr 11, 2026
9a95577
Try to fix tests
santhnm2 Apr 11, 2026
74edd58
Fix test
santhnm2 Apr 11, 2026
b6466d2
More test fixes
santhnm2 Apr 11, 2026
6941afb
Bug fix
santhnm2 Apr 11, 2026
94452d5
Linting
santhnm2 Apr 11, 2026
cd981ea
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 11, 2026
774ec53
kernelize
santhnm2 Apr 11, 2026
d9c2902
more perf optimizations
santhnm2 Apr 13, 2026
45cc1eb
Fix dtype
santhnm2 Apr 13, 2026
6c57910
Fix dtype
santhnm2 Apr 13, 2026
68923e2
Fix cuda graph
santhnm2 Apr 13, 2026
0066e42
Clean up CUDA graph
santhnm2 Apr 13, 2026
0a235ff
Clean up graph
santhnm2 Apr 13, 2026
0d6243a
Fix graph
santhnm2 Apr 13, 2026
5bfd41c
Debugging
santhnm2 Apr 13, 2026
95ed9a0
Fix graphs
santhnm2 Apr 13, 2026
4b1d093
Fix graphs
santhnm2 Apr 14, 2026
7913032
Remove debug prints
santhnm2 Apr 14, 2026
819ec4a
Merge remote-tracking branch 'upstream/main' into mtp_perf_v2
santhnm2 Apr 14, 2026
a39dffd
Clean up
santhnm2 Apr 14, 2026
9e6680c
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 14, 2026
b1e44cf
Merge branch 'mtp_perf_v2' into mtp_perf
santhnm2 Apr 14, 2026
9bd8aa4
Add unit test
santhnm2 Apr 14, 2026
e8690f8
Linting
santhnm2 Apr 14, 2026
89c4ced
Zero out padded values in test
santhnm2 Apr 14, 2026
610d434
Fix test
santhnm2 Apr 14, 2026
0656373
fix test
santhnm2 Apr 14, 2026
93f5a34
Fix test
santhnm2 Apr 14, 2026
bd8c4b9
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 14, 2026
bbbb92d
Fix test
santhnm2 Apr 14, 2026
24d481e
Fix tests
santhnm2 Apr 14, 2026
e872366
Fix tests
santhnm2 Apr 14, 2026
aacefb4
Linting
santhnm2 Apr 14, 2026
ac2048c
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 14, 2026
13122e7
Mark flaky tests
santhnm2 Apr 14, 2026
8dacd28
Merge with main
santhnm2 Apr 20, 2026
8d3a063
Fix rewind_kv_cache_bug
santhnm2 Apr 20, 2026
210415a
Move MTP cuda graph warmup inline
santhnm2 Apr 20, 2026
bcebf41
Linting
santhnm2 Apr 20, 2026
c82b70a
Add Triton tests
santhnm2 Apr 20, 2026
3a3bbdc
Linting
santhnm2 Apr 20, 2026
c05d8a5
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 20, 2026
43de7aa
NVLS all gathers
santhnm2 Apr 20, 2026
78fba30
Fix gather_output
santhnm2 Apr 20, 2026
4c0ac1a
Expand graph scope
santhnm2 Apr 20, 2026
03d52ab
Update tests
santhnm2 Apr 20, 2026
4abc573
Fix graph manager
santhnm2 Apr 20, 2026
d8b08ac
Fix chaining
santhnm2 Apr 20, 2026
836a914
Fix keys
santhnm2 Apr 20, 2026
bb597ca
Inference RS
santhnm2 Apr 20, 2026
3c3b23b
Fix flag
santhnm2 Apr 20, 2026
5391d6c
Merge remote-tracking branch 'upstream/main' into mtp_perf_v2
santhnm2 Apr 21, 2026
bc214cf
Formatting
santhnm2 Apr 21, 2026
2a77e5b
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 21, 2026
91029d4
Merge branch 'mtp_perf_v2' into mtp_perf
santhnm2 Apr 21, 2026
c0f4fff
Clean up and fixes
santhnm2 Apr 21, 2026
c39c8e5
Fix tests
santhnm2 Apr 21, 2026
084e605
Make MTP cuda graphs test engine level
santhnm2 Apr 21, 2026
432722d
Fix dtype
santhnm2 Apr 21, 2026
1a59fcc
Cast model to params_dtype
santhnm2 Apr 21, 2026
2f8e0c8
Fixes
santhnm2 Apr 21, 2026
57771f9
Try again to fix tests
santhnm2 Apr 21, 2026
314a2ee
Fix 0 token count graph error
santhnm2 Apr 21, 2026
f0a87e0
Fix casting
santhnm2 Apr 21, 2026
168028b
Fix input dtype
santhnm2 Apr 21, 2026
3d61c13
Add depth
santhnm2 Apr 21, 2026
43176d1
Delete cuda graphs for eager
santhnm2 Apr 21, 2026
d76da59
Fix delete cuda graphs
santhnm2 Apr 21, 2026
248d761
Fix deletion path
santhnm2 Apr 21, 2026
187b00d
Linting
santhnm2 Apr 21, 2026
4676931
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 21, 2026
aa1e8f6
Cleanup
santhnm2 Apr 21, 2026
1bc4a87
Linting
santhnm2 Apr 21, 2026
f97e3f6
Remove dead code
santhnm2 Apr 21, 2026
fcb2ec3
Clean up resources for test_parallel_inference
santhnm2 Apr 21, 2026
259c1f5
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 21, 2026
f2504c5
Fix EP in test
santhnm2 Apr 21, 2026
3d21b5d
Fix is_expert arg
santhnm2 Apr 21, 2026
fa22202
Fix case where weight is None
santhnm2 Apr 21, 2026
389d83e
Add depth kwarg
santhnm2 Apr 21, 2026
0825420
Nits
santhnm2 Apr 21, 2026
ffdefc5
Fix normalization in unit test
santhnm2 Apr 21, 2026
dbd9f20
Fix bias in unit test
santhnm2 Apr 21, 2026
9b6a2c4
Add no-op get_extra_state function
santhnm2 Apr 21, 2026
343c6bb
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 21, 2026
79c871d
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 21, 2026
2d8b102
Linting
santhnm2 Apr 21, 2026
6d2ace5
Address review comments
santhnm2 Apr 22, 2026
53bd51d
Address reviewer comments
santhnm2 Apr 22, 2026
157c0c5
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 22, 2026
8f5e7d3
Revert unnecessary changes
santhnm2 Apr 22, 2026
6d1f0b6
Linting
santhnm2 Apr 22, 2026
c5a2d5f
Clean up MTP graph manager tracking
santhnm2 Apr 22, 2026
b074789
More cleanup
santhnm2 Apr 22, 2026
ed0e028
Restore docstring
santhnm2 Apr 22, 2026
ffe4780
Fix skip_weight_param_allocation
santhnm2 Apr 22, 2026
3119052
Revert InferenceColumnParallelLinear changes
santhnm2 Apr 22, 2026
d991265
Linting
santhnm2 Apr 22, 2026
9a72316
Standardize inference_reduce_scatter_first_dim API
santhnm2 Apr 22, 2026
0c4b56c
Merge
santhnm2 Apr 22, 2026
071ab7f
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 22, 2026
ba6de96
Merge with main
santhnm2 Apr 23, 2026
4e56aee
Remove is_expert setting
santhnm2 Apr 23, 2026
9d90639
Address reviewer comments
santhnm2 Apr 23, 2026
adafdd1
Linting
santhnm2 Apr 23, 2026
1700d02
Add mtp_utils_pytorch.py
santhnm2 Apr 23, 2026
d75ed23
Linting
santhnm2 Apr 23, 2026
4bab4be
setup_method -> setup_cls
santhnm2 Apr 23, 2026
0afeb18
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 23, 2026
61bde08
Add classmethod decorator
santhnm2 Apr 23, 2026
652c9b5
More test NCCL cleanup
santhnm2 Apr 23, 2026
06e6907
Refactor test_dynamic_engine.py
santhnm2 Apr 23, 2026
96a01ea
clear nvte env vars
santhnm2 Apr 23, 2026
77473c3
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 24, 2026
490165b
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 24, 2026
8eb0c80
Fix test
santhnm2 Apr 24, 2026
ac467b1
Refactor test_text_generation_controller.py
santhnm2 Apr 24, 2026
3e28f26
Explicitly delete cuda graphs
santhnm2 Apr 24, 2026
15d0e83
Add cuda graph deletion to static engine
santhnm2 Apr 24, 2026
b4d0d69
More cleanup
santhnm2 Apr 24, 2026
1a8f7da
Add clear_nvte_env_vars to static test
santhnm2 Apr 24, 2026
9f64401
Add garbage collection to test_moe_inference.py
santhnm2 Apr 24, 2026
f193a01
Linting
santhnm2 Apr 24, 2026
5266808
Merge remote-tracking branch 'upstream/main' into mtp_perf
santhnm2 Apr 24, 2026
c6ad4f3
More cuda graph deletion
santhnm2 Apr 24, 2026
2cae20f
Fix copyright
santhnm2 Apr 24, 2026
f537865
Revert unnecessary cuda graph changes
santhnm2 Apr 24, 2026
84b1ef5
Merge with main
santhnm2 Apr 26, 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
37 changes: 28 additions & 9 deletions examples/inference/gpt/gpt_dynamic_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,6 @@ def main():
)
initialize_megatron()

# Start Nsight profiler.
if os.environ.get("NSIGHT_PREFIX"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this example work with MoEs, given that it does not have the coordinator?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, gpt_dynamic_inference_with_coordinator.py should be preferred for any MoE workload. But the base script still works, just without data parallelism (I pass in a prompt file so all ranks run all prompts). We should probably consolidate these two scripts in the future though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point we should get rid of this example. Perhaps move it inside the tests folder, since some tests still rely on it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted changes to gpt_dynamic_inference.py

torch.cuda.cudart().cudaProfilerStart()

level_str = os.getenv("LOG_LEVEL", "INFO").upper()
level = getattr(logging, level_str, logging.INFO)
logging.basicConfig(level=level, force=True)
Expand Down Expand Up @@ -350,28 +346,53 @@ def main():
print(setup_prefix)
print("~~~")

# Warmup: run one untimed iteration so CUDA caches, JIT kernels, and
# allocator pools are ready before the measured runs.
if args.inference_repeat_n > 1:
print("Running warmup iteration ...")
engine.reset()
run_inference(requests, engine)
torch.cuda.synchronize()
engine.reset()

# Start CUDA profiler after warmup so nsys traces only the measured runs.
if os.environ.get("NSIGHT_PREFIX"):
torch.cuda.cudart().cudaProfilerStart()

# Run and time test, optionally `args.inference_repeat_n` times.
throughputs = []
cuda_start_event = torch.cuda.Event(enable_timing=True)
cuda_end_event = torch.cuda.Event(enable_timing=True)
for _ in range(args.inference_repeat_n):

# Reset engine.
engine.reset()

torch.cuda.reset_peak_memory_stats()

# Trial.
# Synchronize before starting the timer to avoid measuring stale GPU work.
torch.cuda.synchronize()

# Trial — use both wall-clock and CUDA events for accurate GPU timing.
t = get_curr_time()
cuda_start_event.record()
result = run_inference(requests, engine)
cuda_end_event.record()
step_times = result["step_times"]
add_times = result["add_times"]
output_times = result["output_times"]
total_output_tokens = result["total_output_tokens"]
torch.cuda.synchronize()
total_time = get_curr_time() - t
cuda_elapsed_ms = cuda_start_event.elapsed_time(cuda_end_event)
stats = torch.cuda.memory_stats()
throughput = total_output_tokens / total_time
throughputs.append(throughput)

# Stop CUDA profiler after measured runs.
if os.environ.get("NSIGHT_PREFIX"):
torch.cuda.cudart().cudaProfilerStop()

# Validate all requests finished.
for request in requests:
assert request.state == "finished", f"request.state == '{request.state}' != 'finished'."
Expand Down Expand Up @@ -505,19 +526,17 @@ def escape_str(s):
# f"count [ p {p_count}, d {d_count} ]."
# )
capture_str = f"{engine.capture_stats['time']:.2f} sec" if engine.capture_stats else "--"
cuda_throughput = total_output_tokens / (cuda_elapsed_ms / 1000.0)
print(
f"{setup_prefix} … " f"throughput: {throughput:.3f} tok/s … ",
f"total time: {total_time:.3f}s … "
f"cuda time: {cuda_elapsed_ms:.1f}ms ({cuda_throughput:.3f} tok/s) … "
Comment thread
lmcafee-nvidia marked this conversation as resolved.
Resolved
f"mem {peak_alloc_gb:.1f}/{peak_resvd_gb:.1f} GB … "
f"steps: {engine.context.step_count:d} … "
f"capture {capture_str}",
)
print("~~~")

# Stop Nsight profiler.
if os.environ.get("NSIGHT_PREFIX"):
torch.cuda.cudart().cudaProfilerStop()


if __name__ == "__main__":
main()
1 change: 0 additions & 1 deletion examples/inference/gpt/gpt_dynamic_inference_12b.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
set -u

# Libraries.
pip install simpy
pip install sentencepiece
pip install tiktoken

Expand Down
1 change: 0 additions & 1 deletion examples/inference/gpt/gpt_dynamic_inference_357m.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
set -u

# Libraries.
pip install simpy
pip install sentencepiece
pip install tiktoken

Expand Down
17 changes: 6 additions & 11 deletions examples/inference/gpt/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,13 @@ def get_time_offsets(

random.seed(seed)

import simpy # Guard against this import in test case

# Generate random time offsets.
def arrival(r):
while True:
yield env.timeout(random.expovariate(r))
time_offsets.append(env.now)

# Generate Poisson arrival times by accumulating exponential inter-arrival intervals.
time_offsets = []
env = simpy.Environment()
env.process(arrival(incoming_requests_per_sec))
env.run(incoming_requests_duration)
current_time = 0.0
while current_time < incoming_requests_duration:
current_time += random.expovariate(incoming_requests_per_sec)
if current_time < incoming_requests_duration:
time_offsets.append(current_time)

# Ensure at least a single request.
if len(time_offsets) == 0:
Expand Down
4 changes: 4 additions & 0 deletions megatron/core/inference/batch_dimensions_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ def is_valid(
Returns:
True if the config is valid, False otherwise
"""
# A dimension with no tokens serves no requests.
if self.token_count <= 0:
return False

# Check if total requests exceed maximum
if self.prefill_req_count + self.decode_req_count > max_requests:
return False
Expand Down
45 changes: 44 additions & 1 deletion megatron/core/inference/engines/dynamic_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
unset_inference_cuda_graphed_iteration_for_ep_inference,
)
from megatron.core.process_groups_config import ProcessGroupCollection
from megatron.core.transformer.cuda_graphs import delete_cuda_graphs
from megatron.core.transformer.cuda_graphs import delete_cuda_graphs, graph_capture
from megatron.core.transformer.enums import CudaGraphScope
from megatron.core.transformer.moe.router_replay import RouterReplay, RouterReplayAction
from megatron.core.utils import (
Expand All @@ -64,6 +64,7 @@
nvtx_range_pop,
nvtx_range_push,
trace_async_exceptions,
unwrap_model,
)

from .async_zmq_communicator import AsyncZMQCommunicator
Expand Down Expand Up @@ -365,6 +366,21 @@ def create_cuda_graphs(self, reset_context: bool = True):
unwrapped_model = controller.inference_wrapped_model.model
set_inference_cuda_graphed_iteration_for_ep_inference(unwrapped_model)

# MTP warmup preparation: capture MTP CUDA graphs alongside the
# decoder graphs within the same loop rather than in a separate pass.
unwrapped = unwrap_model(controller.inference_wrapped_model.model)
mtp_warmup_enabled = (
controller.num_mtp_heads > 0
and (controller.num_speculative_tokens or 0) > 0
and hasattr(unwrapped, 'mtp')
)
if mtp_warmup_enabled:
tp_size = get_pg_size(controller.inference_wrapped_model.tp_group)
sp_enabled = model_config.sequence_parallel and tp_size > 1
mtp_pass_depth = not unwrapped.mtp.mtp_use_repeated_layer
mtp_warmup_depths = range(controller._num_mtp_depths) if mtp_pass_depth else [None]
mtp_seen_batch_sizes = set()

tbar = enumerate(context.cuda_graph_batch_dimensions_list)
if HAVE_TQDM:
tbar = tqdm(tbar, total=len(context.cuda_graph_batch_dimensions_list))
Expand All @@ -390,12 +406,39 @@ def create_cuda_graphs(self, reset_context: bool = True):
# Forward pass -> logits.
controller._dynamic_step_forward_logits(input_ids, position_ids)

# MTP CUDA graph warmup for this batch dimension.
if mtp_warmup_enabled:
n = cuda_graph_batch_dimension.req_count
if sp_enabled:
n += (tp_size - n % tp_size) % tp_size
if n > 0 and n not in mtp_seen_batch_sizes:
mtp_seen_batch_sizes.add(n)
device = torch.cuda.current_device()
batch_dim = n // tp_size if sp_enabled else n
# Use zeros (not empty) — garbage token IDs cause OOB embedding lookups during graph capture/replay.
for depth in mtp_warmup_depths:
with graph_capture():
Comment thread
sidsingh-nvidia marked this conversation as resolved.
Resolved
unwrapped.compute_mtp_single_step(
hidden_states=torch.zeros(
(batch_dim, 1, model_config.hidden_size),
device=device,
dtype=model_config.params_dtype,
),
next_token_ids=torch.zeros((1, n), device=device, dtype=torch.long),
position_ids=torch.zeros((1, n), device=device, dtype=torch.int64),
depth=depth,
)

context.reset()

# Disable inference dispatcher after graph capture
if is_inference_optimized_ep:
unset_inference_cuda_graphed_iteration_for_ep_inference(unwrapped_model)

if mtp_warmup_enabled and mtp_seen_batch_sizes:
controller._has_mtp_cuda_graphs = True
Comment thread
lmcafee-nvidia marked this conversation as resolved.
Resolved
logging.info("> MTP CUDA graph warmup: %d batch size(s)", len(mtp_seen_batch_sizes))

# Memory usage.
time_end = time.time()
mem_stats_end = torch.cuda.memory_stats()
Expand Down
Loading
Loading