Skip to content

[Dev] feat(MoE): Refactor cuda_graph_scope - part2#2353

Merged
yanring merged 7 commits into
NVIDIA:devfrom
buptzyb:robinz/dev_cudagraph
Dec 1, 2025
Merged

[Dev] feat(MoE): Refactor cuda_graph_scope - part2#2353
yanring merged 7 commits into
NVIDIA:devfrom
buptzyb:robinz/dev_cudagraph

Conversation

@buptzyb

@buptzyb buptzyb commented Nov 21, 2025

Copy link
Copy Markdown
Contributor

What does this PR do ?

main PR #1920.

part1 is #1917.

This part mainly changes the cuda_graph_scope to a enum structure, and fixes cudagraph UTs.

⚠️ For major changes (either in lines of code or in its impact), please make sure to first share discuss a design-doc with the team.

Contribution process

flowchart LR
    A[Pre-checks] --> B[PR Tests]
    subgraph Code Review/Approval
        C1[Expert Review] --> C2[Final Review]
    end
    B --> C1
    C2 --> D[Merge]
Loading

Pre-checks

  • I want this PR in a versioned release and have added the appropriate Milestone (e.g., Core 0.8)
  • I have added relevant unit tests
  • I have added relevant functional tests
  • I have added proper typing to my code Typing guidelines
  • I have added relevant documentation
  • I have run the autoformatter.sh on my PR

Code review

The following process is enforced via the CODEOWNERS file for changes into megatron/core. For changes outside of megatron/core, it is up to the PR author whether or not to tag the Final Reviewer team.

For MRs into `main` branch

(Step 1): Add PR label Expert Review

(Step 2): Collect the expert reviewers reviews

  1. Attach the Expert Review label when your PR is ready for review.
  2. GitHub auto-assigns expert reviewers based on your changes. They will get notified and pick up your PR soon.

⚠️ Only proceed to the next step once all reviewers have approved, merge-conflict are resolved and the CI is passing.
Final Review might get declined if these requirements are not fulfilled.

(Step 3): Final Review

  1. Add Final Review label
  2. GitHub auto-assigns final reviewers based on your changes. They will get notified and pick up your PR soon.

(Optional Step 4): Cherry-pick into release branch

If this PR also needs to be merged into core_r* release branches, after this PR has been merged, select Cherry-pick to open a new PR into the release branch.

For MRs into `dev` branch The proposed review process for `dev` branch is under active discussion.

MRs are mergable after one approval by either eharper@nvidia.com or zijiey@nvidia.com.

Merging your PR

Any member of core-adlr and core-nemo will be able to merge your PR.

Signed-off-by: Robin Zhang <robinz@nvidia.com>
Signed-off-by: Robin Zhang <robinz@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Nov 21, 2025

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@buptzyb buptzyb self-assigned this Nov 21, 2025
@buptzyb

buptzyb commented Nov 21, 2025

Copy link
Copy Markdown
Contributor Author

/ok to test df48cc1

@ko3n1g ko3n1g added this to the Core 0.16 milestone Nov 21, 2025
@buptzyb

buptzyb commented Nov 26, 2025

Copy link
Copy Markdown
Contributor Author

/ok to test e3292c1

@yanring yanring left a comment

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.

LGTM, here are some AI-generated comments for you to review and adjust as needed:

megatron/core/transformer/cuda_graphs.py

Line 1715-1736:

The delete_cuda_graphs() method looks useful! Suggestions:

  1. Add a warning log when the TE version is too low to actually delete graphs
  2. Consider calling torch.cuda.empty_cache() after deleting graphs to release GPU memory

Line 1219:

The enumerated _ variable is unused. Consider using for layers in self.callables_per_chunk: directly.


megatron/core/transformer/moe/token_dispatcher.py

Line 1078-1082:

What is the intent of this change? Please add a comment explaining why these values need to be retained in drop_and_pad mode. This is important for understanding CUDA graph compatibility.


megatron/core/transformer/transformer_layer.py

Line 624-627:

👍 Good fix for the missing NVTX marker! Consider adding a brief comment explaining why the pop needs to happen early in this code path.


