Skip to content

Commit 575b59f

Browse files
authored
🚨 Separate EP logic into its own file (#49679)
* the branch is now pointing to main and not to unify-resolve-plan * Refactor expert parallelism registration and improve documentation - Consolidated the registration of parallel styles into the `ParallelInterface` class for better organization. - Updated the `expert_parallel.py` and `tensor_parallel.py` files to streamline the registration process of expert parallel strategies. - Enhanced documentation for clarity regarding the usage of tensor parallel strategies and their registration. - Removed redundant code related to `MoeExpertsParallel` and `MoeIdentityParallel` classes, which are now properly registered in the new structure. * comment * some models missed the tests EP mixin * fix copyright
1 parent 4b3a3d7 commit 575b59f

26 files changed

Lines changed: 1032 additions & 671 deletions

‎.circleci/create_circleci_config.py‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ def job_name(self):
306306
torch_job = CircleCIJob(
307307
"torch",
308308
docker_image=[{"image": "huggingface/transformers-torch-light"}],
309-
marker="not (generate or is_training_test or is_tensor_parallel_test or is_fsdp_test)",
309+
marker="not (generate or is_training_test or is_tensor_parallel_test or is_expert_parallel_test or is_fsdp_test)",
310310
parallelism=6,
311311
)
312312

@@ -388,6 +388,15 @@ def job_name(self):
388388
parallelism=6,
389389
)
390390

