@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / eb24dc4a45
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 15, 22:15 UTC

[v1] torchrun compatibility (#13642)

Signed-off-by: youkaichao <youkaichao@gmail.com>

youkaichao committed Feb 23, 2025 at 22:47 UTC eb24dc4a4538445504d06a1eb1f4f053cceec593
14 files changed +67 -24
.buildkite/test-pipeline.yaml
+1
@@ -503,6 +503,7 @@ steps:
503 - entrypoints/llm/test_collective_rpc.py
504 commands:
505 - pytest -v -s entrypoints/llm/test_collective_rpc.py
506 + - VLLM_USE_V1=1 torchrun --nproc-per-node=2 distributed/test_torchrun_example.py
507 - torchrun --nproc-per-node=2 distributed/test_torchrun_example.py
508 - pytest -v -s ./compile/test_basic_correctness.py
509 - pytest -v -s ./compile/test_wrapper.py
tests/distributed/test_torchrun_example.py
+6
@@ -48,6 +48,12 @@ test_consistent_across_ranks(
48 test_consistent_across_ranks(
49 llm.llm_engine.vllm_config.cache_config.num_gpu_blocks)
50
51 +# make sure we can access the model parameters from the calling process
52 +# of the `LLM` instance.
53 +params = list(llm.llm_engine.model_executor.driver_worker.worker.model_runner.
54 + model.parameters())
55 +test_consistent_across_ranks(len(params))
56 +
57 # all ranks should have the same outputs
58 for output in outputs:
59 prompt = output.prompt
tests/v1/engine/test_engine_core.py
+4 -2
@@ -5,6 +5,7 @@ import threading
5 import time
6 import uuid
7 from concurrent.futures import Future
8 +from typing import List
9
10 import pytest
11 from transformers import AutoTokenizer
@@ -211,8 +212,9 @@ def test_engine_core_concurrent_batches(monkeypatch):
212
213 class DummyExecutor(UniProcExecutor):
214
214 - def initialize(self, kv_cache_config: KVCacheConfig) -> None:
215 - super().initialize(kv_cache_config)
215 + def initialize_from_config(
216 + self, kv_cache_configs: List[KVCacheConfig]) -> None:
217 + super().initialize_from_config(kv_cache_configs)
218
219 # This executor actually can only run 1 batch at a time
220 self.semaphore = threading.Semaphore(1)
vllm/config.py
+5
@@ -1407,6 +1407,11 @@ class ParallelConfig:
1407 self.data_parallel_master_port = envs.VLLM_DP_MASTER_PORT
1408 self.world_size_across_dp = self.world_size * self.data_parallel_size
1409
1410 + if self.distributed_executor_backend == "external_launcher":
1411 + import os
1412 + os.environ["VLLM_ENABLE_V1_MULTIPROCESSING"] = "0"
1413 + logger.info("Disabling V1 multiprocessing for external launcher.")
1414 +
1415 ray_only_devices = ["tpu"]
1416 from vllm.platforms import current_platform
1417 if (current_platform.device_type in ray_only_devices
vllm/executor/ray_distributed_executor.py
+1 -1
@@ -541,7 +541,7 @@ class RayDistributedExecutor(DistributedExecutorBase):
541 # and the TP group executes in SPMD fashion.
542 if self.use_v1:
543 outputs = [
544 - worker.execute_model.
544 + worker.execute_model_ray.
545 bind( # type: ignore[attr-defined]
546 outputs[i]) for i, worker in enumerate(tp_group)
547 ]
vllm/executor/ray_utils.py
+3 -1
@@ -112,10 +112,12 @@ try:
112 torch.cuda.set_device(self.worker.device)
113 self.compiled_dag_cuda_device_set = True
114
115 - def execute_model(
115 + def execute_model_ray(
116 self,
117 scheduler_output: "SchedulerOutput",
118 ) -> "ModelRunnerOutput":
119 + # this method is used to compile ray CG,
120 + # and it needs a special logic of self.setup_device_if_necessary()
121 self.setup_device_if_necessary()
122 assert self.worker is not None, "Worker is not initialized"
123 if isinstance(scheduler_output, tuple):
vllm/executor/uniproc_executor.py
+4 -3
@@ -93,9 +93,10 @@ class ExecutorWithExternalLauncher(UniProcExecutor):
93 ("ExecutorWithExternalLauncher needs deterministic "
94 "execution, so it"
95 "does not support delay_factor in scheduling")
96 - assert not envs.VLLM_USE_V1, \
97 - ("V1 architecture cannot guarantee deterministic execution, "
98 - "so it is not supported in ExecutorWithExternalLauncher.")
96 + if envs.VLLM_USE_V1:
97 + assert not envs.VLLM_ENABLE_V1_MULTIPROCESSING, \
98 + ("To get deterministic execution in V1, "
99 + "please set VLLM_ENABLE_V1_MULTIPROCESSING=0")
100 self.driver_worker = WorkerWrapperBase(vllm_config=self.vllm_config,
101 rpc_rank=0)
102 # engines are launched in torchrun-compatible launchers
vllm/v1/engine/core.py
+1 -1
@@ -110,7 +110,7 @@ class EngineCore:
110 num_cpu_blocks = 0
111
112 # Initialize kv cache and warmup the execution
113 - self.model_executor.initialize(kv_cache_configs)
113 + self.model_executor.initialize_from_config(kv_cache_configs)
114
115 elapsed = time.time() - start
116 logger.info(("init engine (profile, create kv cache, "
vllm/v1/engine/llm_engine.py
+7 -2
@@ -4,10 +4,10 @@ from typing import Dict, List, Mapping, Optional, Type, Union
4
5 from typing_extensions import TypeVar
6
7 +import vllm.envs as envs
8 from vllm.config import ParallelConfig, VllmConfig
9 from vllm.engine.arg_utils import EngineArgs
10 from vllm.engine.metrics_types import StatLoggerBase
10 -from vllm.envs import VLLM_ENABLE_V1_MULTIPROCESSING
11 from vllm.inputs import INPUT_REGISTRY, InputRegistry, PromptType
12 from vllm.logger import init_logger
13 from vllm.lora.request import LoRARequest
@@ -44,6 +44,7 @@ class LLMEngine:
44 use_cached_outputs: bool = False,
45 multiprocess_mode: bool = False,
46 ) -> None:
47 + self.vllm_config = vllm_config
48 self.model_config = vllm_config.model_config
49 self.cache_config = vllm_config.cache_config
50
@@ -83,6 +84,10 @@ class LLMEngine:
84 log_stats=False, # FIXME: implement
85 )
86
87 + if not multiprocess_mode:
88 + # for v0 compatibility
89 + self.model_executor = self.engine_core.engine_core.model_executor # type: ignore
90 +
91 @classmethod
92 def from_engine_args(
93 cls,
@@ -97,7 +102,7 @@ class LLMEngine:
102 vllm_config = engine_args.create_engine_config(usage_context)
103 executor_class = Executor.get_class(vllm_config)
104
100 - if VLLM_ENABLE_V1_MULTIPROCESSING:
105 + if envs.VLLM_ENABLE_V1_MULTIPROCESSING:
106 logger.debug("Enabling multiprocessing for LLMEngine.")
107 enable_multiprocessing = True
108
vllm/v1/executor/abstract.py
+17 -3
@@ -3,6 +3,9 @@
3 from concurrent.futures import Future
4 from typing import List, Type, Union
5
6 +import torch
7 +import torch.distributed as dist
8 +
9 from vllm.config import VllmConfig
10 from vllm.executor.executor_base import ExecutorBase
11 from vllm.executor.uniproc_executor import ( # noqa
@@ -49,12 +52,14 @@ class Executor(ExecutorBase):
52 f"{distributed_executor_backend}")
53 return executor_class
54
52 - def initialize(self, kv_cache_configs: List[KVCacheConfig]) -> None:
55 + def initialize_from_config(self,
56 + kv_cache_configs: List[KVCacheConfig]) -> None:
57 """
58 Initialize the KV caches and begin the model execution loop of the
59 underlying workers.
60 """
57 - self.collective_rpc("initialize_cache", args=(kv_cache_configs, ))
61 + self.collective_rpc("initialize_from_config",
62 + args=(kv_cache_configs, ))
63 self.collective_rpc("compile_or_warm_up_model")
64
65 def determine_available_memory(self) -> int: # in bytes
@@ -89,4 +94,13 @@ class UniProcExecutor(UniProcExecutorV0, Executor):
94
95
96 class ExecutorWithExternalLauncher(ExecutorWithExternalLauncherV0, Executor):
92 - pass
97 +
98 + def determine_available_memory(self) -> int: # in bytes
99 + # same as determine_num_available_blocks in v0,
100 + # we need to get the min across all ranks.
101 + memory = super().determine_available_memory()
102 + from vllm.distributed.parallel_state import get_world_group
103 + cpu_group = get_world_group().cpu_group
104 + memory_tensor = torch.tensor([memory], device="cpu", dtype=torch.int64)
105 + dist.all_reduce(memory_tensor, group=cpu_group, op=dist.ReduceOp.MIN)
106 + return memory_tensor.item()
vllm/v1/executor/multiproc_executor.py
+3 -2
@@ -216,9 +216,10 @@ class WorkerProc:
216 "local_rank": local_rank,
217 "rank": rank,
218 "distributed_init_method": distributed_init_method,
219 + "is_driver_worker": rank == 0,
220 }
221 wrapper.init_worker(all_kwargs)
221 - self.worker = wrapper.worker
222 + self.worker = wrapper
223
224 pid = os.getpid()
225 _add_prefix(sys.stdout, f"VllmWorker rank={rank}", pid)
@@ -239,7 +240,7 @@ class WorkerProc:
240 ready_socket.send_string(WorkerProc.READY_STR)
241 ready_socket.send(payload)
242
242 - wrapper.init_device()
243 + self.worker.init_device()
244 self.worker.load_model()
245
246 @staticmethod
vllm/v1/worker/gpu_worker.py
+3 -4
@@ -2,7 +2,7 @@
2 """A GPU worker class."""
3 import gc
4 import os
5 -from typing import TYPE_CHECKING, List, Optional
5 +from typing import TYPE_CHECKING, Optional
6
7 import torch
8 import torch.distributed
@@ -185,9 +185,8 @@ class Worker(WorkerBase):
185 def get_kv_cache_spec(self) -> KVCacheSpec:
186 return self.model_runner.get_kv_cache_spec()
187
188 - def initialize_cache(self, kv_cache_configs: List[KVCacheConfig]) -> None:
188 + def initialize_from_config(self, kv_cache_config: KVCacheConfig) -> None:
189 """Allocate GPU KV cache with the specified kv_cache_config."""
190 - kv_cache_config = kv_cache_configs[self.rank]
190 if self.vllm_config.model_config.enable_sleep_mode:
191 allocator = CuMemAllocator.get_instance()
192 context = allocator.use_memory_pool(tag="kv_cache")
@@ -225,7 +224,7 @@ class Worker(WorkerBase):
224 scheduler_output: "SchedulerOutput",
225 ) -> Optional[ModelRunnerOutput]:
226 output = self.model_runner.execute_model(scheduler_output)
228 - return output if self.rank == 0 else None
227 + return output if self.is_driver_worker else None
228
229 def profile(self, is_start: bool = True):
230 if self.profiler is None:
vllm/v1/worker/tpu_worker.py
+3 -3
@@ -36,6 +36,7 @@ class TPUWorker:
36 distributed_init_method: str,
37 is_driver_worker: bool = False,
38 ):
39 + self.is_driver_worker = is_driver_worker
40 self.vllm_config = vllm_config
41 self.model_config = vllm_config.model_config
42 self.cache_config = vllm_config.cache_config
@@ -151,7 +152,7 @@ class TPUWorker:
152 scheduler_output: "SchedulerOutput",
153 ) -> Optional[ModelRunnerOutput]:
154 output = self.model_runner.execute_model(scheduler_output)
154 - return output if self.rank == 0 else None
155 + return output if self.is_driver_worker else None
156
157 def load_model(self) -> None:
158 self.model_runner.load_model()
@@ -170,9 +171,8 @@ class TPUWorker:
171 def get_kv_cache_spec(self) -> KVCacheSpec:
172 return self.model_runner.get_kv_cache_spec()
173
173 - def initialize_cache(self, kv_cache_configs: List[KVCacheConfig]) -> None:
174 + def initialize_from_config(self, kv_cache_config: KVCacheConfig) -> None:
175 """Allocate GPU KV cache with the specified kv_cache_config."""
175 - kv_cache_config = kv_cache_configs[self.rank]
176 self.model_runner.initialize_kv_cache(kv_cache_config)
177
178 def check_health(self) -> None:
vllm/worker/worker_base.py
+9 -2
@@ -567,6 +567,10 @@ class WorkerWrapperBase:
567 self.worker = worker_class(**kwargs)
568 assert self.worker is not None
569
570 + def initialize_from_config(self, kv_cache_configs: List[Any]) -> None:
571 + kv_cache_config = kv_cache_configs[self.rpc_rank]
572 + self.worker.initialize_from_config(kv_cache_config) # type: ignore
573 +
574 def init_device(self):
575 with set_current_vllm_config(self.vllm_config):
576 # To make vLLM config available during device initialization
@@ -574,8 +578,11 @@ class WorkerWrapperBase:
578
579 def execute_method(self, method: Union[str, bytes], *args, **kwargs):
580 try:
577 - target = self if self.worker is None else self.worker
578 - return run_method(target, method, args, kwargs)
581 + # method resolution order:
582 + # if a method is defined in this class, it will be called directly.
583 + # otherwise, since we define `__getattr__` and redirect attribute
584 + # query to `self.worker`, the method will be called on the worker.
585 + return run_method(self, method, args, kwargs)
586 except Exception as e:
587 # if the driver worker also execute methods,
588 # exceptions in the rest worker may cause deadlock in rpc like ray