cpu_executor.py 6.38 KB
Newer Older
1
from typing import List, Set, Tuple
2
3
4

import torch

5
import vllm.envs as envs
6
from vllm.config import CacheConfig, ModelConfig, SchedulerConfig
7
from vllm.executor.executor_base import ExecutorAsyncBase, ExecutorBase
8
9
from vllm.logger import init_logger
from vllm.lora.request import LoRARequest
10
from vllm.prompt_adapter.request import PromptAdapterRequest
11
from vllm.sequence import ExecuteModelRequest, SamplerOutput
12
13
from vllm.utils import (get_distributed_init_method, get_ip, get_open_port,
                        make_async)
14
15
16
17
18
19

logger = init_logger(__name__)


class CPUExecutor(ExecutorBase):

20
21
    uses_ray: bool = False

22
23
24
25
26
27
28
    def _init_executor(self) -> None:
        assert self.device_config.device_type == "cpu"
        assert self.lora_config is None, "cpu backend doesn't support LoRA"
        self.model_config = _verify_and_get_model_config(self.model_config)
        self.cache_config = _verify_and_get_cache_config(self.cache_config)
        self.scheduler_config = _verify_and_get_scheduler_config(
            self.scheduler_config)
29
30
31
32
33
34
35
36
37
38
39
40
41

        # Instantiate the worker and load the model to CPU.
        self._init_worker()

    def _init_worker(self):
        from vllm.worker.cpu_worker import CPUWorker

        assert self.parallel_config.world_size == 1, (
            "CPUExecutor only supports single CPU socket currently.")

        distributed_init_method = get_distributed_init_method(
            get_ip(), get_open_port())
        self.driver_worker = CPUWorker(
42
43
44
45
46
            model_config=self.model_config,
            parallel_config=self.parallel_config,
            scheduler_config=self.scheduler_config,
            device_config=self.device_config,
            cache_config=self.cache_config,
47
            load_config=self.load_config,
48
49
50
51
            local_rank=0,
            rank=0,
            distributed_init_method=distributed_init_method,
            lora_config=self.lora_config,
52
            multimodal_config=self.multimodal_config,
53
            kv_cache_dtype=self.cache_config.cache_dtype,
54
            prompt_adapter_config=self.prompt_adapter_config,
55
56
57
58
59
            is_driver_worker=True,
        )
        self.driver_worker.init_device()
        self.driver_worker.load_model()

60
    def determine_num_available_blocks(self) -> Tuple[int, int]:
61
62
63
64
65
66
67
68
69
70
71
72
        """Determine the number of available KV blocks by invoking the
        underlying worker.
        """
        return self.driver_worker.determine_num_available_blocks()

    def initialize_cache(self, num_gpu_blocks: int,
                         num_cpu_blocks: int) -> None:
        """Initialize the KV cache by invoking the underlying worker.
        """
        # NOTE: We log here to avoid multiple logs when number of workers is
        # greater than one. We could log in the engine, but not all executors
        # have GPUs.
73
74
75
        # NOTE: `cpu block` for CPU backend is located on CPU memory but is
        # referred as `gpu block`. Because we want to reuse the existing block
        # management procedure.
76
        logger.info("# CPU blocks: %d", num_gpu_blocks)
77
        self.driver_worker.initialize_cache(num_gpu_blocks, num_cpu_blocks)
78

79
80
81
82
    def execute_model(
            self,
            execute_model_req: ExecuteModelRequest) -> List[SamplerOutput]:
        output = self.driver_worker.execute_model(execute_model_req)
83
84
85
        return output

    def add_lora(self, lora_request: LoRARequest) -> bool:
86
        return self.driver_worker.add_lora(lora_request)
87
88

    def remove_lora(self, lora_id: int) -> bool:
89
        return self.driver_worker.remove_lora(lora_id)
90

91
92
93
    def pin_lora(self, lora_id: int) -> bool:
        return self.driver_worker.pin_lora(lora_id)

94
    def list_loras(self) -> Set[int]:
95
        return self.driver_worker.list_loras()
96

97
98
99
100
101
102
103
104
105
106
107
108
109
    def add_prompt_adapter(
            self, prompt_adapter_request: PromptAdapterRequest) -> bool:
        return self.driver_worker.add_prompt_adapter(prompt_adapter_request)

    def remove_prompt_adapter(self, prompt_adapter_id: int) -> bool:
        return self.driver_worker.remove_prompt_adapter(prompt_adapter_id)

    def list_prompt_adapters(self) -> Set[int]:
        return self.driver_worker.list_prompt_adapters()

    def pin_prompt_adapter(self, prompt_adapter_id: int) -> bool:
        return self.driver_worker.pin_prompt_adapter(prompt_adapter_id)

110
111
112
113
114
115
    def check_health(self) -> None:
        # CPUExecutor will always be healthy as long as
        # it's running.
        return


116
117
118
class CPUExecutorAsync(CPUExecutor, ExecutorAsyncBase):

    async def execute_model_async(
119
120
121
122
            self,
            execute_model_req: ExecuteModelRequest) -> List[SamplerOutput]:
        output = await make_async(self.driver_worker.execute_model
                                  )(execute_model_req=execute_model_req, )
123
124
125
126
127
128
129
130
        return output

    async def check_health_async(self) -> None:
        # CPUExecutor will always be healthy as long as
        # it's running.
        return


131
132
133
134
135
136
137
138
139
140
141
142
def _verify_and_get_model_config(config: ModelConfig) -> ModelConfig:
    if config.dtype == torch.float16:
        logger.warning("float16 is not supported on CPU, casting to bfloat16.")
        config.dtype = torch.bfloat16
    if not config.enforce_eager:
        logger.warning(
            "CUDA graph is not supported on CPU, fallback to the eager "
            "mode.")
        config.enforce_eager = True
    return config


143
144
145
146
147
148
149
150
151
def _verify_and_get_scheduler_config(
        config: SchedulerConfig) -> SchedulerConfig:
    if config.chunked_prefill_enabled:
        logger.warning("Chunked prefill is not supported on CPU, disable it.")
        config.chunked_prefill_enabled = False

    return config


152
153
154
155
156
157
def _verify_and_get_cache_config(config: CacheConfig) -> CacheConfig:
    _GB = 1 << 30
    if config.enable_prefix_caching:
        logger.warning("Prefix caching is not supported on CPU, disable it.")
        config.enable_prefix_caching = False

158
    kv_cache_space = envs.VLLM_CPU_KVCACHE_SPACE
159
160
161
162
163
164
165
166
167
168
169
170
171
172

    if kv_cache_space >= 0:
        if kv_cache_space == 0:
            config.cpu_kvcache_space_bytes = 4 * _GB  # type: ignore
            logger.warning("Environment variable VLLM_CPU_KVCACHE_SPACE (GB) "
                           "for CPU backend is not set, using 4 by default.")
        else:
            config.cpu_kvcache_space_bytes = kv_cache_space * _GB  # type: ignore
    else:
        raise RuntimeError(
            "Invalid environment variable VLLM_CPU_KVCACHE_SPACE"
            f" {kv_cache_space}, expect a positive integer value.")

    return config