main
py 289 lines 9.27 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Factory for weight transfer engines with lazy loading."""
4
5 import importlib
6 from collections.abc import Callable
7 from typing import TYPE_CHECKING
8
9 from vllm.distributed.weight_transfer.base import (
10 TrainerWeightTransferEngine,
11 WeightTransferEngine,
12 )
13 from vllm.logger import init_logger
14
15 if TYPE_CHECKING:
16 import torch
17
18 from vllm.config import VllmConfig
19 from vllm.config.weight_transfer import WeightTransferConfig
20 from vllm.distributed.weight_transfer.base import (
21 TrainerInitInfo,
22 VLLMWeightSyncClient,
23 WeightSource,
24 )
25
26 logger = init_logger(__name__)
27
28
29 class WeightTransferEngineFactory:
30 """Factory for creating weight transfer engines with lazy loading.
31
32 This factory implements a registry pattern that supports:
33 - Lazy loading: Engine modules are only imported when actually needed
34 - Extensibility: Custom engines can be registered at runtime
35 - Centralized registration: All built-in engines registered in one place
36 """
37
38 _registry: dict[str, Callable[[], type[WeightTransferEngine]]] = {}
39
40 @classmethod
41 def register_engine(
42 cls,
43 name: str,
44 module_path_or_cls: str | type[WeightTransferEngine],
45 class_name: str | None = None,
46 ) -> None:
47 """Register an engine with lazy-loading or direct class reference.
48
49 Supports two calling conventions:
50 1. Lazy loading: register_engine(name, module_path, class_name)
51 2. Direct class: register_engine(name, engine_cls)
52
53 Args:
54 name: The name to register the engine under (e.g., "nccl")
55 module_path_or_cls: Either a module path string for lazy loading,
56 or the engine class directly
57 class_name: Name of the engine class (required if module_path is string)
58
59 Raises:
60 ValueError: If an engine with the same name is already registered
61
62 """
63 if name in cls._registry:
64 raise ValueError(f"Weight transfer engine '{name}' is already registered.")
65
66 if isinstance(module_path_or_cls, str):
67 # Lazy loading path
68 module_path = module_path_or_cls
69 if class_name is None:
70 raise ValueError(
71 "class_name is required when registering with module path"
72 )
73
74 def loader() -> type[WeightTransferEngine]:
75 module = importlib.import_module(module_path)
76 return getattr(module, class_name)
77
78 cls._registry[name] = loader
79 else:
80 # Direct class registration
81 engine_cls = module_path_or_cls
82 cls._registry[name] = lambda: engine_cls
83
84 @classmethod
85 def create_engine(
86 cls,
87 config: "WeightTransferConfig",
88 vllm_config: "VllmConfig",
89 device: "torch.device",
90 model: "torch.nn.Module",
91 ) -> WeightTransferEngine:
92 """Create a weight transfer engine instance.
93
94 Args:
95 config: Weight transfer configuration containing the backend name
96 vllm_config: The full vLLM config (provides parallel/model config)
97 device: The device this worker's model lives on
98 model: The local model instance which will receive the weights
99
100 Returns:
101 An initialized weight transfer engine instance
102
103 Raises:
104 ValueError: If the backend is not registered
105
106 """
107 backend = config.backend
108 if backend not in cls._registry:
109 available = list(cls._registry.keys())
110 raise ValueError(
111 f"Invalid weight transfer backend: {backend}. "
112 f"Available engines: {available}"
113 )
114 engine_cls = cls._registry[backend]()
115
116 logger.info(
117 "Creating weight transfer engine: %s",
118 engine_cls.__name__,
119 )
120
121 return engine_cls(config, vllm_config, device, model)
122
123
124 class WeightTransferTrainerFactory:
125 """Factory for creating trainer-side weight transfer engines.
126
127 Parallel to `WeightTransferEngineFactory`, with its own lazy-import
128 registry. The trainer-side and worker-side registries are kept separate:
129 they share backend names by convention, but the trainer process never
130 instantiates a worker engine and vice versa, so unifying them would only
131 couple the import graphs.
132 """
133
134 _registry: dict[str, Callable[[], type[TrainerWeightTransferEngine]]] = {}
135
136 @classmethod
137 def register_engine(
138 cls,
139 name: str,
140 module_path_or_cls: "str | type[TrainerWeightTransferEngine]",
141 class_name: str | None = None,
142 ) -> None:
143 """Register a trainer engine. Same conventions as
144 `WeightTransferEngineFactory.register_engine`."""
145 if name in cls._registry:
146 raise ValueError(
147 f"Weight transfer trainer engine '{name}' is already registered."
148 )
149
150 if isinstance(module_path_or_cls, str):
151 module_path = module_path_or_cls
152 if class_name is None:
153 raise ValueError(
154 "class_name is required when registering with module path"
155 )
156
157 def loader() -> type[TrainerWeightTransferEngine]:
158 module = importlib.import_module(module_path)
159 return getattr(module, class_name)
160
161 cls._registry[name] = loader
162 else:
163 engine_cls = module_path_or_cls
164 cls._registry[name] = lambda: engine_cls
165
166 @classmethod
167 def trainer_init(
168 cls,
169 init_info: "TrainerInitInfo",
170 *,
171 client: "VLLMWeightSyncClient",
172 source: "WeightSource | None" = None,
173 ) -> TrainerWeightTransferEngine:
174 """Build and rendezvous a ready-to-send trainer engine.
175
176 Called on every trainer rank (multi-rank trainers construct on all
177 ranks; the sender is resolved inside the engine's ``trainer_init``).
178
179 The trainer side takes no `WeightTransferConfig` and no separate
180 `backend` argument: the backend is read from ``init_info.backend`` (a
181 `ClassVar` on each `TrainerInitInfo` subclass), and the static wire
182 params ride `init_info`.
183
184 Args:
185 init_info: Backend-specific trainer init info. Its `backend`
186 selects the engine; it also carries the wire params (e.g.
187 `packed`).
188 client: Inference-side control-plane client.
189 source: `WeightSource` of `(name, tensor)` pairs to send each round,
190 for full-resync backends (NCCL, IPC). Sparse backend
191 omits it and passes its per-round payload to `send_weights`.
192
193 Raises:
194 ValueError: If `init_info.backend` is not registered.
195
196 """
197 backend = init_info.backend
198 if backend not in cls._registry:
199 available = list(cls._registry.keys())
200 raise ValueError(
201 f"Invalid weight transfer backend: {backend}. "
202 f"Available trainer engines: {available}"
203 )
204 engine_cls = cls._registry[backend]()
205
206 logger.info(
207 "Creating weight transfer trainer engine: %s",
208 engine_cls.__name__,
209 )
210
211 return engine_cls.trainer_init(
212 init_info=init_info,
213 client=client,
214 source=source,
215 )
216
217
218 # Register built-in weight transfer engines here.
219 # Registration should be centralized to ensure lazy loading -
220 # engine modules are only imported when actually used.
221
222 WeightTransferEngineFactory.register_engine(
223 "nccl",
224 "vllm.distributed.weight_transfer.nccl_engine",
225 "NCCLWeightTransferEngine",
226 )
227
228 WeightTransferEngineFactory.register_engine(
229 "ipc",
230 "vllm.distributed.weight_transfer.ipc_engine",
231 "IPCWeightTransferEngine",
232 )
233
234 WeightTransferEngineFactory.register_engine(
235 "sparse_nccl",
236 "vllm.distributed.weight_transfer.sparse_nccl_engine",
237 "SparseNCCLWeightTransferEngine",
238 )
239
240 WeightTransferEngineFactory.register_engine(
241 "sharded_rdt",
242 "vllm.distributed.weight_transfer.sharded_rdt_engine",
243 "ShardedRDTWeightTransferEngine",
244 )
245
246 WeightTransferEngineFactory.register_engine(
247 "nccl_m2n",
248 "vllm.distributed.weight_transfer.m2n_engine",
249 "M2NWeightTransferEngine",
250 )
251
252
253 WeightTransferEngineFactory.register_engine(
254 "modelexpress",
255 "vllm.distributed.weight_transfer.modelexpress_engine",
256 "ModelExpressWeightTransferEngine",
257 )
258
259
260 # Trainer-side engines, parallel to the worker registry above.
261 WeightTransferTrainerFactory.register_engine(
262 "nccl",
263 "vllm.distributed.weight_transfer.nccl_engine",
264 "NCCLTrainerWeightTransferEngine",
265 )
266
267 WeightTransferTrainerFactory.register_engine(
268 "ipc",
269 "vllm.distributed.weight_transfer.ipc_engine",
270 "IPCTrainerWeightTransferEngine",
271 )
272
273 WeightTransferTrainerFactory.register_engine(
274 "sparse_nccl",
275 "vllm.distributed.weight_transfer.sparse_nccl_engine",
276 "SparseNCCLTrainerWeightTransferEngine",
277 )
278
279 WeightTransferTrainerFactory.register_engine(
280 "sharded_rdt",
281 "vllm.distributed.weight_transfer.sharded_rdt_trainer",
282 "ShardedRDTTrainerWeightTransferEngine",
283 )
284
285 WeightTransferTrainerFactory.register_engine(
286 "nccl_m2n",
287 "vllm.distributed.weight_transfer.m2n_trainer",
288 "M2NTrainerWeightTransferEngine",
289 )