Skip to content

Clean up SSM ops directory structure - #6444

Merged
guihong-nv merged 9 commits into
NVIDIA:mainfrom
santhnm2:ssm_ops_reorg
Aug 14, 2026
Merged

guihong-nv merged 9 commits into
NVIDIA:mainfrom
santhnm2:ssm_ops_reorg

Conversation

@santhnm2

@santhnm2 santhnm2 commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor
  • I, the PR author, have personally reviewed every line of this PR.

What does this PR do?

Splits megatron/core/ssm/ops/ into two sub-packages to prepare for future linear attention variants:

  • ops/common/ -- the short causal convolution (causal_conv1d_triton, causal_conv1d_varlen), the slot-indexed state gather/scatter that prefix caching uses (intermediate_extraction), and the autotune-config filtering that keeps the kernels deterministic (determinism). Every linear-attention mixer needs these; determinism was already shared between the SSD kernels and the varlen convolution.
  • ops/mamba2/ -- the state-space duality kernels (ssd_*, mamba_ssm, batch_invariant_decode).

intermediate_extraction reads as Mamba-specific from its docstring, but the kernels are a plain conditional gather/scatter over flat state rows with no Mamba math in them, so it belongs with the shared code.

Pure move. The top-level ops re-exports are unchanged, so callers that go through megatron.core.ssm.ops need no edit. Unit tests move to mirror the new layout.

⚠️ For major changes (either in lines of code or in its impact), please make sure to first share a design doc with the team. If you're unsure what's the best way to do so, contact @NVIDIA/mcore-oncall.

Issue tracking

For PRs from open-source community contributors:

  • New features: a linked issue is required. Please open a feature request and reference it here before submitting the PR.
  • Small updates (bug fixes, minor improvements): a linked issue is recommended and will accelerate the PR review process.

Linked issue:

Contribution process

Pre-checks

  • 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

Feel free to message or comment @NVIDIA/mcore-oncall to help accelerate your merge into main. The less complex your PR is, the faster it will be approved and merged!

All PRs start as draft. If you open a non-draft PR, it will be automatically converted to draft.

Step 1: Mark PR as "Ready for Review"

  1. When your PR is ready, click Ready for Review.
  2. An oncall reviewer is auto-assigned and expert reviewers are notified based on your changes.
    • Some PRs may jump straight to step 2. This is determined by .github/CODEOWNERS.

⚠️ Only mark as ready once merge-conflicts are resolved and the CI is passing.
Final Review might get declined if these requirements are not fulfilled.

Step 2: Final Review

For PRs that change megatron/core, once all expert reviewers have approved, the Final Review label is applied automatically and final reviewers are assigned.

For PRs outside megatron/core, this step is skipped.

Step 3: Approved

Once all required reviewers have approved, the Approved label is applied automatically.

Merge

Any member of mcore-engineers will be able to merge your PR.

@copy-pr-bot

copy-pr-bot Bot commented Aug 11, 2026

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.

@santhnm2 santhnm2 changed the title refactor(ssm): split the SSM op kernels by recurrence Clean up SSM ops directory structure Aug 11, 2026
`megatron/core/ssm/ops/` was a flat directory holding the Mamba2 SSD kernels
alongside the handful of kernels that are not specific to any recurrence. That
was tenable while Mamba2 was the only variant with forked kernels. It stops
being tenable as soon as a second variant lands its own fork, because there is
then no way to tell from the layout which kernels a given mixer owns.

Split into two packages:

* `ops/common/` -- the short causal convolution (`causal_conv1d_triton`,
  `causal_conv1d_varlen`), the slot-indexed state gather/scatter that prefix
  caching uses (`intermediate_extraction`), and the autotune-config filtering
  that keeps the kernels deterministic (`determinism`). Every linear-attention
  mixer needs these; `determinism` was already shared between the SSD kernels
  and the varlen convolution.
* `ops/mamba2/` -- the state-space duality kernels (`ssd_*`, `mamba_ssm`,
  `batch_invariant_decode`).

A later variant with its own forked kernels adds a sibling package next to
`mamba2` rather than growing the flat directory.

`intermediate_extraction` reads as Mamba-specific from its docstring, but the
kernels are a plain conditional gather/scatter over flat state rows with no
Mamba math in them, so it belongs with the shared code.

Pure move. The top-level `ops` re-exports are unchanged, so callers that go
through `megatron.core.ssm.ops` need no edit. Unit tests move to mirror the
new layout.

