Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions megatron/core/datasets/data_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import torch

from megatron.core import parallel_state
from megatron.core.pipeline_parallel.hybrid_cp_schedule import BalancedCPScheduler
from megatron.core.pipeline_parallel.dynamic_cp_schedule import BalancedCPScheduler
from megatron.core.process_groups_config import ProcessGroupCollection


class HybridCPDataLoaderWrapper:
class DynamicCPDataLoaderWrapper:
"""
A wrapper class that wraps around an existing data_iterator.
For every __next__ call,
Expand Down Expand Up @@ -40,7 +40,7 @@ def __init__(
self.tp_group = pg_collection.tp
assert (
self.dp_cp_group is not None and self.dp_group is not None and self.tp_group is not None
), "dp_cp_group, dp_group, tp_group must not be None when using hybrid context parallel"
), "dp_cp_group, dp_group, tp_group must not be None when using dynamic context parallel"

self.cp_balancing_scheduler = BalancedCPScheduler(
max_seq_len_per_rank=self.config.max_seqlen_per_dp_cp_rank, dp_cp_group=self.dp_cp_group
Expand Down
4 changes: 2 additions & 2 deletions megatron/core/datasets/gpt_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ class GPTDatasetConfig(BlendedMegatronDatasetConfig):
Set to 0 if sequence parallel is not enabled regardless of TP size.
"""

