main
py 182 lines 6.23 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterator
5 from contextlib import contextmanager
6 from typing import TYPE_CHECKING, Any
7
8 import torch
9
10 from vllm.config.mamba import MambaBackendEnum
11 from vllm.logger import init_logger
12 from vllm.model_executor.layers.mamba.ops.ssu_dispatch import (
13 flashinfer_replayssm_autotune_supported,
14 )
15
16 if TYPE_CHECKING:
17 from vllm.v1.worker.gpu_model_runner import GPUModelRunner
18
19 logger = init_logger(__name__)
20
21
22 def _replayssm_autotune_kwargs(
23 runner: "GPUModelRunner",
24 max_token_prefill_kwargs: dict[str, Any],
25 ) -> tuple[int, dict[str, Any]] | None:
26 config = runner.vllm_config
27 if not (
28 config.cache_config.use_replayssm
29 and config.mamba_config.backend == MambaBackendEnum.FLASHINFER
30 ):
31 return None
32 if not flashinfer_replayssm_autotune_supported():
33 logger.info_once(
34 "Skipping FlashInfer ReplaySSM autotuning because "
35 "flashinfer.mamba.checkpointing_ssu.CheckpointingSSURunner "
36 "is unavailable."
37 )
38 return None
39 v2_runner: Any = runner
40 query_len = (
41 v2_runner.decode_query_len
42 if config.use_v2_model_runner
43 else runner.uniform_decode_query_len
44 )
45 max_num_reqs = min(
46 runner.scheduler_config.max_num_seqs,
47 runner.max_num_tokens // query_len,
48 runner.kv_cache_config.num_blocks - 1,
49 )
50 if max_num_reqs <= 0:
51 logger.warning_once(
52 "Skipping FlashInfer ReplaySSM autotuning because no non-padding "
53 "state slot is available."
54 )
55 return None
56
57 decode_kwargs = {
58 **max_token_prefill_kwargs,
59 "num_tokens": max_num_reqs * query_len,
60 "uniform_decode": True,
61 }
62 if config.use_v2_model_runner:
63 decode_kwargs["valid_dummy_state_slots"] = True
64 else:
65 decode_kwargs.update(
66 allow_microbatching=False,
67 force_attention=True,
68 profile_seq_lens=query_len + 1,
69 )
70 return max_num_reqs, decode_kwargs
71
72
73 @contextmanager
74 def _temporary_replayssm_autotune_state(
75 runner: "GPUModelRunner", max_num_reqs: int
76 ) -> Iterator[None]:
77 from vllm.model_executor.layers.mamba.mamba_mixer2 import MambaMixer2
78 from vllm.model_executor.layers.mamba.ops.ssu_dispatch import (
79 reset_replayssm_ring_trackers,
80 update_replayssm_ring_trackers,
81 )
82
83 reset_tensors: dict[int, torch.Tensor] = {}
84 tracker_specs: dict[
85 int, tuple[torch.Tensor, torch.Tensor, torch.Tensor, int, int]
86 ] = {}
87 for module in runner.get_model().modules():
88 if not isinstance(module, MambaMixer2) or not module.use_replayssm:
89 continue
90 assert module.replayssm_buffer_len is not None
91 ring_start = module._replayssm_ring_start
92 prev_num_accepted = module._replayssm_prev_num_accepted
93 prev_query_len = module._replayssm_prev_query_len
94 tracker_specs.setdefault(
95 ring_start.data_ptr(),
96 (
97 ring_start,
98 prev_num_accepted,
99 prev_query_len,
100 module.replayssm_buffer_len,
101 module.kv_cache[2].size(2),
102 ),
103 )
104 tensors = (
105 *module.kv_cache,
106 ring_start,
107 prev_num_accepted,
108 prev_query_len,
109 )
110 for tensor in tensors:
111 if tensor.numel():
112 reset_tensors.setdefault(tensor.data_ptr(), tensor)
113
114 v2_runner: Any = runner
115 block_tables = saved_block_ids = None
116 if not runner.vllm_config.use_v2_model_runner:
117 block_tables = runner.input_batch.block_table.block_tables
118 saved_block_ids = tuple(
119 block_table.block_table.np[:max_num_reqs, 0].copy()
120 for block_table in block_tables
121 )
122 dummy_block_ids = range(1, max_num_reqs + 1)
123 for block_table in block_tables:
124 block_table.block_table.np[:max_num_reqs, 0] = dummy_block_ids
125 runner.input_batch.block_table.commit_block_table(max_num_reqs)
126
127 first_tracker = next(iter(tracker_specs.values()), None)
128 if first_tracker is not None and first_tracker[0].is_cuda:
129 state_slots = torch.arange(
130 1, max_num_reqs + 1, dtype=torch.int32, device=first_tracker[0].device
131 )
132 for (
133 ring_start,
134 prev_num_accepted,
135 prev_query_len,
136 logical_window,
137 ring_buffer_len,
138 ) in tracker_specs.values():
139 # Compile reset (prefill) and advance (decode) before inference.
140 # The final reset leaves the decode tuning run in a clean state.
141 reset_replayssm_ring_trackers(
142 ring_start, prev_num_accepted, prev_query_len, state_slots
143 )
144 update_replayssm_ring_trackers(
145 ring_start,
146 prev_num_accepted,
147 prev_query_len,
148 state_slots,
149 logical_window,
150 ring_buffer_len,
151 )
152 reset_replayssm_ring_trackers(
153 ring_start, prev_num_accepted, prev_query_len, state_slots
154 )
155
156 try:
157 yield
158 finally:
159 if runner.vllm_config.use_v2_model_runner:
160 v2_runner.block_tables.get_dummy_block_tables(max_num_reqs)
161 else:
162 assert block_tables is not None and saved_block_ids is not None
163 for block_table, block_ids in zip(block_tables, saved_block_ids):
164 block_table.block_table.np[:max_num_reqs, 0] = block_ids
165 runner.input_batch.block_table.commit_block_table(max_num_reqs)
166 for tensor in reset_tensors.values():
167 tensor[1 : max_num_reqs + 1].zero_()
168
169
170 def replayssm_autotune_warmup(runner: "GPUModelRunner") -> None:
171 max_token_prefill_kwargs = {
172 "num_tokens": runner.scheduler_config.max_num_batched_tokens,
173 "skip_eplb": True,
174 "is_profile": True,
175 "randomize_inputs": True,
176 }
177 autotune = _replayssm_autotune_kwargs(runner, max_token_prefill_kwargs)
178 if autotune is None:
179 return
180 max_num_reqs, decode_kwargs = autotune
181 with _temporary_replayssm_autotune_state(runner, max_num_reqs):
182 runner._dummy_run(**decode_kwargs)