Signed-off-by: Keshav Santhanam <ksanthanam@nvidia.com>
@santhnm2
santhnm2 marked this pull request as ready for review August 11, 2026 19:39
@santhnm2
santhnm2 requested review from a team as code owners August 11, 2026 19:39
@santhnm2

Copy link
Copy Markdown
Contributor Author

/ok to test 43f1592

@svcnvidia-nemo-ci svcnvidia-nemo-ci added the Final Review PR is in the "final review" stage label Aug 11, 2026

@guihong-nv guihong-nv 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.

Nice, focused move overall. I left three comments on the bits that seem worth clearing up before it lands.

Comment thread megatron/core/ssm/mamba_mixer.py Outdated
Comment on lines +26 to +32
from megatron.core.ssm.ops.mamba2.batch_invariant_decode import MambaBatchInvariantDecode
from megatron.core.ssm.ops.common.causal_conv1d_triton import causal_conv1d_update
from megatron.core.ssm.ops.common.intermediate_extraction import (
scatter_intermediate_conv,
scatter_intermediate_ssm,
)
from megatron.core.ssm.ops.mamba_ssm import selective_state_update
from megatron.core.ssm.ops.mamba2.mamba_ssm import selective_state_update

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.

Could you run isort on this block? common sorts before mamba2, so the two common imports should come first. The lint job runs isort --check here, so this should fail as written.

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

@@ -0,0 +1,10 @@
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.