hybrid_context_parallel: bool = False
"""Option to enable hybrid context parallelism. When setting this to True,
dynamic_context_parallel: bool = False
"""Option to enable dynamic context parallelism. When setting this to True,
each sample should be divisible by the data parallel size * context parallel size * 2.
If sequence parallel is enabled, it should be divisible by the
data parallel size * context parallel size * sequence parallel size * 2.
Expand Down
26 changes: 11 additions & 15 deletions megatron/core/extensions/transformer_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -1363,21 +1363,17 @@ def forward(
"""Forward."""
if packed_seq_params is not None:
# If Dynamic CP group is provided, update TE DPA CP group
if packed_seq_params.cp_group is not None:
self.cp_group = packed_seq_params.cp_group
super().set_context_parallel_group(
self.cp_group,
torch.distributed.get_process_group_ranks(self.cp_group),
TEDotProductAttention.cp_stream,
self.cp_comm_type,
)
# If cp_group is None but local_cp_size is provided,
# Indicates to turn off CP dynamically
elif packed_seq_params.local_cp_size is not None:
assert (
packed_seq_params.local_cp_size == 1
), "local_cp_size must be == 1 if provided without cp_group"
super().set_context_parallel_group(None, None, None, self.cp_comm_type)
if packed_seq_params.local_cp_size is not None:
if packed_seq_params.local_cp_size == 1:
super().set_context_parallel_group(None, None, None, self.cp_comm_type)
else:
self.cp_group = packed_seq_params.cp_group
super().set_context_parallel_group(
self.cp_group,
torch.distributed.get_process_group_ranks(self.cp_group),
TEDotProductAttention.cp_stream,
self.cp_comm_type,
)
self.kept_packed_seq_params.discard("cp_group")
self.kept_packed_seq_params.discard("local_cp_size")

Expand Down
6 changes: 3 additions & 3 deletions megatron/core/model_parallel_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ class ModelParallelConfig:
each rank when using hybrid_context_parallel.
"""

hybrid_context_parallel: bool = False
dynamic_context_parallel: bool = False
"""
If true, enables hybrid context parallel. This is used to balance the workload of
If true, enables dynamic context parallel. This is used to balance the workload of
each CP rank when we use packed samples with variable sequence lengths.
Please set max_seqlen_per_dp_cp_rank when using hybrid_context_parallel.
Please set max_seqlen_per_dp_cp_rank when using dynamic_context_parallel.
"""

expert_model_parallel_size: int = 1
Expand Down
60 changes: 38 additions & 22 deletions megatron/core/parallel_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@
_CONTEXT_PARALLEL_GLOBAL_RANKS = None
# Hierarchical context parallel groups
_HIERARCHICAL_CONTEXT_PARALLEL_GROUPS = None
# Hybrid context parallel groups
_HYBRID_DP_CP_GROUPS = {}
# Dynamic context parallel groups
_DYNAMIC_DP_CP_GROUPS = {}

# Data parallel group information with context parallel combined.
_DATA_PARALLEL_GROUP_WITH_CP = None
Expand Down Expand Up @@ -420,29 +420,29 @@ def create_hierarchical_groups(
return hierarchical_groups, hierarchical_groups_gloo


def create_hybrid_dp_cp_groups(rank, ranks, pg_options):
def create_dynamic_dp_cp_groups(rank, ranks, pg_options):
"""
Creates groups required for hybrid DPxCP.
Creates groups required for dynamic DPxCP.
Creates a new group for every power of 2 up to the number of DPxCP ranks.
Returns a dictionary indexed by group size.
"""
hybrid_dp_cp_groups = {}
dynamic_dp_cp_groups = {}
# Generate group for every power of 2 up to the number of CP ranks
# We limit the allowed group sizes in order to avoid excessive overhead.
group_sizes = [2**i for i in range(int(log2(len(ranks))))][1:]
group_sizes = [2**i for i in range(int(log2(len(ranks))))]
for group_size in group_sizes:
for i in range(0, len(ranks), group_size):
group = create_group(
ranks[i : i + group_size],
pg_options=pg_options,
group_desc=f"HYBRID_DP_CP_GROUP_{group_size}",
group_desc=f"DYNAMIC_DP_CP_GROUP_{group_size}",
)
if rank in ranks[i : i + group_size]:
assert (
group_size not in hybrid_dp_cp_groups
), f"Rank {rank} appears in multiple Hybrid DP CP groups of size {group_size}"
hybrid_dp_cp_groups[group_size] = group
return hybrid_dp_cp_groups
group_size not in dynamic_dp_cp_groups
), f"Rank {rank} appears in multiple Dynamic DP CP groups of size {group_size}"
dynamic_dp_cp_groups[group_size] = group
return dynamic_dp_cp_groups


class RankGenerator(object):
Expand Down Expand Up @@ -554,7 +554,7 @@ def initialize_model_parallel(
use_sharp: bool = False,
context_parallel_size: int = 1,
hierarchical_context_parallel_sizes: Optional[List[int]] = None,
hybrid_context_parallel: bool = False,
dynamic_context_parallel: bool = False,
expert_model_parallel_size: int = 1,
num_distributed_optimizer_instances: int = 1,
expert_tensor_parallel_size: Optional[int] = None,
Expand Down Expand Up @@ -940,18 +940,34 @@ def initialize_model_parallel(
if "NCCL_COLLNET_ENABLE" in os.environ:
del os.environ["NCCL_COLLNET_ENABLE"]

if hybrid_context_parallel:
global _HYBRID_DP_CP_GROUPS
if dynamic_context_parallel:
# TODO: Are gloo groups needed for Dynamic CP?
global _DYNAMIC_DP_CP_GROUPS
for ranks_with_cp in decoder_rank_generator.get_ranks('dp-cp'):
assert (
len(ranks_with_cp) % 2 == 0
), "Hybrid context parallel requires an even number of ranks"
_HYBRID_DP_CP_GROUPS.update(
create_hybrid_dp_cp_groups(
), "Dynamic context parallel requires an even number of ranks"
_DYNAMIC_DP_CP_GROUPS.update(
create_dynamic_dp_cp_groups(
rank, ranks_with_cp, get_nccl_options("dp_cp", nccl_comm_cfgs)
)
)
# TODO: Are gloo groups needed for hybrid cp?

# PyTorch is performing lazy initialization of the communicator group.
# Therefore, we need to perform a nccl call to ensure that the communicator group is created.
group_sizes = [
2**i
for i in range(
0, int(log2(data_parallel_size))
)
]
if group_sizes[-1] * 2 == data_parallel_size:
group_sizes.append(data_parallel_size)
for group_size in group_sizes:
group = get_dynamic_data_context_parallel_groups(group_size=group_size)
torch.distributed.barrier(group=group, device_ids=[torch.cuda.current_device()])
torch.cuda.synchronize()


for ranks in decoder_rank_generator.get_ranks('dp'):
group = create_group(
Expand Down Expand Up @@ -1475,16 +1491,16 @@ def get_hierarchical_context_parallel_groups(check_initialized=True):
return _HIERARCHICAL_CONTEXT_PARALLEL_GROUPS


def get_hybrid_data_context_parallel_groups(check_initialized=True, group_size=None):
"""Get the hybrid context parallel groups the caller rank belongs to."""
def get_dynamic_data_context_parallel_groups(check_initialized=True, group_size=None):
"""Get the dynamic context parallel groups the caller rank belongs to."""
# If the group size is the same as the entire DPxCP group, return the original group
if get_data_parallel_world_size(with_context_parallel=True) == group_size:
if check_initialized:
assert _DATA_PARALLEL_GROUP_WITH_CP is not None
return _DATA_PARALLEL_GROUP_WITH_CP
if check_initialized:
assert _HYBRID_DP_CP_GROUPS is not None
return _HYBRID_DP_CP_GROUPS[group_size]
assert _DYNAMIC_DP_CP_GROUPS is not None
return _DYNAMIC_DP_CP_GROUPS[group_size]


def get_embedding_group(check_initialized=True):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def gpus_needed(self, seq_len: int) -> int:
This is used to determine the CP size of a sub-sample.

The number is rounded up to the next power of 2 to match the available
hybrid context parallel process group sizes.
dynamic context parallel process group sizes.
"""
return max(1, 2 ** ceil(log2((seq_len / self.max_seq_len_per_rank))))

Expand Down Expand Up @@ -370,7 +370,7 @@ def fill_empty_gpus(
"try to increase 'max-seqlen-per-cp-rank'."

min_group_size = min(existing_group_sizes)
# We have Hybrid DPxCP groups for every power of 2 of GPUs or the entire DPxCP group.
# We have Dynamic DPxCP groups for every power of 2 of GPUs or the entire DPxCP group.
next_power = min(min_group_size * 2, total_gpus)

# Find the first group of min_group_size that can be expanded
Expand Down Expand Up @@ -474,7 +474,7 @@ def get_groups_and_subsamples(self, sample_id_seqlens, config):
return groups, sample_id_groups


def hybrid_context_parallel_forward_backward(
def dynamic_context_parallel_forward_backward(
forward_step_func,
data_iterator,
model,
Expand All @@ -492,7 +492,7 @@ def hybrid_context_parallel_forward_backward(
model_type,
):
"""
Scheduler for Hybrid Context Parallel.
Scheduler for Dynamic Context Parallel.

This function performs the packed sample scheduling and determines
1. The number of microbatches to schedule for each CP rank
Expand Down
6 changes: 3 additions & 3 deletions megatron/core/pipeline_parallel/schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
combined_1f1b_schedule_for_interleaved_pipelining,
combined_1f1b_schedule_for_no_pipelining,
)
from .hybrid_cp_schedule import hybrid_context_parallel_forward_backward
from .dynamic_cp_schedule import dynamic_context_parallel_forward_backward

# Types
Shape = Union[List[int], torch.Size]
Expand Down Expand Up @@ -615,8 +615,8 @@ def forward_backward_no_pipelining(
total_num_tokens,
partial(check_first_val_step, first_val_step, forward_only),
)
elif config.hybrid_context_parallel:
forward_data_store, total_num_tokens = hybrid_context_parallel_forward_backward(
elif config.dynamic_context_parallel:
forward_data_store, total_num_tokens = dynamic_context_parallel_forward_backward(
forward_step_func,
data_iterator,
model,
Expand Down
5 changes: 5 additions & 0 deletions megatron/core/transformer/attention.py
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,11 @@ def forward(
(Tuple[Tensor, Tensor]) Attention output and bias.

"""
# here we need to set the right cp group for dynamic-cp
if packed_seq_params is not None and packed_seq_params.local_cp_size is not None:
assert packed_seq_params.cp_group is not None, "cp_group must be set in dynamic-cp mode"
self.pg_collection.cp = packed_seq_params.cp_group

# Check if we need to skip RoPE
# no_rope is 0-indexed array and self.layer_number is 1-indexed
no_rope = (
Expand Down
4 changes: 2 additions & 2 deletions megatron/core/transformer/multi_latent_attention.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,8 @@ def get_query_key_value_tensors(
if packed_seq_params is not None:
assert (
packed_seq_params.local_cp_size is None
), "hybrid_context_parallel is not supported with MLA yet and is planned for future. \
Please disable hybrid_context_parallel."
), "dynamic_context_parallel is not supported with MLA yet and is planned for future. \
Please disable dynamic_context_parallel."

inference_context = deprecate_inference_params(inference_context, inference_params)

Expand Down
21 changes: 10 additions & 11 deletions megatron/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2168,11 +2168,11 @@ def get_thd_batch_on_this_cp_rank(


################################
### hybrid context parallel ###
### dynamic context parallel ###
################################


def get_batch_on_this_hybrid_cp_rank(
def get_batch_on_this_dynamic_cp_rank(
batch: Dict[str, Any],
local_cp_size: int,
cp_group: Optional[torch.distributed.ProcessGroup] = None,
Expand All @@ -2182,18 +2182,17 @@ def get_batch_on_this_hybrid_cp_rank(
"""
assert local_cp_size is not None
if cp_group is None:
# Get the local cp group required for as defined by the HybridCPDataLoaderWrapper
if local_cp_size > 1:
cp_group = parallel_state.get_hybrid_data_context_parallel_groups(
group_size=local_cp_size
)
# Get the local cp group required for as defined by the DynamicCPDataLoaderWrapper
cp_group = parallel_state.get_dynamic_data_context_parallel_groups(
group_size=local_cp_size
)
else:
# If cp group is provided, it must match the local cp size
# as defined by the HybridCPDataLoaderWrapper
# as defined by the DynamicCPDataLoaderWrapper
assert cp_group.size() == local_cp_size

# Convert [seqlen] to [1, seqlen] similar to default collate_fn
# as hybrid_context_parallel dataloader wrapper does not go through default collate_fn
# as dynamic_context_parallel dataloader wrapper does not go through default collate_fn
for key, data in batch.items():
if key in ['attention_mask']:
continue
Expand All @@ -2213,8 +2212,8 @@ def get_batch_on_this_hybrid_cp_rank(
cp_group=cp_group,
)

if cp_group is not None and cp_group.size() > 1:
# When using hybrid_context_parallel, each sub-sample of a packed sample is
if cp_group.size() > 1:
# When using dynamic_context_parallel, each sub-sample of a packed sample is
# required to be divisible by CP*DP*2 or CP*DP*TP*2 (if using sequence parallel)
batch = get_batch_on_this_cp_rank(batch, cp_group=cp_group)

Expand Down
12 changes: 6 additions & 6 deletions megatron/training/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -1150,12 +1150,12 @@ def validate_args(args, defaults={}):
if args.tp_comm_overlap:
assert args.sequence_parallel == True, 'Tensor parallel communication/GEMM overlap can happen only when sequence parallelism is enabled'

if args.hybrid_context_parallel:
assert not args.pipeline_model_parallel_size > 1, 'Hybrid context parallelism not supported with pipeline parallelism'
assert not args.enable_cuda_graph, 'Hybrid context parallelism not supported with CUDA Graph'
assert not args.use_megatron_fsdp, 'Hybrid context parallelism not supported with Megatron FSDP'
assert args.dataloader_type == 'single', 'Hybrid context parallelism only supported with single dataloader type'
assert args.calculate_per_token_loss, 'Hybrid context parallelism must be used with --calculate-per-token-loss'
if args.dynamic_context_parallel:
assert not args.pipeline_model_parallel_size > 1, 'Dynamic context parallelism not supported with pipeline parallelism'
assert not args.enable_cuda_graph, 'Dynamic context parallelism not supported with CUDA Graph'
assert not args.use_megatron_fsdp, 'Dynamic context parallelism not supported with Megatron FSDP'
assert args.dataloader_type == 'single', 'Dynamic context parallelism only supported with single dataloader type'
assert args.calculate_per_token_loss, 'Dynamic context parallelism must be used with --calculate-per-token-loss'

# disable async_tensor_model_parallel_allreduce when
# model parallel memory optimization is enabled
Expand Down
12 changes: 6 additions & 6 deletions megatron/training/datasets/data_samplers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def build_pretraining_data_loader(dataset, consumed_samples):
data_parallel_size=mpu.get_data_parallel_world_size(),
)
elif args.dataloader_type == 'single':
if args.hybrid_context_parallel:
batch_sampler = HybridCPMegatronPretrainingSampler(
if args.dynamic_context_parallel:
batch_sampler = DynamicCPMegatronPretrainingSampler(
total_samples=len(dataset),
consumed_samples=consumed_samples,
micro_batch_size=args.micro_batch_size,
Expand Down Expand Up @@ -79,7 +79,7 @@ def worker_init_fn(_):
worker_init_fn if args.exit_signal_handler and args.num_workers > 0 else None
)
# Torch dataloader.
if args.hybrid_context_parallel:
if args.dynamic_context_parallel:
extra_kwargs = {"collate_fn": lambda x: x,}
else:
extra_kwargs = {}
Expand Down Expand Up @@ -162,11 +162,11 @@ def __iter__(self):
start_idx, end_idx = self.get_start_end_idx()
yield batch[start_idx:end_idx]

class HybridCPMegatronPretrainingSampler(MegatronPretrainingSampler):
class DynamicCPMegatronPretrainingSampler(MegatronPretrainingSampler):
"""
Data sampler for hybrid context parallel (Hybrid CP) format.
Data sampler for dynamic context parallel (Dynamic CP) format.
This data sampler pulls in the entire global batch at once across all data parallel ranks.
This helps provide the Hybrid CP Dataloader Wrapper to schedule and load balance sub-samples
This helps provide the Dynamic CP Dataloader Wrapper to schedule and load balance sub-samples
of the entire global batch.
"""

Expand Down
2 changes: 1 addition & 1 deletion megatron/training/initialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def _initialize_distributed(get_embedding_ranks, get_position_embedding_ranks, s
use_sharp=args.use_sharp,
context_parallel_size=args.context_parallel_size,
hierarchical_context_parallel_sizes=args.hierarchical_context_parallel_sizes,
hybrid_context_parallel=args.hybrid_context_parallel,
dynamic_context_parallel=args.dynamic_context_parallel,
expert_model_parallel_size=args.expert_model_parallel_size,
num_distributed_optimizer_instances=args.num_distributed_optimizer_instances,
expert_tensor_parallel_size=args.expert_tensor_parallel_size,
Expand Down
Loading