391+
expert_parallel_ci_job = CircleCIJob(
392+
"expert_parallel_ci",
393+
additional_env={"RUN_EXPERT_PARALLEL_TESTS": True},
394+
docker_image=[{"image": "huggingface/transformers-torch-light"}],
395+
install_steps=["uv pip install ."],
396+
marker="is_expert_parallel_test",
397+
parallelism=6,
398+
)
399+
391400
fsdp_ci_job = CircleCIJob(
392401
"fsdp_ci",
393402
additional_env={"RUN_FSDP_TESTS": True},
@@ -434,9 +443,10 @@ def job_name(self):
434443
DOC_TESTS = [doc_test_job]
435444
TRAINING_CI_TESTS = [training_ci_job]
436445
TENSOR_PARALLEL_CI_TESTS = [tensor_parallel_ci_job]
446+
EXPERT_PARALLEL_CI_TESTS = [expert_parallel_ci_job]
437447
FSDP_CI_TESTS = [fsdp_ci_job]
438448
PEFT_INTEGRATION_TESTS = [peft_integration_job]
439-
ALL_TESTS = REGULAR_TESTS + PIPELINE_TESTS + REPO_UTIL_TESTS + DOC_TESTS + [custom_tokenizers_job] + [exotic_models_job] + TRAINING_CI_TESTS + TENSOR_PARALLEL_CI_TESTS + FSDP_CI_TESTS + PEFT_INTEGRATION_TESTS # fmt: skip
449+
ALL_TESTS = REGULAR_TESTS + PIPELINE_TESTS + REPO_UTIL_TESTS + DOC_TESTS + [custom_tokenizers_job] + [exotic_models_job] + TRAINING_CI_TESTS + TENSOR_PARALLEL_CI_TESTS + EXPERT_PARALLEL_CI_TESTS + FSDP_CI_TESTS + PEFT_INTEGRATION_TESTS # fmt: skip
440450

441451

442452
def create_circleci_config(folder=None):

‎docs/source/en/perf_infer_gpu_multi.md‎

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -101,35 +101,28 @@ print(model.tp_plan)
101101

102102
## Partitioning strategies
103103

104-
The `ParallelInterface` class maps each strategy name you can use in a `tp_plan` to a configured strategy instance. You don't interact with it directly to shard a model, but it's the authoritative list of available names.
104+
`ALL_PARALLEL_STYLES`, a `ParallelInterface` defined in [distributed/parallel_utils.py](https://github.com/huggingface/transformers/blob/main/src/transformers/distributed/parallel_utils.py), maps each strategy name you can use in a plan to a configured strategy instance. You don't interact with it directly to shard a model, but it's the authoritative list of available names. [distributed/tensor_parallel.py](https://github.com/huggingface/transformers/blob/main/src/transformers/distributed/tensor_parallel.py) registers the tensor parallel strategies into it:
105105

106106
```py
107-
class ParallelInterface(GeneralInterface):
108-
_global_mapping = {
109-
"embedding_rowwise": EmbeddingParallel(shard_dim=0),
110-
"embedding_colwise": EmbeddingParallel(shard_dim=1),
111-
"colwise_gather_output": ColwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()),
112-
"colwise_rep": ColwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()),
113-
"colwise": ColwiseParallel(input_layouts=Replicate(), output_layouts=Shard(-1)),
114-
"rowwise": RowwiseParallel(input_layouts=Shard(-1), output_layouts=Replicate()),
115-
"rowwise_split_input": RowwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()),
116-
"rowwise_rep": RowwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()),
117-
"packed_colwise": PackedColwiseParallel(),
118-
"packed_rowwise": PackedRowwiseParallel(),
119-
"sequence_parallel": SequenceParallel(use_local_output=True),
120-
"grouped_gemm": MoEParamShard(Shard(0), shards_expert_dim=True),
121-
"ep_router": EpRouterParallel(),
122-
"megamoe_router": RouterParallelMegaMoe(),
123-
"moe_tp_experts": MoeExpertsParallel(),
124-
"megamoe_experts": MoeTensorParalellMegaMoeExperts(),
125-
"moe_identity_expert": MoeIdentityParallel(),
126-
"replicated_with_grad_allreduce": ReplicatedWithGradAllReduce(),
127-
"mla_kv_a_proj": MlaKvAProjParallel(),
128-
"all_reduce": AllReduceParallel(),
129-
}
107+
ALL_PARALLEL_STYLES.register("embedding_rowwise", EmbeddingParallel(shard_dim=0))
108+
ALL_PARALLEL_STYLES.register("embedding_colwise", EmbeddingParallel(shard_dim=1))
109+
ALL_PARALLEL_STYLES.register("colwise_gather_output", ColwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
110+
ALL_PARALLEL_STYLES.register("colwise_rep", ColwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
111+
ALL_PARALLEL_STYLES.register("colwise", ColwiseParallel(input_layouts=Replicate(), output_layouts=Shard(-1)))
112+
ALL_PARALLEL_STYLES.register("rowwise", RowwiseParallel(input_layouts=Shard(-1), output_layouts=Replicate()))
113+
ALL_PARALLEL_STYLES.register("rowwise_split_input", RowwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
114+
ALL_PARALLEL_STYLES.register("rowwise_rep", RowwiseParallel(input_layouts=Replicate(), output_layouts=Replicate()))
115+
ALL_PARALLEL_STYLES.register("packed_colwise", PackedColwiseParallel())
116+
ALL_PARALLEL_STYLES.register("packed_rowwise", PackedRowwiseParallel())
117+
ALL_PARALLEL_STYLES.register("sequence_parallel", SequenceParallel(use_local_output=True))
118+
ALL_PARALLEL_STYLES.register("replicated_with_grad_allreduce", ReplicatedWithGradAllReduce())
119+
ALL_PARALLEL_STYLES.register("mla_kv_a_proj", MlaKvAProjParallel())
120+
ALL_PARALLEL_STYLES.register("all_reduce", AllReduceParallel())
121+
ALL_PARALLEL_STYLES.register("moe_tp_experts", MoeExpertsParallel())
122+
ALL_PARALLEL_STYLES.register("moe_identity_expert", MoeIdentityParallel())
130123
```
131124

132-
Every strategy is a subclass of `TensorParallelLayer` in [distributed/tensor_parallel.py](https://github.com/huggingface/transformers/blob/main/src/transformers/distributed/tensor_parallel.py). The two you'll reach for most, `ColwiseParallel` and `RowwiseParallel`, take `input_layouts` and `output_layouts` placements, which is how one class covers several plan names. `colwise` leaves its output sharded on the last dim for a following `rowwise` layer to consume, while `colwise_gather_output` all-gathers it back to a full tensor.
125+
Every tensor parallel strategy is a subclass of `TensorParallelLayer` in that file. The expert parallel strategies, such as `ep_dispatch_experts`, live in [distributed/expert_parallel.py](https://github.com/huggingface/transformers/blob/main/src/transformers/distributed/expert_parallel.py), subclass `ExpertParallelLayer`, which has the same hooks, and register into the same `ALL_PARALLEL_STYLES`. The two you'll reach for most, `ColwiseParallel` and `RowwiseParallel`, take `input_layouts` and `output_layouts` placements, which is how one class covers several plan names. `colwise` leaves its output sharded on the last dim for a following `rowwise` layer to consume, while `colwise_gather_output` all-gathers it back to a full tensor.
133126

134127
The table below describes each strategy.
135128

@@ -144,12 +137,12 @@ The table below describes each strategy.
144137
| `ReplicatedWithGradAllReduce` | Replicates a parameter but all-reduces its gradient. Needed for norms that sit between a column-wise and a row-wise layer and normalize along a sharded axis, where each rank only sees its own heads. |
145138
| `AllReduceParallel` | All-reduces a module's `Partial()` forward output to `Replicate()`. Use it as a sync point for a module whose compute ends in a partial sum. |
146139
| `MlaKvAProjParallel` | Splits the `kv_a_proj_with_mqa` output of DeepSeek-V2 style MLA attention and all-reduces the gradient of the RoPE half, which bypasses `kv_b_proj` and would otherwise keep a partial gradient. Requires `qk_rope_head_dim` in the model config. |
140+
| `MoeExpertsParallel` | Tensor parallel MoE experts. All-reduces the expert output forward and adds the backward all-reduces for hidden states and routing weights. |
141+
| `MoeIdentityParallel` | Pre-divides the input of a zero or identity expert by the mesh size, cancelling the all-reduce that `moe_tp_experts` applies downstream. |
147142
| `MoEParamShard` | Shards MoE expert weights on a given placement. Backs the `grouped_gemm` name, where `shards_expert_dim=True` also rewrites `module.num_experts` to the per-rank expert count. |
148143
| `EpRouterParallel` | Masks router scores for non-local experts and remaps global expert IDs to local ones, so each rank runs only the experts it owns. Requires `num_experts` to be divisible by the mesh size. |
149144
| `RouterParallelMegaMoe` | Router variant for DeepGEMM Mega MoE, which dispatches experts inside the kernel which wants the router output untouched. |
150-
| `MoeExpertsParallel` | Tensor parallel MoE experts. All-reduces the expert output forward and adds the backward all-reduces for hidden states and routing weights. |
151145
| `MoeTensorParalellMegaMoeExperts` | Inference-only experts layer for DeepGEMM Mega MoE. Skips the gradient syncs and passes the process group into the module so the kernel can set up its shared buffers on the first forward. |
152-
| `MoeIdentityParallel` | Pre-divides the input of a zero or identity expert by the mesh size, cancelling the all-reduce that `moe_tp_experts` applies downstream. |
153146

154147
### Packed strategies
155148

@@ -242,9 +235,9 @@ The example below walks through a trimmed version of `ColwiseParallel`.
242235
import torch
243236

244237
from transformers import AutoModelForCausalLM, DistributedConfig
245-
from transformers.distributed.tensor_parallel import ParallelInterface
238+
from transformers.distributed.parallel_utils import ALL_PARALLEL_STYLES
246239

247-
ParallelInterface.register("colwise_custom", ColwiseParallel())
240+
ALL_PARALLEL_STYLES.register("colwise_custom", ColwiseParallel())
248241
tp_plan = {
249242
"model.layers.*.self_attn.q_proj": "colwise_custom",
250243
...

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ markers = [
9393
"generate: marks tests that use the GenerationTesterMixin",
9494
"is_training_test: marks tests that use the TrainingTesterMixin (deselect with '-m \"not is_training_test\"')",
9595
"is_tensor_parallel_test: marks tests that use the TensorParallelTesterMixin (deselect with '-m \"not is_tensor_parallel_test\"')",
96+
"is_expert_parallel_test: marks tests that use the ExpertParallelTesterMixin (deselect with '-m \"not is_expert_parallel_test\"')",
9697
"is_fsdp_test: marks tests that use the FSDPTesterMixin (deselect with '-m \"not is_fsdp_test\"')",
9798
]
9899
log_cli = 1

0 commit comments

Comments
 (0)