"""Triton kernels shared by every linear-attention / SSM mixer.

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.

Since this package is now described as shared, could you also update common/intermediate_extraction.py's module docstring? It still says “for Mamba intermediate-state extraction,” which makes the file sound Mamba-specific.

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.

Updated to clarify that the intermediate_extraction.py file is not specific to Mamba

* `mamba2` -- the Mamba2 state-space duality kernels, adapted from vLLM and
state-spaces/mamba.

The re-exports below are the historical top-level entry points and are kept so

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.

Are direct submodule imports intentionally considered internal? These re-exports preserve the top-level API, but imports like ops.mamba_ssm.selective_state_update and ops.intermediate_extraction.scatter_intermediate_* disappear without a shim or deprecation path. If those paths are public, could we keep thin re-export modules for a release? Otherwise, it would help to call out in the PR that they’re internal.

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.

I can include a backwards-compatible shim like this:

# Backwards-compatible aliases for the pre-split module paths, e.g.
# `megatron.core.ssm.ops.ssd_bmm` -> `megatron.core.ssm.ops.mamba2.ssd_bmm`.
# Registering them in `sys.modules` keeps `from megatron.core.ssm.ops.<old> import x`
# working, since importing any submodule imports this package first.
# Remove in megatron-core 0.20.
_MOVED_MODULES = {
    "batch_invariant_decode": "mamba2.batch_invariant_decode",
    "causal_conv1d_triton": "common.causal_conv1d_triton",
    "causal_conv1d_varlen": "common.causal_conv1d_varlen",
    "determinism": "common.determinism",
    "intermediate_extraction": "common.intermediate_extraction",
    "mamba_ssm": "mamba2.mamba_ssm",
    "ssd_bmm": "mamba2.ssd_bmm",
    "ssd_chunk_scan": "mamba2.ssd_chunk_scan",
    "ssd_chunk_state": "mamba2.ssd_chunk_state",
    "ssd_combined": "mamba2.ssd_combined",
    "ssd_state_passing": "mamba2.ssd_state_passing",
}


class _MovedModule(types.ModuleType):
    """Alias for a module that moved into a subpackage, warning on attribute access."""

    def __init__(self, old_name: str, new_name: str):
        super().__init__(old_name, f"Deprecated alias for {new_name}.")
        self._new_name = new_name

    def __getattr__(self, name: str):
        warnings.warn(
            f"{self.__name__} has moved to {self._new_name}. The old path will be removed
"
            "in megatron-core 0.20.",
            DeprecationWarning,
            stacklevel=2,
        )
        return getattr(importlib.import_module(self._new_name), name)


for _old, _new in _MOVED_MODULES.items():
    _old_path = f"{__name__}.{_old}"
    if _old_path not in sys.modules:
        sys.modules[_old_path] = _MovedModule(_old_path, f"{__name__}.{_new}")

I suspect there aren't actually any external consumers of these ops though, so I have a slight preference towards breaking the backwards compatibility. Either way is fine though, let me know which you prefer.

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.

Inference code at this point is considered internal. We disabled the backward compatibility check for Inference.

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.

Added a comment confirming this

@shanmugamr1992

shanmugamr1992 commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

NeMo-RL functional tests

cluster: oci-hsg · image: nvcr.io/nvidian/nemo-rl:nightly · updated 2026-08-12 06:28 UTC

Exactly what was tested

Component Ref Commit
megatron-core #6444 head e5c980ad
Megatron-Bridge refs/heads/main — an override, not the Bridge NeMo-RL pins 600d069b
NeMo-RL nrlta/integration e287604e

Compared against a baseline of the same suite on megatron-core main at 80cda5db.

Test Status Comment
grpo_megatron_generation ✅ pass
grpo_megatron_generation_topology ✅ pass
grpo_megatron_generation_non_colocated ✅ pass
grpo_megatron_generation_async ✅ pass
grpo_megatron_generation_colocated_async ✅ pass
grpo_megatron_generation_async_gym ✅ pass
grpo_megatron_generation_topp_topk ⚠️ fail (pre-existing) Pre-existing on megatron-core main, not caused by any labeled PR. grpo_megatron_generation_topp_topk runs to completion but max(train/token_mult_prob_error) lands at 1.078047513961792 against a < 1.06 threshold. The value is bit-identical on Megatron-LM#5382 head eb01b68 and on main 3aee84c (both with the Bridge FSDP fix), so it is a deterministic train/generation logprob mismatch in the top-p/top-k sampling path rather than run-to-run noise. Marginal at ~1.7% over. No fix raised; needs an owner to decide whether the sampling path regressed or the threshold is stale.
grpo_megatron_generation_colocated_gym ✅ pass
grpo_megatron_generation_async_prefix_caching ⚠️ fail (pre-existing) L2 nano-3.5 prefix-caching GRPO on megatron generation exceeds train/token_mult_prob_error max<1.1 on megatron-core main as well. Not attributed to a labeled Megatron-LM PR; needs a dedicated NeMo-RL/mcore investigation (threshold vs real logprob mismatch).
grpo_megatron_generation_async_mxfp8 ⚠️ fail (pre-existing) L2 nano-3.5 MXFP8 GRPO on megatron generation exceeds train/token_mult_prob_error max<1.5 on megatron-core main as well. Not attributed to a labeled Megatron-LM PR; needs a dedicated NeMo-RL/mcore investigation.

No failures attributed to this PR. The three red rows (L1 topp/topk and both L2 nano-3.5 tests) match known pre-existing token_mult_prob_error threshold breaks on megatron-core main. Megatron-Bridge was pinned to refs/heads/main because the NeMo-RL submodule pin still lags the merged FSDP factory fix (Bridge#5431 / open bump NVIDIA-NeMo/RL#3577). Integration branch carries the L2 Megatron functional suite (not yet on NeMo-RL main).

Posted by the nemo-rl-testing-agent. Re-runs edit this comment in place.

Signed-off-by: Keshav Santhanam <ksanthanam@nvidia.com>
Signed-off-by: Keshav Santhanam <ksanthanam@nvidia.com>
Signed-off-by: Keshav Santhanam <ksanthanam@nvidia.com>
@svcnvidia-nemo-ci svcnvidia-nemo-ci added Final Review PR is in the "final review" stage and removed Approved All necessary approvals have been made labels Aug 13, 2026
@santhnm2

Copy link
Copy Markdown
Contributor Author

/ok to test 5fec93f

@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/31731634391

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 13, 2026
@guihong-nv
guihong-nv added this pull request to the merge queue Aug 13, 2026
@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/31761228988

@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/31763897419

Merged via the queue into NVIDIA:main with commit de4a82e Aug 14, 2026
90 checks passed
ksivaman pushed a commit to ksivaman/Megatron-LM that referenced this pull request Sep 1, 2026
Signed-off-by: Keshav Santhanam <ksanthanam@nvidia.com>
Co-authored-by: Shanmugam Ramasamy <shanmugamr@nvidia.com>

This branch was previously deployed

2 inactive deployments
public — 5fec93fd Deployed Aug 13, 2026 by copy-pr-bot[bot] via finalize / publish-docs #6024
test — 5fec93fd Deployed Aug 13, 2026 by copy-pr-bot[bot] via cicd-wait-in-queue #18928
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complexity: low Final Review PR is in the "final review" stage NemoRLTest

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants