[Megatron] Add SwiGLU integration - #1326
Conversation
Tcc0403
left a comment
There was a problem hiding this comment.
Thanks for the contribution, the kernel impl looks good.
However, there are plenty of new added functions to resolve environment issue with megatron-core installed in our test suite. I prefer moving them to another PR, ideally a proposal issue first, to address such issues.
|
|
||
| @triton.jit | ||
| def _swiglu_fused_gate_up_backward_kernel( | ||
| dc_ptr, y_ptr, dy_ptr, in_stride, out_stride, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr |
There was a problem hiding this comment.
| dc_ptr, y_ptr, dy_ptr, in_stride, out_stride, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr | |
| dc_ptr, y_ptr, dy_ptr, in_stride, out_stride, ffn_size: tl.constexpr, BLOCK_SIZE: tl.constexpr |
nit: name it ffn_size for readability
| two_n = ori_shape[-1] | ||
| if two_n % 2 != 0: | ||
| raise ValueError(f"fused gate-up input must have an even trailing dim; got {two_n}.") | ||
| n_cols = two_n // 2 |
| 2 × GPU (TP=2, PP=1) for 5 iterations and print the resolved norm classes | ||
| so you can see which slots picked up Liger. | ||
| 2 × GPU (TP=1, PP=1, DP=2) for 5 iterations and print the resolved norm | ||
| classes, CE bindings and SwiGLU bindings so you can see which slots picked |
There was a problem hiding this comment.
it would be better to create a op support matrix and append new op to it instead of inline new changes
| ) | ||
|
|
||
|
|
||
| def _ensure_dataset_helpers() -> None: |
There was a problem hiding this comment.
out of this PR scope? is it a required change to make this script work?
| ) | ||
|
|
||
|
|
||
| def _ensure_dataset_helpers() -> None: |
| ) | ||
|
|
||
|
|
||
| def _rebind_consumer_symbol( |
There was a problem hiding this comment.
I don't fully understand the purpose of this function. In what scenario do we need this rebind function, could you elaborate further?
Should we also apply it to ce and norm?
| # Force-import the submodule so liger_kernel.ops.swiglu can resolve | ||
| # torch.distributed.tensor.DTensor on torch 2.11+, where the subpackage is no longer | ||
| # auto-loaded as an attribute of torch.distributed. | ||
| import torch.distributed.tensor # noqa: F401 # isort: skip | ||
|
|
||
| from liger_kernel.ops import LigerFusedGateUpSiLUMulFunction |
There was a problem hiding this comment.
would from liger_kernel.ops import LigerFusedGateUpSiLUMulFunction fail without importing torch.distributed.tensor beforehand?
| _SAVED_REAL_MEGATRON: dict = {} | ||
| _STUBS_ACTIVE = False | ||
|
|
||
|
|
||
| def _is_megatron_module(name: str) -> bool: | ||
| return name == "megatron" or name.startswith("megatron.") | ||
|
|
||
|
|
||
| def _snapshot_real_megatron(): |
There was a problem hiding this comment.
is there any conflict with stub megatron when megatron-core is installed? would the test fail without these functions?
| # Unlike CE and RMSNorm, its consumers use by-name imports, so the patch has to rewrite | ||
| # their module attributes too; several tests below exist specifically to enforce that. |
There was a problem hiding this comment.
Follow CE and RMSNorm tests convention for self-consistent and maintainability. Issues in megatron-installed environment should be landed in another PR
| # --------------------------------------------------------------------------- | ||
| # 6. End-to-end validation against a real megatron-core install | ||
| # --------------------------------------------------------------------------- | ||
| # | ||
| # Everything above verifies the wrapper against our own reproduction of Megatron's | ||
| # formula. That cannot catch a wrong belief about Megatron's API. The tests below import | ||
| # the real package and pin: the true signature and positional order of | ||
| # ``bias_swiglu_impl``, numerical parity with Megatron's own TorchScript implementation, | ||
| # that the consumer module paths the monkey patch rebinds actually exist and are rebound, | ||
| # and that a real ``MLP`` produces identical output and gradients when patched. | ||
| # | ||
| # The import is conditional rather than a module-level ``pytest.importorskip`` so that the | ||
| # dependency-free tests above still run when megatron-core is absent. |
There was a problem hiding this comment.
Although this section is skipped when megatron-core is not installed, I still suggest removing it to keep this PR concise. Feel free to raise an issue if you plan to handle actual megatron-core environment in the test suite.
6f87072 to
9bfdbff
Compare
|
Hi @Tcc0403 , thanks for reviewing. The comments are pretty thorough, and I updated the code. A big change is instead of patching
|
Summary
Adds Liger's Triton SwiGLU to Megatron-Core. Megatron's dense MLP routes its activation through
bias_swiglu_implinmegatron.core.fusions.fused_bias_swiglu, a TorchScript@jit_fuserimplementation. This PR replaces it with a new fused gate-up Triton kernel written for Megatron's memory layout.At the Megatron training shape (
S=2048, B=4, bf16) on H100, againstmegatron-core==0.16.1:in_place=True)ffn_local=4096ffn_local=32768Background
Liger's existing
LigerSiLUMulFunctiontakes gate and up as two separate tensors (a,b), because that is how HuggingFace models arrive:gate_proj(x)andup_proj(x)are distinct GEMMs.Megatron is the opposite.
linear_fc1is a single fusedColumnParallelLinearproducing one[s, b, 2 * ffn_local]tensor with gate in columns[0, n)and up in[n, 2n). Bridging to the two-tensor kernel therefore requires atorch.chunk, andchunkon the last dimension returns non-contiguous views, so@ensure_contiguousmaterializes 2 full copies on every forward. We measured that bridge before writing anything new.ffn_local=32768bias_swiglu_implchunkbridgeThe chunk bridge is 3.4× slower than the fused kernel and also inflated memory by +40%. The new
LigerFusedGateUpSiLUMulFunctionreads both halves via a column offset into the single buffer, with no copies, nocat. Input row stride is2n, output row stride isn.How to use
Mode 1: One-line monkey patch
Patches
bias_swiglu_implacross Megatron's core modules (fused_bias_swiglu,mlp, andmoe.shared_experts).Mode 2: Use the class directly
The signature mirrors
bias_swiglu_implpositionally. To use Mode 2, subclass MLP (see examples/megatron/run_mode2_hand_spec.py).Optional:
in_place=Truefor a 20% memory reductionOverwrites the linear_fc1 output buffer during backward passes to reduce memory by 20% vs Megatron with zero speed penalty.
Scope
bias_swiglu_impl) and shared experts at any TP size withbias=None.bias is not None(Liger lacks bias support)fp8_input_store=True(requires FP8 input retention)cpu_offload_input=True(requires offloadable inputs)fallback_impl=Noneto raise an error instead of falling back.weighted_bias_swiglu_impl).What's added
src/liger_kernel/ops/swiglu.pyLigerFusedGateUpSiLUMulFunction+ fwd/bwd Triton kernels for the[tokens, 2n]layout. Existing two-tensor path untouched.src/liger_kernel/megatron/swiglu.pyLigerMegatronSwiGLUnn.Module, fallback policy, shape validation.src/liger_kernel/megatron/monkey_patch.pyswiglu=flag,_patch_bias_swiglu_impl(),_rebind_consumer_symbol().src/liger_kernel/megatron/__init__.pyLigerMegatronSwiGLU.test/megatron/test_swiglu.pymegatron-core.test/megatron/test_monkey_patch.pybenchmark/scripts/benchmark_megatron_swiglu.pyliger,liger_in_place,megatron,torch.examples/megatron/*Megatron feature compatibility
Verified: Tensor Parallelism (TP), fp32 + bf16, 2D and 3D inputs, distributed checkpointing (save/load round-trip in both example scripts), real
MLPforward+backward parity against Megatron's TorchScript, and both consumer module paths.Should work, not yet E2E-verified: multi-rank patch application (every rank must call
apply_...before model build — single-process patching is verified, the multi-rank ordering is not), CP,use_distributed_optimizer=True.Out of scope: activation recompute and CUDA-graph capture with
in_place=True— precisely why that flag is opt-in. The default path is recompute-safe.Known issues / follow-ups
in_place=Trueandretain_graph=Trueincompatibility: In-place Triton kernel writes don't bump PyTorch's_versioncounter, so running backward a second time silently yields garbage gradients without triggering autograd guards. We added actx.already_backwardflag to explicitly raise an error when reusing the graph within_place=True.weighted_bias_swiglu_implrequires aper_token_scale-aware kernel (left as a follow-up)._patch_local_spec_provider_layer_normcaused by forwarding an unsupported has_residual kwarg onmegatron-core 0.16.1. Signature matching is now version-agnostic viainspect.signature.megatron-core 0.16.1compatibility issues in the example scripts (AttributeError onas_mlp_submodule, Makefile build errors in pip wheels, and invalid tokenizer metadata).Testing Done
pytest test/megatron/withmegatron-coreinstalled — 175 passed, 49 skippedpytest test/megatron/withoutmegatron-core(default CI shape) — 168 passed, 56 skippedpytest test/transformers/test_swiglu.py— 49 passed, 4 skipped, 4 xfailed (existing two-tensor SwiGLU not regressed by the sharedops/swiglu.pychange)make checkstyleto ensure code style — clean;prek run -aalso cleanall_benchmark_data.csvmake test-convergenceto ensure convergenceBenchmark reproduction
python benchmark/scripts/benchmark_megatron_swiglu.py --overwrite python benchmark/benchmarks_visualizer.py \ --kernel-name megatron_swiglu --metric-name speed --overwrite