Skip to content

Commit 602347f

Browse files
authored
fix sglang expert sharding (#95)
<!-- **Thanks for contributing to Awex.** **If this is your first time opening a PR on Awex, you can refer to [CONTRIBUTING.md](https://github.com/inclusionAI/asystem-awex/blob/main/CONTRIBUTING.md).** Contribution Checklist - The **Awex** community has requirements on the naming of pr titles. You can also find instructions in [CONTRIBUTING.md](https://github.com/inclusionAI/asystem-awex/blob/main/CONTRIBUTING.md). --> ## What does this PR do? <!-- Describe the details of this PR. --> ## Related issues Closes #94 ## Does this PR introduce any user-facing change? <!-- If any user-facing interface changes, please [open an issue](https://github.com/inclusionAI/asystem-awex/issues/new/choose) describing the need to do so and update the document if necessary. Delete section if not applicable. --> - [ ] Does this PR introduce any public API change? - [ ] Does this PR introduce any binary protocol compatibility change?
1 parent af31295 commit 602347f

6 files changed

Lines changed: 33 additions & 42 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ import sglang as sgl
117117

118118
sgl_engine = sgl.Engine(model_path="xxx", tp_size=2, random_seed=42)
119119
awex_config = InferenceConfig.from_sgl_engine(sgl_engine, comm_backend="nccl")
120-
# for sglang support, you must ensure https://github.com/sgl-project/sglang/pull/13595
120+
# for sglang support, you must ensure https://github.com/sgl-project/sglang/pull/13595
121121
# is included in your sglang version
122122
inference_engine = SGLangEngine(awex_config, sgl_engine)
123123
reader = WeightsReader(inference_engine)

awex/config.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,9 @@ class InferenceConfig:
3636
ep_size: Optional[int] = None
3737
enable_dp_attention: Optional[bool] = None
3838
enable_dp_lm_head: Optional[bool] = None
39-
enable_ep_moe: Optional[bool] = None
40-
enable_deepep_moe: Optional[bool] = None
4139
deepep_mode: Optional[Literal["auto", "normal", "low_latency"]] = None
4240
ep_num_redundant_experts: Optional[int] = None
4341
enable_eplb: Optional[bool] = None
44-
eplb_rebalance_num_iterations: Optional[int] = None
4542
enable_memory_saver: Optional[bool] = None
4643
moe_dense_tp_size: Optional[int] = None
4744
n_share_experts_fusion: Optional[int] = None

awex/converter/sglang_converter.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def __init__(
3535
self.total_kv_heads = model_config.num_key_value_heads
3636
self.infer_engine_config = infer_engine_config
3737
self.rank_info = rank_info
38-
self.enable_ep_moe = infer_engine_config.enable_ep_moe
3938
self.tp_size = infer_engine_config.tp_size
4039
self.tp_rank = self.rank_info.tp_rank
4140
self.ep_size = infer_engine_config.ep_size
@@ -205,7 +204,6 @@ def _convert_expert_moe_param(
205204
self, name: str, parameter: torch.Tensor, layer_number: str
206205
) -> List[Tuple[str, torch.Tensor]]:
207206
"""Convert expert parameters from SGlang to HuggingFace format."""
208-
assert self.enable_ep_moe, "EP mode must be enabled"
209207
# w13_weight shape: num_experts_per_partition, 2 * intermediate_size, hidden_size
210208
# w2_weight shape: num_experts_per_partition, hidden_size, intermediate_size
211209
if "expert_bias" in name:

awex/sharding/sglang_sharding.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def get_sglang_sharding_strategy(
3434
moe_dense_tp_size=infer_engine_config.moe_dense_tp_size,
3535
tp_size=rank_info.tp_size,
3636
ep_size=infer_engine_config.ep_size,
37-
ep_tp_size=1,
37+
ep_tp_size=rank_info.ep_tp_size,
3838
rank_info=rank_info,
3939
**kwargs,
4040
)
@@ -50,21 +50,15 @@ def get_sglang_rank_info(model_context, engine_rank) -> RankInfo:
5050
tp_size = model_context["tp_size"]
5151
tp_rank = model_context["tp_rank"]
5252
ep_size = infer_engine_config.ep_size
53-
if (
54-
infer_engine_config.enable_ep_moe
55-
or infer_engine_config.enable_deepep_moe
56-
or (
57-
hasattr(infer_engine_config, "enable_pplx_moe")
58-
and infer_engine_config.enable_pplx_moe
59-
)
60-
):
61-
assert ep_size == tp_size, "ep_size must be equal to tp_size"
62-
ep_rank = tp_rank
53+
if ep_size > 1:
54+
ep_tp_size = tp_size // ep_size
55+
ep_tp_rank = tp_rank % ep_tp_size
56+
ep_rank = tp_rank // ep_tp_size
6357
else:
6458
assert ep_size == 1, "ep_size must be 1"
6559
ep_rank = 0
66-
ep_tp_size = 1
67-
ep_tp_rank = 0
60+
ep_tp_size = 1
61+
ep_tp_rank = 0
6862
return RankInfo(
6963
tp_rank=tp_rank,
7064
tp_size=tp_size,

awex/tests/test_meta_resolver.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,6 @@ def __init__(self):
108108
server_args.enable_dp_lm_head = False
109109
server_args.moe_dense_tp_size = 1
110110
server_args.ep_size = 1
111-
server_args.enable_ep_moe = False
112-
server_args.enable_deepep_moe = False
113-
server_args.enable_pplx_moe = False
114111

115112
self.engine = MagicMock()
116113
self.engine.server_args = server_args

docs/README.md

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,44 @@ The core functional modules of weight exchange consist mainly of 5 parts:
2323
- **RDMA weight transmission**: Uses NUMA affinity and RDMA communication for globally load-balanced transfer plan for weight updates;
2424

2525
### (1) Unified Training-Inference Weight Convert
26+
2627
Due to different computational workloads, training and inference engines generally adopt different parallelism strategies. Megatron training engine uses 5D parallelism strategy, DeepSpeed/FSDP uses Zero + DP data parallelism, while SGLang and VLLM inference engines mostly use DP + TP + EP. Additionally, different engines perform **fusion, transposition, and quantization** optimizations on weights after loading to adapt to high-performance operators.
2728

2829
To eliminate differences between different engines for subsequent weight exchange, Awex constructs a **unified weight convert layer** that performs the following converts:
2930

30-
+ **Weight splitting**: Splits merged weights (such as FFN's gate/up) into independent weights, supporting cross-TP Resharding;
31-
+ **Weight name unification**: Converts all internal weights from all engines to the same namespace, establishing weight mapping relationships between training and inference engines;
32-
+ **Attention weight ReGroup**: On the training engine side, regroups and aligns QKV weights along the inference engine's TP/DPAttention parallelism strategy, avoiding shard explosion from fine-grained splitting;
33-
+ **Quantization, precision, and format conversion**: Automatically converts weights on the training side **according to the precision and format of the inference side**, and this low-precision conversion can also reduce the amount of transmitted data.
31+
- **Weight splitting**: Splits merged weights (such as FFN's gate/up) into independent weights, supporting cross-TP Resharding;
32+
- **Weight name unification**: Converts all internal weights from all engines to the same namespace, establishing weight mapping relationships between training and inference engines;
33+
- **Attention weight ReGroup**: On the training engine side, regroups and aligns QKV weights along the inference engine's TP/DPAttention parallelism strategy, avoiding shard explosion from fine-grained splitting;
34+
- **Quantization, precision, and format conversion**: Automatically converts weights on the training side **according to the precision and format of the inference side**, and this low-precision conversion can also reduce the amount of transmitted data.
3435

3536
The entire weight convert adaptation layer is implemented as a **pluggable structure** that can be fully customized at the engine layer, model weight convert, and sharding layer to meet the customization needs of complex scenarios.
3637

3738
### (2) Global Weight Metadata Management
39+
3840
Each training and inference process needs to **be aware of the weight metadata of all training and inference processes globally** for constructing subsequent weight transfer plan. Awex also performs **consistency validation of weight metadata between training and inference** at this step. The main workflow is as follows:
3941

40-
+ Each process in the training engine performs weight convert and obtains metadata for the converted shards
41-
+ Through all_gather_object, each rank obtains global training shard metadata
42-
+ Rank0 on the training side serializes global metadata and reports it to Meta Server
43-
+ Inference instance 0 on the inference side performs similar work; other inference instances have identical metadata and don't need additional computation
44-
+ All training and inference processes obtain global metadata from MetaServer
45-
+ Training and inference engines each perform shard-level metadata consistency and compatibility validation
42+
- Each process in the training engine performs weight convert and obtains metadata for the converted shards
43+
- Through all_gather_object, each rank obtains global training shard metadata
44+
- Rank0 on the training side serializes global metadata and reports it to Meta Server
45+
- Inference instance 0 on the inference side performs similar work; other inference instances have identical metadata and don't need additional computation
46+
- All training and inference processes obtain global metadata from MetaServer
47+
- Training and inference engines each perform shard-level metadata consistency and compatibility validation
4648

4749
### (3) P2P Weight Transmission Execution Plan
50+
4851
After obtaining global weight metadata, Awex constructs a **deterministic point-to-point transmission plan** within each training and inference process.
4952

5053
**Core Strategy** (NCCL mode):
5154

52-
+ For each replica of the same tensor shard, assign training shards to inference shards through Round Robin to ensure uniform pulling;
53-
+ For overlapping shard intervals, if perfectly aligned, directly map; otherwise, use two sends to different shards;
54-
+ Pre-filter shards related to the current process to avoid constructing a global plan (shards can reach tens of millions for trillion-parameter models);
55-
+ Ensure strict order consistency of NCCL send/recv;
55+
- For each replica of the same tensor shard, assign training shards to inference shards through Round Robin to ensure uniform pulling;
56+
- For overlapping shard intervals, if perfectly aligned, directly map; otherwise, use two sends to different shards;
57+
- Pre-filter shards related to the current process to avoid constructing a global plan (shards can reach tens of millions for trillion-parameter models);
58+
- Ensure strict order consistency of NCCL send/recv;
5659

5760
RDMA is more flexible than NCCL and uses a separate transmission plan, which we will expand on in subsequent articles.
5861

5962
### (4) NCCL Weight Transmission
63+
6064
Awex supports two transmission modes: NCCL (NVIDIA Collective Communications Library) and RDMA (Remote Direct Memory Access). NCCL mode is more user-friendly, while RDMA mode is more flexible with higher performance.
6165

6266
NCCL transmission mode primarily uses NCCL's send/recv interface for weight transmission. There are some implementation differences in Awex for separated and co-located modes, which we will detail here.
@@ -81,12 +85,13 @@ In this case, Awex uses **CUDA IPC to zero-copy map the training process's GPU m
8185

8286
In implementation, we have also made some **performance optimizations**:
8387

84-
+ **Problem**: Each CUDA IPC Handle's Open/Close has significant overhead; MOE and other models may have thousands to tens of thousands of weight tensors per card requiring IPC serialization;
85-
+ **Solution**: Before IPC serialization, merge tensors by shape and dtype, reducing the count to dozens, greatly reducing CUDA IPC overhead;
88+
- **Problem**: Each CUDA IPC Handle's Open/Close has significant overhead; MOE and other models may have thousands to tens of thousands of weight tensors per card requiring IPC serialization;
89+
- **Solution**: Before IPC serialization, merge tensors by shape and dtype, reducing the count to dozens, greatly reducing CUDA IPC overhead;
8690

8791
(**Note**: CUDA IPC does not support CUDA virtual memory. Future plans include allocating additional physical memory space for weight merging and transmission when enabling virtual GPU memory in the training engine)
8892

8993
### (5) RDMA Weight Transmission
94+
9095
Although NCCL transmission mode can already significantly improve weight exchange performance, NCCL mode has two main limitations:
9196

9297
1. **NCCL versions on training and inference sides need to remain compatible**, otherwise NCCL transmission may hang, preventing independent updates and iterations of training and inference engines;
@@ -100,9 +105,9 @@ Considering these two reasons, we also developed an RDMA-based transmission impl
100105

101106
**RDMA Mode Advantages**:
102107

103-
+ Removes NCCL version binding, supports independent iteration of training and inference engines
104-
+ More flexible transmission plan optimization space
105-
+ Supports dynamic scaling of inference instances
106-
+ Further performance improvement (1T model from 20 seconds to 6 seconds)
108+
- Removes NCCL version binding, supports independent iteration of training and inference engines
109+
- More flexible transmission plan optimization space
110+
- Supports dynamic scaling of inference instances
111+
- Further performance improvement (1T model from 20 seconds to 6 seconds)
107112

108113
RDMA mode implementation will be open-sourced soon. Stay tuned.

0 commit comments

Comments
 (0)