Skip to content

Commit 9df495f

Browse files
perf(ckpt): build remaining split indices on the input device (#6347)
Signed-off-by: Eugenio "Jay" Zuccarelli <11176606+jayzuccarelli@users.noreply.github.com>
1 parent 2c75856 commit 9df495f

2 files changed

Lines changed: 75 additions & 21 deletions

File tree

‎src/megatron/bridge/models/conversion/param_mapping.py‎

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,11 +2575,13 @@ def hf_to_megatron(
25752575
d_tot_ssm = config.mamba_state_dim * config.mamba_num_groups
25762576

25772577
# Define component indices in the concatenated tensor
2578-
z_shard_idx = torch.arange(d_inner)
2579-
x_shard_idx = torch.arange(d_inner, 2 * d_inner)
2580-
B_shard_idx = torch.arange(2 * d_inner, 2 * d_inner + d_tot_ssm)
2581-
C_shard_idx = torch.arange(2 * d_inner + d_tot_ssm, 2 * d_inner + 2 * d_tot_ssm)
2582-
dt_shard_idx = torch.arange(2 * (d_inner + d_tot_ssm), 2 * (d_inner + d_tot_ssm) + config.mamba_num_heads)
2578+
z_shard_idx = torch.arange(d_inner, device=hf_weights.device)
2579+
x_shard_idx = torch.arange(d_inner, 2 * d_inner, device=hf_weights.device)
2580+
B_shard_idx = torch.arange(2 * d_inner, 2 * d_inner + d_tot_ssm, device=hf_weights.device)
2581+
C_shard_idx = torch.arange(2 * d_inner + d_tot_ssm, 2 * d_inner + 2 * d_tot_ssm, device=hf_weights.device)
2582+
dt_shard_idx = torch.arange(
2583+
2 * (d_inner + d_tot_ssm), 2 * (d_inner + d_tot_ssm) + config.mamba_num_heads, device=hf_weights.device
2584+
)
25832585

25842586
# Reshape for tensor parallel distribution
25852587
target_shape = (self.tp_size, -1, config.hidden_size)
@@ -2625,11 +2627,15 @@ def megatron_to_hf(
26252627
n_heads_local = config.mamba_num_heads // self.tp_size
26262628

26272629
# Extract local components
2628-
z_shard_idx = torch.arange(d_inner_local)
2629-
x_shard_idx = torch.arange(d_inner_local) + d_inner_local
2630-
B_shard_idx = torch.arange(d_tot_ssm_local) + 2 * d_inner_local
2631-
C_shard_idx = torch.arange(d_tot_ssm_local) + 2 * d_inner_local + d_tot_ssm_local
2632-
dt_shard_idx = torch.arange(n_heads_local) + 2 * (d_inner_local + d_tot_ssm_local)
2630+
z_shard_idx = torch.arange(d_inner_local, device=megatron_weights.device)
2631+
x_shard_idx = torch.arange(d_inner_local, device=megatron_weights.device) + d_inner_local
2632+
B_shard_idx = torch.arange(d_tot_ssm_local, device=megatron_weights.device) + 2 * d_inner_local
2633+
C_shard_idx = (
2634+
torch.arange(d_tot_ssm_local, device=megatron_weights.device) + 2 * d_inner_local + d_tot_ssm_local
2635+
)
2636+
dt_shard_idx = torch.arange(n_heads_local, device=megatron_weights.device) + 2 * (
2637+
d_inner_local + d_tot_ssm_local
2638+
)
26332639

26342640
local_components = [
26352641
megatron_weights[z_shard_idx],
@@ -2668,7 +2674,9 @@ def __init__(self, megatron_param: str, hf_param: str):
26682674
self._tp_mapping = ColumnParallelMapping(megatron_param, megatron_param)
26692675

26702676
@abstractmethod
2671-
def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
2677+
def get_shard_idx(
2678+
self, config: TransformerConfig, local_tp: bool, device: torch.device | None = None
2679+
) -> List[int]:
26722680
"""Get shard indices for the given config."""
26732681
...
26742682

@@ -2688,7 +2696,7 @@ def hf_to_megatron(
26882696
assert "bias" in self.megatron_param, "Only bias and weight are supported for conv1d"
26892697
target_shape = (self.tp_size, -1)
26902698

2691-
shard_idx = self.get_shard_idx(config, local_tp=False)
2699+
shard_idx = self.get_shard_idx(config, local_tp=False, device=hf_weights.device)
26922700

26932701
# Extract and reshape components
26942702
sharded_weights = [hf_weights[idx].reshape(target_shape) for idx in shard_idx]
@@ -2723,7 +2731,7 @@ def megatron_to_hf(
27232731
config = remove_non_pickleables(config, max_depth=3)
27242732
config = self.broadcast_obj_from_pp_rank(config, cache_key="config")
27252733

2726-
shard_idx = self.get_shard_idx(config, local_tp=True)
2734+
shard_idx = self.get_shard_idx(config, local_tp=True, device=megatron_weights.device)
27272735

27282736
local_components = [megatron_weights[idx] for idx in shard_idx]
27292737

@@ -2747,17 +2755,19 @@ class GDNConv1dMapping(ChunkedMapping):
27472755
tensor-parallel distributed format for GDN SSM layers.
27482756
"""
27492757

2750-
def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
2758+
def get_shard_idx(
2759+
self, config: TransformerConfig, local_tp: bool, device: torch.device | None = None
2760+
) -> List[int]:
27512761
"""Get shard indices for the given config."""
27522762
qk_dim = config.linear_key_head_dim * config.linear_num_key_heads
27532763
v_dim = config.linear_value_head_dim * config.linear_num_value_heads
27542764
if local_tp:
27552765
qk_dim = qk_dim // self.tp_size
27562766
v_dim = v_dim // self.tp_size
27572767

2758-
q_shard_idx = torch.arange(qk_dim)
2759-
k_shard_idx = torch.arange(qk_dim) + qk_dim
2760-
v_shard_idx = torch.arange(v_dim) + qk_dim * 2
2768+
q_shard_idx = torch.arange(qk_dim, device=device)
2769+
k_shard_idx = torch.arange(qk_dim, device=device) + qk_dim
2770+
v_shard_idx = torch.arange(v_dim, device=device) + qk_dim * 2
27612771

27622772
return [q_shard_idx, k_shard_idx, v_shard_idx]
27632773

@@ -2769,7 +2779,9 @@ class MambaConv1dMapping(ChunkedMapping):
27692779
tensor-parallel distributed format for Mamba SSM layers.
27702780
"""
27712781

2772-
def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
2782+
def get_shard_idx(
2783+
self, config: TransformerConfig, local_tp: bool, device: torch.device | None = None
2784+
) -> List[int]:
27732785
"""Get shard indices for the given config."""
27742786
d_inner = config.mamba_num_heads * config.mamba_head_dim
27752787
d_tot_ssm = config.mamba_state_dim * config.mamba_num_groups
@@ -2778,9 +2790,9 @@ def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
27782790
d_tot_ssm = d_tot_ssm // self.tp_size
27792791

27802792
# Extract local components
2781-
x_shard_idx = torch.arange(d_inner)
2782-
B_shard_idx = torch.arange(d_tot_ssm) + d_inner
2783-
C_shard_idx = torch.arange(d_tot_ssm) + d_inner + d_tot_ssm
2793+
x_shard_idx = torch.arange(d_inner, device=device)
2794+
B_shard_idx = torch.arange(d_tot_ssm, device=device) + d_inner
2795+
C_shard_idx = torch.arange(d_tot_ssm, device=device) + d_inner + d_tot_ssm
27842796

27852797
return [x_shard_idx, B_shard_idx, C_shard_idx]
27862798

‎tests/unit_tests/models/test_param_mapping.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
FusedExpertMapping,
3030
FusedGatedExpertMapping,
3131
GatedMLPMapping,
32+
GDNConv1dMapping,
3233
KVMapping,
34+
MambaConv1dMapping,
35+
MambaInProjMapping,
3336
MegatronParamMapping,
3437
QKVMapping,
3538
ReplicatedMapping,
@@ -802,6 +805,45 @@ def test_megatron_to_hf(self, mock_distributed_env, transformer_config):
802805
assert torch.equal(result["v.weight"], v)
803806

804807

808+
class TestSSMMappings:
809+
config = SimpleNamespace(
810+
hidden_size=8,
811+
mamba_num_heads=4,
812+
mamba_head_dim=2,
813+
mamba_state_dim=2,
814+
mamba_num_groups=1,
815+
linear_key_head_dim=2,
816+
linear_num_key_heads=2,
817+
linear_value_head_dim=2,
818+
linear_num_value_heads=2,
819+
)
820+
821+
@pytest.mark.parametrize(
822+
"mapping, hf_weight",
823+
[
824+
(MambaInProjMapping("mixer.in_proj.weight", "hf.in_proj.weight"), torch.randn(24, 8)),
825+
(MambaConv1dMapping("mixer.conv1d.weight", "hf.conv1d.weight"), torch.randn(12, 1, 4)),
826+
(MambaConv1dMapping("mixer.conv1d.bias", "hf.conv1d.bias"), torch.randn(12)),
827+
(GDNConv1dMapping("mixer.conv1d.weight", "hf.conv1d.weight"), torch.randn(12, 1, 4)),
828+
],
829+
)
830+
def test_round_trip_builds_shard_idx_on_input_device(self, mock_distributed_env, mapping, hf_weight):
831+
mock_distributed_env()
832+
megatron_module = MockModule(self.config, weight_shape=hf_weight.shape)
833+
834+
with (
835+
patch.object(mapping._tp_mapping, "hf_to_megatron", side_effect=lambda weight, module: weight),
836+
patch("torch.arange", wraps=torch.arange) as mock_arange,
837+
):
838+
megatron_weight = mapping.hf_to_megatron(hf_weight, megatron_module)
839+
result = mapping.megatron_to_hf(megatron_weight, megatron_module)
840+
841+
assert torch.equal(result[mapping.hf_param], hf_weight)
842+
assert mock_arange.call_count > 0
843+
for call in mock_arange.call_args_list:
844+
assert call.kwargs.get("device") == hf_weight.device
845+
846+
805847
class TestGatedMLPMapping:
806848
def test_hf_to_megatron_single_tp(self, mock_distributed_env, transformer_config):
807849
"""Test gate+up merging with single TP rank."""

0 commit comments

Comments
 (0)