Override Plugin Mechanism#
This document describes how to use the @overridable / register() plugin system in FlagScale, which supports replacing megatron.core (Megatron-LM-FL side) and megatron.training (FlagScale side) implementations.
Three replacement scenarios are supported:
Replacing a class method
Replacing a module-level function
Replacing an entire class
Core Concepts#
Role |
Description |
|---|---|
|
Decorates a function/method/class in megatron core, marking it as replaceable by a plugin |
|
Declares target → impl mapping in |
Plugin Implementation |
The actual replacement logic, written under the corresponding path in |
1. Replacing a Class Method#
Scenario: Replace a single method in a class while keeping other methods unchanged.
Core Side — Mark as Overridable#
# megatron/core/optimizer/optimizer.py
from megatron.plugin.decorators import overridable
class MixedPrecisionOptimizer:
def __init__(self, ...):
...
@overridable
def _unscale_main_grads_and_check_for_nan(self):
"""Original implementation"""
# ... original logic ...
return found_inf_flag
Register Mapping#
# megatron/plugin/override_registry.py
from megatron.plugin.decorators import register
register(
target="megatron.core.optimizer.optimizer.MixedPrecisionOptimizer._unscale_main_grads_and_check_for_nan",
impl="megatron.plugin.optimizer.optimizer._unscale_main_grads_and_check_for_nan",
)
Plugin Side — Implement Replacement Function#
# megatron/plugin/optimizer/optimizer.py
import torch
def _unscale_main_grads_and_check_for_nan(self):
"""Plugin implementation: supports CPU communication and multi-group mode"""
if not self.is_stub_optimizer:
main_grads = self._collect_main_grad_data_for_unscaling()
self.found_inf.fill_(0.0)
if not self.is_stub_optimizer:
torch._amp_foreach_non_finite_check_and_unscale_(
main_grads, self.found_inf, self.grad_scaler.inv_scale
)
# Custom: support list-type groups
groups = self.get_grad_stats_parallel_group()
if not isinstance(groups, list):
groups = [groups]
for group in groups:
torch.distributed.all_reduce(
self.found_inf, op=torch.distributed.ReduceOp.MAX, group=group
)
return self.found_inf.item() > 0
Note: When replacing a class method, the first parameter of the plugin function must be
self, which receives the instance of the original class.
2. Replacing a Module-Level Function#
Scenario: Replace a standalone function in a module.
Core Side — Mark as Overridable#
# megatron/core/optimizer/clip_grads.py
from megatron.plugin.decorators import overridable
@overridable
def get_grad_norm_fp32(
grads_for_norm,
norm_type=2,
grad_stats_parallel_group=None,
):
"""Original implementation"""
# ... original logic ...
return total_norm
Register Mapping#
# megatron/plugin/override_registry.py
from megatron.plugin.decorators import register
register(
target="megatron.core.optimizer.clip_grads.get_grad_norm_fp32",
impl="megatron.plugin.optimizer.clip_grads.get_grad_norm_fp32",
)
Plugin Side — Implement Replacement Function#
# megatron/plugin/optimizer/clip_grads.py
import torch
def get_grad_norm_fp32(grads_for_norm, norm_type=2, grad_stats_parallel_group=None):
"""Plugin implementation: supports list-type parallel groups and CPU communication"""
if isinstance(grads_for_norm, torch.Tensor):
grads_for_norm = [grads_for_norm]
# ... custom grad norm calculation logic ...
return total_norm
3. Replacing an Entire Class#
Scenario: Completely replace an original class with a new class. All places that instantiate the original class automatically receive the replacement class.
Core Side — Mark as Overridable#
# megatron/core/optimizer/lr_scheduler.py
from megatron.plugin.decorators import overridable
@overridable
class CosineAnnealingLR:
def __init__(self, optimizer, max_steps, min_lr=0.0):
self.optimizer = optimizer
self.max_steps = max_steps
self.min_lr = min_lr
self.current_step = 0
def step(self):
"""Cosine annealing"""
import math
progress = self.current_step / self.max_steps
lr = self.min_lr + 0.5 * (1 + math.cos(math.pi * progress))
for group in self.optimizer.param_groups:
group['lr'] = lr
self.current_step += 1
def get_lr(self):
return self.optimizer.param_groups[0]['lr']
Register Mapping#
# megatron/plugin/override_registry.py
from megatron.plugin.decorators import register
register(
target="megatron.core.optimizer.lr_scheduler.CosineAnnealingLR",
impl="megatron.plugin.optimizer.lr_scheduler.WSDScheduler",
)
Plugin Side — Implement Replacement Class#
# megatron/plugin/optimizer/lr_scheduler.py
from megatron.core.optimizer.lr_scheduler import CosineAnnealingLR
class WSDScheduler(CosineAnnealingLR):
"""Plugin implementation: Warmup-Stable-Decay scheduler"""
def __init__(self, optimizer, max_steps, min_lr=0.0, warmup_steps=1000):
super().__init__(optimizer, max_steps, min_lr)
self.warmup_steps = warmup_steps
def step(self):
if self.current_step < self.warmup_steps:
# Warmup phase
lr = (self.current_step / self.warmup_steps)
elif self.current_step < self.max_steps * 0.9:
# Stable phase
lr = 1.0
else:
# Decay phase
decay_progress = (self.current_step - self.max_steps * 0.9) / (self.max_steps * 0.1)
lr = max(self.min_lr, 1.0 * (0.5 ** decay_progress))
for group in self.optimizer.param_groups:
group['lr'] = lr
self.current_step += 1
Requirement: The replacement class must inherit from the original class to ensure
isinstance(obj, CosineAnnealingLR)still returnsTrue.
Transparent to Callers#
# Business code requires no modification
from megatron.core.optimizer.lr_scheduler import CosineAnnealingLR
scheduler = CosineAnnealingLR(optimizer, max_steps=10000)
# Actually receives a WSDScheduler instance
scheduler.step()
Multi-Vendor Support#
Multiple vendor implementations can be registered for the same target, selected via environment variable:
# override_registry.py
register(
target="megatron.core.optimizer.clip_grads.get_grad_norm_fp32",
impl="megatron.plugin.optimizer.clip_grads.get_grad_norm_fp32",
)
register(
target="megatron.core.optimizer.clip_grads.get_grad_norm_fp32",
impl="megatron.plugin.optimizer.clip_grads.get_grad_norm_fp32_musa",
vendor="musa",
)
Runtime selection:
export MG_FL_PREFER=musa # Use MUSA vendor implementation
When MG_FL_PREFER is not set, the vendor="default" implementation is used.
method_key Generation Rules#
The target parameter of register() is automatically converted to an internal method_key:
target path |
method_key |
|---|---|
|
|
|
|
|
|
Rules:
Module-level function / Class: module basename before the last segment +
.+ nameClass method: If the second-to-last segment starts with an uppercase letter, it is treated as a class name →
ClassName.method_name
Quick Start Checklist#
Add
@overridableto the target in core codeAdd
register(...)inmegatron/plugin/override_registry.pyWrite the implementation under the corresponding path in
megatron/plugin/(pure function or subclass, no@overridedecorator needed)Done — takes effect automatically at runtime