megatron/training/arguments.py

Line 1495:

The lambda function is quite complex. Consider extracting it into a named function for better readability and testability. Also consider handling case-insensitive inputs.

def parse_cuda_graph_scope(scope):
    if not isinstance(scope, str):
        return scope
    scope_lower = scope.lower()
    if scope_lower == "full":
        return "full"
    try:
        return CudaGraphScope[scope]
    except KeyError:
        valid_scopes = [s.name for s in CudaGraphScope] + ["full"]
        raise argparse.ArgumentTypeError(
            f"Invalid cuda_graph_scope '{scope}'. Valid values are: {valid_scopes}"
        )

megatron/core/transformer/transformer_config.py

Line 1593-1614:

Good backward compatibility handling! Suggestions:

  1. Consider extracting this logic into a separate helper function for better maintainability
  2. Add unit tests covering various input formats (string, list, enum, comma-separated string, etc.)

megatron/core/transformer/enums.py

Line 67-80:

Consider adding docstrings for each enum value to clarify their usage:

class CudaGraphScope(enum.Enum):
    """Cuda Graph Scope - defines which parts of the model to capture."""
    
    full_iteration = 1  # Captures the entire training/inference iteration
    attn = 2           # Captures attention operations
    mlp = 3            # Captures MLP operations (dense layers only)
    moe = 4            # Captures full MoE layer including router and experts
    moe_router = 5     # Captures only MoE router and preprocessing
    moe_preprocess = 6 # Captures MoE preprocessing (requires moe_router)
    mamba = 7          # Captures Mamba layer operations

megatron/core/transformer/moe/fused_a2a.py

Line 320-327:

The new reset_hybrid_ep_buffer() function should include documentation about when it should be called and thread-safety considerations:

def reset_hybrid_ep_buffer():
    '''
    Reset the HybridEP buffer.
    
    Should be called:
    - When changing parallelism configurations
    - During test teardown to release resources
    
    Note: Not thread-safe. Ensure no concurrent operations
    are accessing the buffer when calling this function.
    '''
    global _hybrid_ep_buffer
    _hybrid_ep_buffer = None

Comment thread megatron/training/arguments.py Outdated
'"local": capture the CUDA graph using MCore local implementation. --cuda-graph-scope=\"full_iteration\" enables whole iteration CUDA graph. '
'"transformer_engine": capture the CUDA graph using TE make_graphed_callables().')
group.add_argument('--cuda-graph-scope', nargs='+', type=str, default=[],
group.add_argument('--cuda-graph-scope', nargs='+', type=lambda scope: CudaGraphScope[scope] if isinstance(scope, str) and scope != "full" else scope, default=[],

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.

"isinstance(scope, str)" seems to be redundant since argparse will always pass str.

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.

removed

for _, layers in enumerate(self.callables_per_chunk):
for layer in layers:
for graph in layer.cuda_graphs:
if is_te_min_version("2.10.0"):

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.

te version check is repeated inside loop, maybe only one pre-check is enough.

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.

done

Comment on lines 966 to +968
@pytest.mark.skipif(
not (HAVE_TE and is_te_min_version("1.14.0")),
reason="Partial CUDA graph support requires TransformerEngine version >= 1.14.0",
not (HAVE_TE and is_te_min_version("2.10.0")),
reason="Partial CUDA graph UT support requires TransformerEngine version >= 2.10.0",

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.

Just curious that the version requirement jumping is only due to UT code compatibility or functional requirement🤔

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.

In TE 2.10.0 we added the reset() API for a graph object (this commit NVIDIA/TransformerEngine@262c184) . Without the reset() called after training, there is a probability that the UT hangs at exiting.

Signed-off-by: Robin Zhang <robinz@nvidia.com>
@yanring

yanring commented Dec 1, 2025

Copy link
Copy Markdown
Contributor

/ok to test efb8f5d

@buptzyb

buptzyb commented Dec 1, 2025

Copy link
Copy Markdown
Contributor Author

/ok to test 1e2e708

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dev branch Dev branch related issues and development Expert Review [deprecated] Apply this label to indicate that your PR is ready for expert review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants