main
py 178 lines 5.64 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 # Adapted from
4 # https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/utils/offloader.py
5 """Base classes for model parameter offloading."""
6
7 from abc import ABC, abstractmethod
8 from collections.abc import Generator
9 from typing import TYPE_CHECKING
10
11 import torch.nn as nn
12
13 import vllm.envs as envs
14 from vllm.logger import init_logger
15 from vllm.utils.platform_utils import is_pin_memory_available
16
17 if TYPE_CHECKING:
18 from vllm.config import OffloadConfig
19
20 logger = init_logger(__name__)
21
22
23 def should_pin_memory() -> bool:
24 """Check if pinned memory should be used for weight offloading.
25
26 Combines the platform capability check with the user override env var.
27 On unified-memory systems (e.g. GH200) pinned memory eats into GPU
28 memory, so users can disable it via VLLM_WEIGHT_OFFLOADING_DISABLE_PIN_MEMORY.
29 """
30 return (
31 is_pin_memory_available() and not envs.VLLM_WEIGHT_OFFLOADING_DISABLE_PIN_MEMORY
32 )
33
34
35 """
36 class relation:
37
38 BaseOffloader (ABC)
39 * implemented by: UVAOffloader
40 * implemented by: PrefetchOffloader
41 * uses: _ModuleOffloader
42 * uses: _BaseParamOffloader (ABC)
43 * implemented by: _CpuParamOffloader
44 """
45
46
47 class BaseOffloader(ABC):
48 """Base class for model parameter offloading strategies.
49
50 Offloaders control how model parameters are stored and loaded during
51 inference. Different strategies trade memory for compute/transfer time.
52 """
53
54 supports_tower_offload: bool = False
55 """Whether `wrap_modules` also accepts modules routed by
56 `SupportsMultiModal._mark_tower_model`, outside the `make_layers` call.
57
58 Offloaders whose `wrap_modules` may only be called on the decoder layer
59 stack (e.g. `PrefetchOffloader`, which schedules prefetches over a
60 circular layer stack) must keep this `False`.
61 """
62
63 @abstractmethod
64 def wrap_modules(
65 self,
66 modules_generator: Generator[nn.Module, None, None],
67 prefix: str = "",
68 ) -> list[nn.Module]:
69 """Wrap modules with offloading logic.
70
71 Args:
72 modules_generator: Generator yielding modules to potentially offload.
73 prefix: Name prefix prepended to parameter names before matching
74 them against the offloading parameter set. Used when the
75 modules are not the full model, so that name segments stay
76 fully qualified (e.g. `visual` for a tower module).
77
78 Returns:
79 List of modules, potentially with offloading hooks installed.
80
81 """
82 pass
83
84 def post_init(self):
85 """Called after model construction completes.
86
87 Offloaders can use this to:
88 - Finalize parameter storage
89 - Start initial prefetching
90 - Allocate shared resources
91 """
92 return
93
94 def sync_prev_onload(self) -> None: # noqa: B027
95 """Sync previous onload operations. Override in subclasses."""
96 pass
97
98 def join_after_forward(self) -> None: # noqa: B027
99 """Join streams after forward. Override in subclasses."""
100 pass
101
102 def _wait_for_layer(self, layer_idx: int) -> None: # noqa: B027
103 """Wait for layer prefetch. Override in subclasses."""
104 pass
105
106 def _start_prefetch(self, layer_idx: int) -> None: # noqa: B027
107 """Start layer prefetch. Override in subclasses."""
108 pass
109
110
111 class NoopOffloader(BaseOffloader):
112 """No-op offloader that returns modules as-is without any offloading."""
113
114 def wrap_modules(
115 self,
116 modules_generator: Generator[nn.Module, None, None],
117 prefix: str = "",
118 ) -> list[nn.Module]:
119 """Return modules unchanged."""
120 return list(modules_generator)
121
122
123 # Global singleton offloader instance (defaults to no-op).
124 _instance: BaseOffloader = NoopOffloader()
125
126
127 def get_offloader() -> BaseOffloader:
128 """Get the global offloader instance."""
129 return _instance
130
131
132 def set_offloader(instance: BaseOffloader | None) -> None:
133 """Set or reset the global offloader instance."""
134 global _instance
135 _instance = NoopOffloader() if instance is None else instance
136 if isinstance(_instance, NoopOffloader):
137 logger.debug_once("Offloader set to NoopOffloader (no offloading).")
138 else:
139 logger.info_once("Offloader set to %s", type(_instance).__name__)
140
141
142 def create_offloader(offload_config: "OffloadConfig") -> BaseOffloader:
143 """Create an offloader based on the offload configuration.
144
145 Uses the explicit ``offload_backend`` selector. When set to ``"auto"``,
146 selects prefetch if ``offload_group_size > 0``, UVA if
147 ``cpu_offload_gb > 0``, otherwise noop.
148 """
149 from vllm.model_executor.offloader.prefetch import PrefetchOffloader
150 from vllm.model_executor.offloader.uva import UVAOffloader
151
152 backend = offload_config.offload_backend
153 uva = offload_config.uva
154 prefetch = offload_config.prefetch
155
156 if backend == "auto":
157 if prefetch.offload_group_size > 0:
158 backend = "prefetch"
159 elif uva.cpu_offload_gb > 0:
160 backend = "uva"
161 else:
162 return NoopOffloader()
163
164 if backend == "prefetch":
165 return PrefetchOffloader(
166 group_size=prefetch.offload_group_size,
167 num_in_group=prefetch.offload_num_in_group,
168 prefetch_step=prefetch.offload_prefetch_step,
169 offload_params=prefetch.offload_params,
170 mode="cpu",
171 )
172 elif backend == "uva":
173 return UVAOffloader(
174 cpu_offload_max_bytes=int(uva.cpu_offload_gb * 1024**3),
175 cpu_offload_params=uva.cpu_offload_params,
176 )
177 else:
178 return NoopOffloader()