Skip to content

Commit 2f31c32

Browse files
committed
perf(ckpt): build remaining split indices on the input device
Follow-up to #6236: create the Mamba in_proj and chunked conv1d shard index tensors on the weight's device instead of on CPU. Signed-off-by: Eugenio "Jay" Zuccarelli <11176606+jayzuccarelli@users.noreply.github.com>
1 parent 4778c23 commit 2f31c32

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
@@ -2532,11 +2532,13 @@ def hf_to_megatron(
25322532
d_tot_ssm = config.mamba_state_dim * config.mamba_num_groups
25332533

25342534
# Define component indices in the concatenated tensor
2535-
z_shard_idx = torch.arange(d_inner)
2536-
x_shard_idx = torch.arange(d_inner, 2 * d_inner)
2537-
B_shard_idx = torch.arange(2 * d_inner, 2 * d_inner + d_tot_ssm)
2538-
C_shard_idx = torch.arange(2 * d_inner + d_tot_ssm, 2 * d_inner + 2 * d_tot_ssm)
2539-
dt_shard_idx = torch.arange(2 * (d_inner + d_tot_ssm), 2 * (d_inner + d_tot_ssm) + config.mamba_num_heads)
2535+
z_shard_idx = torch.arange(d_inner, device=hf_weights.device)
2536+
x_shard_idx = torch.arange(d_inner, 2 * d_inner, device=hf_weights.device)
2537+
B_shard_idx = torch.arange(2 * d_inner, 2 * d_inner + d_tot_ssm, device=hf_weights.device)
2538+
C_shard_idx = torch.arange(2 * d_inner + d_tot_ssm, 2 * d_inner + 2 * d_tot_ssm, device=hf_weights.device)
2539+
dt_shard_idx = torch.arange(
2540+
2 * (d_inner + d_tot_ssm), 2 * (d_inner + d_tot_ssm) + config.mamba_num_heads, device=hf_weights.device
2541+
)
25402542

25412543
# Reshape for tensor parallel distribution
25422544
target_shape = (self.tp_size, -1, config.hidden_size)
@@ -2582,11 +2584,15 @@ def megatron_to_hf(
25822584
n_heads_local = config.mamba_num_heads // self.tp_size
25832585

25842586
# Extract local components
2585-
z_shard_idx = torch.arange(d_inner_local)
2586-
x_shard_idx = torch.arange(d_inner_local) + d_inner_local
2587-
B_shard_idx = torch.arange(d_tot_ssm_local) + 2 * d_inner_local
2588-
C_shard_idx = torch.arange(d_tot_ssm_local) + 2 * d_inner_local + d_tot_ssm_local
2589-
dt_shard_idx = torch.arange(n_heads_local) + 2 * (d_inner_local + d_tot_ssm_local)
2587+
z_shard_idx = torch.arange(d_inner_local, device=megatron_weights.device)
2588+
x_shard_idx = torch.arange(d_inner_local, device=megatron_weights.device) + d_inner_local
2589+
B_shard_idx = torch.arange(d_tot_ssm_local, device=megatron_weights.device) + 2 * d_inner_local
2590+
C_shard_idx = (
2591+
torch.arange(d_tot_ssm_local, device=megatron_weights.device) + 2 * d_inner_local + d_tot_ssm_local
2592+
)
2593+
dt_shard_idx = torch.arange(n_heads_local, device=megatron_weights.device) + 2 * (
2594+
d_inner_local + d_tot_ssm_local
2595+
)
25902596

25912597
local_components = [
25922598
megatron_weights[z_shard_idx],
@@ -2625,7 +2631,9 @@ def __init__(self, megatron_param: str, hf_param: str):
26252631
self._tp_mapping = ColumnParallelMapping(megatron_param, megatron_param)
26262632

26272633
@abstractmethod
2628-
def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
2634+
def get_shard_idx(
2635+
self, config: TransformerConfig, local_tp: bool, device: torch.device | None = None
2636+
) -> List[int]:
26292637
"""Get shard indices for the given config."""
26302638
...
26312639

@@ -2645,7 +2653,7 @@ def hf_to_megatron(
26452653
assert "bias" in self.megatron_param, "Only bias and weight are supported for conv1d"
26462654
target_shape = (self.tp_size, -1)
26472655

2648-
shard_idx = self.get_shard_idx(config, local_tp=False)
2656+
shard_idx = self.get_shard_idx(config, local_tp=False, device=hf_weights.device)
26492657

26502658
# Extract and reshape components
26512659
sharded_weights = [hf_weights[idx].reshape(target_shape) for idx in shard_idx]
@@ -2680,7 +2688,7 @@ def megatron_to_hf(
26802688
config = remove_non_pickleables(config, max_depth=3)
26812689
config = self.broadcast_obj_from_pp_rank(config, cache_key="config")
26822690

2683-
shard_idx = self.get_shard_idx(config, local_tp=True)
2691+
shard_idx = self.get_shard_idx(config, local_tp=True, device=megatron_weights.device)
26842692

26852693
local_components = [megatron_weights[idx] for idx in shard_idx]
26862694

@@ -2704,17 +2712,19 @@ class GDNConv1dMapping(ChunkedMapping):
27042712
tensor-parallel distributed format for GDN SSM layers.
27052713
"""
27062714

2707-
def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
2715+
def get_shard_idx(
2716+
self, config: TransformerConfig, local_tp: bool, device: torch.device | None = None
2717+
) -> List[int]:
27082718
"""Get shard indices for the given config."""
27092719
qk_dim = config.linear_key_head_dim * config.linear_num_key_heads
27102720
v_dim = config.linear_value_head_dim * config.linear_num_value_heads
27112721
if local_tp:
27122722
qk_dim = qk_dim // self.tp_size
27132723
v_dim = v_dim // self.tp_size
27142724

2715-
q_shard_idx = torch.arange(qk_dim)
2716-
k_shard_idx = torch.arange(qk_dim) + qk_dim
2717-
v_shard_idx = torch.arange(v_dim) + qk_dim * 2
2725+
q_shard_idx = torch.arange(qk_dim, device=device)
2726+
k_shard_idx = torch.arange(qk_dim, device=device) + qk_dim
2727+
v_shard_idx = torch.arange(v_dim, device=device) + qk_dim * 2
27182728

27192729
return [q_shard_idx, k_shard_idx, v_shard_idx]
27202730

@@ -2726,7 +2736,9 @@ class MambaConv1dMapping(ChunkedMapping):
27262736
tensor-parallel distributed format for Mamba SSM layers.
27272737
"""
27282738

2729-
def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
2739+
def get_shard_idx(
2740+
self, config: TransformerConfig, local_tp: bool, device: torch.device | None = None
2741+
) -> List[int]:
27302742
"""Get shard indices for the given config."""
27312743
d_inner = config.mamba_num_heads * config.mamba_head_dim
27322744
d_tot_ssm = config.mamba_state_dim * config.mamba_num_groups
@@ -2735,9 +2747,9 @@ def get_shard_idx(self, config: TransformerConfig, local_tp: bool) -> List[int]:
27352747
d_tot_ssm = d_tot_ssm // self.tp_size
27362748

27372749
# Extract local components
2738-
x_shard_idx = torch.arange(d_inner)
2739-
B_shard_idx = torch.arange(d_tot_ssm) + d_inner
2740-
C_shard_idx = torch.arange(d_tot_ssm) + d_inner + d_tot_ssm
2750+
x_shard_idx = torch.arange(d_inner, device=device)
2751+
B_shard_idx = torch.arange(d_tot_ssm, device=device) + d_inner
2752+
C_shard_idx = torch.arange(d_tot_ssm, device=device) + d_inner + d_tot_ssm
27412753

27422754
return [x_shard_idx, B_shard_idx, C_shard_idx]
27432755

‎tests/unit_tests/models/test_param_mapping.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
FusedExpertMapping,
2929
FusedGatedExpertMapping,
3030
GatedMLPMapping,
31+
GDNConv1dMapping,
3132
KVMapping,
33+
MambaConv1dMapping,
34+
MambaInProjMapping,
3235
QKVMapping,
3336
ReplicatedMapping,
3437
RMSNorm2ZeroCenteredRMSNormMapping,
@@ -611,6 +614,45 @@ def test_megatron_to_hf(self, mock_distributed_env, transformer_config):
611614
assert torch.equal(result["v.weight"], v)
612615

613616

617+
class TestSSMMappings:
618+
config = SimpleNamespace(
619+
hidden_size=8,
620+
mamba_num_heads=4,
621+
mamba_head_dim=2,
622+
mamba_state_dim=2,
623+
mamba_num_groups=1,
624+
linear_key_head_dim=2,
625+
linear_num_key_heads=2,
626+
linear_value_head_dim=2,
627+
linear_num_value_heads=2,
628+
)
629+
630+
@pytest.mark.parametrize(
631+
"mapping, hf_weight",
632+
[
633+
(MambaInProjMapping("mixer.in_proj.weight", "hf.in_proj.weight"), torch.randn(24, 8)),
634+
(MambaConv1dMapping("mixer.conv1d.weight", "hf.conv1d.weight"), torch.randn(12, 1, 4)),
635+
(MambaConv1dMapping("mixer.conv1d.bias", "hf.conv1d.bias"), torch.randn(12)),
636+
(GDNConv1dMapping("mixer.conv1d.weight", "hf.conv1d.weight"), torch.randn(12, 1, 4)),
637+
],
638+
)
639+
def test_round_trip_builds_shard_idx_on_input_device(self, mock_distributed_env, mapping, hf_weight):
640+
mock_distributed_env()
641+
megatron_module = MockModule(self.config, weight_shape=hf_weight.shape)
642+
643+
with (
644+
patch.object(mapping._tp_mapping, "hf_to_megatron", side_effect=lambda weight, module: weight),
645+
patch("torch.arange", wraps=torch.arange) as mock_arange,
646+
):
647+
megatron_weight = mapping.hf_to_megatron(hf_weight, megatron_module)
648+
result = mapping.megatron_to_hf(megatron_weight, megatron_module)
649+
650+
assert torch.equal(result[mapping.hf_param], hf_weight)
651+
assert mock_arange.call_count > 0
652+
for call in mock_arange.call_args_list:
653+
assert call.kwargs.get("device") == hf_weight.device
654+
655+
614656
class TestGatedMLPMapping:
615657
def test_hf_to_megatron_single_tp(self, mock_distributed_env, transformer_config):
616658
"""Test gate+up merging with single TP rank."""

0 commit comments

Comments
 (0)