|
18 | 18 | ) |
19 | 19 | from vllm.v1.core.kv_cache_utils import resolve_kv_cache_block_sizes |
20 | 20 | from vllm.v1.core.sched.output import SchedulerOutput |
21 | | -from vllm.v1.kv_cache_interface import SlidingWindowSpec, UniformTypeKVCacheSpecs |
| 21 | +from vllm.v1.kv_cache_interface import MambaSpec, SlidingWindowSpec, UniformTypeKVCacheSpecs |
22 | 22 | from vllm.v1.outputs import KVConnectorOutput |
23 | 23 |
|
24 | 24 | from vllm_ascend.distributed.kv_transfer.kv_pool.recompute_cpu_offload.metadata import ( |
@@ -70,9 +70,13 @@ def __init__( |
70 | 70 | assert kv_cache_config is not None |
71 | 71 | self.vllm_config = vllm_config |
72 | 72 | self.enable_offload_prefix_caching = enable_offload_prefix_caching |
| 73 | + self.num_spec_tokens = ( |
| 74 | + vllm_config.speculative_config.num_speculative_tokens if vllm_config.speculative_config else 0 |
| 75 | + ) |
73 | 76 | self.cpu_kv_cache_config = self._derive_cpu_config(kv_cache_config, cpu_capacity_bytes) |
74 | 77 | self.num_cpu_blocks = self.cpu_kv_cache_config.num_blocks |
75 | 78 | self._group_is_sliding_window = self._get_group_is_sliding_window(kv_cache_config) |
| 79 | + self._group_is_mamba = self._get_group_is_mamba(kv_cache_config) |
76 | 80 | self.enable_kv_cache_events = ( |
77 | 81 | vllm_config.kv_events_config is not None and vllm_config.kv_events_config.enable_kv_cache_events |
78 | 82 | ) |
@@ -129,6 +133,18 @@ def _get_group_is_sliding_window(kv_cache_config: "KVCacheConfig") -> list[bool] |
129 | 133 | group_is_sliding_window.append(isinstance(group.kv_cache_spec, SlidingWindowSpec)) |
130 | 134 | return group_is_sliding_window |
131 | 135 |
|
| 136 | + @staticmethod |
| 137 | + def _get_group_is_mamba(kv_cache_config: "KVCacheConfig") -> list[bool]: |
| 138 | + group_is_mamba: list[bool] = [] |
| 139 | + for group in kv_cache_config.kv_cache_groups: |
| 140 | + if isinstance(group.kv_cache_spec, UniformTypeKVCacheSpecs): |
| 141 | + group_is_mamba.append( |
| 142 | + any(isinstance(spec, MambaSpec) for spec in group.kv_cache_spec.kv_cache_specs.values()) |
| 143 | + ) |
| 144 | + else: |
| 145 | + group_is_mamba.append(isinstance(group.kv_cache_spec, MambaSpec)) |
| 146 | + return group_is_mamba |
| 147 | + |
132 | 148 | @staticmethod |
133 | 149 | def _derive_cpu_config(gpu_config: "KVCacheConfig", cpu_capacity_bytes: int) -> "KVCacheConfig": |
134 | 150 | from vllm.v1.kv_cache_interface import KVCacheConfig as KVCacheConfigCls |
@@ -247,53 +263,49 @@ def _create_preempt_state( |
247 | 263 | group_gpu_hashes: list[list[BlockHashWithGroupId | None]] = [] |
248 | 264 | missing_hashes: set[BlockHashWithGroupId] = set() |
249 | 265 | num_unhashed = 0 |
| 266 | + num_mamba_blocks = 0 |
250 | 267 |
|
251 | 268 | for g, group_gpu_ids in enumerate(block_ids_by_group): |
252 | | - group_block_size = kv_cache_groups[g].kv_cache_spec.block_size |
253 | | - logical_num_blocks = cdiv(num_computed_tokens, group_block_size) |
254 | | - aligned_group_gpu_ids = self._align_group_block_ids(g, group_gpu_ids, logical_num_blocks) |
255 | | - eviction_group_gpu_ids = self._align_group_block_ids( |
256 | | - g, |
257 | | - group_gpu_ids, |
258 | | - max(logical_num_blocks, len(group_gpu_ids)), |
259 | | - ) |
260 | 269 | gpu_blocks: list[KVCacheBlock | None] = [] |
261 | 270 | effective_hashes: list[BlockHashWithGroupId | None] = [] |
262 | | - |
263 | | - for block_idx, block_id in enumerate(eviction_group_gpu_ids): |
264 | | - if block_id <= 0: |
265 | | - continue |
266 | | - gpu_block = self._gpu_block_pool.blocks[block_id] |
267 | | - block_is_computed = (block_idx + 1) * group_block_size <= num_computed_tokens |
268 | | - if not block_is_computed and gpu_block.block_hash is not None: |
269 | | - # allocate_slots() may assign a hash using tokens planned |
270 | | - # for this scheduling step. If the request is then |
271 | | - # preempted before forward, that block does not contain the |
272 | | - # hashed KV and must not remain in the GPU prefix cache. |
273 | | - self._gpu_block_pool._maybe_evict_cached_block(gpu_block) |
274 | | - |
275 | | - for block_idx, block_id in enumerate(aligned_group_gpu_ids): |
276 | | - if block_id <= 0: |
277 | | - gpu_blocks.append(None) |
278 | | - effective_hashes.append(None) |
279 | | - continue |
280 | | - |
281 | | - gpu_block = self._gpu_block_pool.blocks[block_id] |
282 | | - block_is_computed = (block_idx + 1) * group_block_size <= num_computed_tokens |
283 | | - block_hash = gpu_block.block_hash if block_is_computed and self.enable_offload_prefix_caching else None |
284 | | - gpu_blocks.append(gpu_block) |
285 | | - effective_hashes.append(block_hash) |
286 | | - if block_hash is None: |
287 | | - num_unhashed += 1 |
288 | | - elif ( |
289 | | - self.cpu_block_pool.cached_block_hash_to_block.get_one_block(block_hash) is None |
290 | | - and block_hash not in self._pending_hash_blocks |
291 | | - ): |
292 | | - missing_hashes.add(block_hash) |
| 271 | + if self._group_is_mamba[g]: |
| 272 | + # For Mamba cache, only the last `1 + num_spec_tokens` blocks need to offload |
| 273 | + offload_start_idx = len(group_gpu_ids) - self.num_spec_tokens - 1 |
| 274 | + for block_idx, block_id in enumerate(group_gpu_ids): |
| 275 | + if block_idx >= offload_start_idx: |
| 276 | + num_mamba_blocks += 1 |
| 277 | + gpu_block = self._gpu_block_pool.blocks[block_id] |
| 278 | + gpu_blocks.append(gpu_block) |
| 279 | + effective_hashes.append(None) |
| 280 | + else: |
| 281 | + group_block_size = kv_cache_groups[g].kv_cache_spec.block_size |
| 282 | + logical_num_blocks = cdiv(num_computed_tokens, group_block_size) |
| 283 | + aligned_group_gpu_ids = self._align_group_block_ids(g, group_gpu_ids, logical_num_blocks) |
| 284 | + |
| 285 | + for block_idx, block_id in enumerate(aligned_group_gpu_ids): |
| 286 | + if block_id <= 0: |
| 287 | + gpu_blocks.append(None) |
| 288 | + effective_hashes.append(None) |
| 289 | + continue |
| 290 | + |
| 291 | + gpu_block = self._gpu_block_pool.blocks[block_id] |
| 292 | + block_is_computed = (block_idx + 1) * group_block_size <= num_computed_tokens |
| 293 | + block_hash = ( |
| 294 | + gpu_block.block_hash if block_is_computed and self.enable_offload_prefix_caching else None |
| 295 | + ) |
| 296 | + gpu_blocks.append(gpu_block) |
| 297 | + effective_hashes.append(block_hash) |
| 298 | + if block_hash is None: |
| 299 | + num_unhashed += 1 |
| 300 | + elif ( |
| 301 | + self.cpu_block_pool.cached_block_hash_to_block.get_one_block(block_hash) is None |
| 302 | + and block_hash not in self._pending_hash_blocks |
| 303 | + ): |
| 304 | + missing_hashes.add(block_hash) |
293 | 305 | group_gpu_blocks.append(gpu_blocks) |
294 | 306 | group_gpu_hashes.append(effective_hashes) |
295 | 307 |
|
296 | | - num_needed = num_unhashed + len(missing_hashes) |
| 308 | + num_needed = num_unhashed + len(missing_hashes) + num_mamba_blocks |
297 | 309 | if not any(any(gpu_block is not None for gpu_block in group) for group in group_gpu_blocks): |
298 | 310 | return False |
299 | 311 | if num_needed > self.cpu_block_pool.get_num_free_blocks(): |
@@ -410,45 +422,50 @@ def _prepare_preempt_load_after_alloc( |
410 | 422 | gpu_block_ids: list[int] = [] |
411 | 423 | cpu_block_ids: list[int] = [] |
412 | 424 | for g, group_cpu_ids in enumerate(state.cpu_block_ids): |
413 | | - group_block_size = self.cpu_kv_cache_config.kv_cache_groups[g].kv_cache_spec.block_size |
414 | | - start_block = load_start_tokens // group_block_size |
415 | | - end_block = min( |
416 | | - len(group_cpu_ids), |
417 | | - len( |
418 | | - self._align_group_block_ids( |
419 | | - g, |
420 | | - block_ids_by_group[g], |
421 | | - max( |
422 | | - cdiv(load_end_tokens, group_block_size), |
423 | | - len(block_ids_by_group[g]), |
424 | | - ), |
425 | | - ) |
426 | | - ), |
427 | | - cdiv(load_end_tokens, group_block_size), |
428 | | - ) |
429 | | - if end_block == start_block: |
430 | | - continue |
431 | | - if end_block < start_block: |
432 | | - raise RuntimeError( |
433 | | - "Recompute H2D produced an empty block range: " |
434 | | - f"req_id={request.request_id}, group={g}, " |
435 | | - f"start_block={start_block}, end_block={end_block}, " |
436 | | - f"gpu_blocks={len(block_ids_by_group[g])}, " |
437 | | - f"cpu_blocks={len(group_cpu_ids)}" |
| 425 | + if self._group_is_mamba[g]: |
| 426 | + accept_token_idx = self.num_spec_tokens - (state.num_computed_tokens - request.num_tokens + 1) |
| 427 | + cpu_block_ids.append(group_cpu_ids[accept_token_idx]) |
| 428 | + gpu_block_ids.append(block_ids_by_group[g][0]) |
| 429 | + else: |
| 430 | + group_block_size = self.cpu_kv_cache_config.kv_cache_groups[g].kv_cache_spec.block_size |
| 431 | + start_block = load_start_tokens // group_block_size |
| 432 | + end_block = min( |
| 433 | + len(group_cpu_ids), |
| 434 | + len( |
| 435 | + self._align_group_block_ids( |
| 436 | + g, |
| 437 | + block_ids_by_group[g], |
| 438 | + max( |
| 439 | + cdiv(load_end_tokens, group_block_size), |
| 440 | + len(block_ids_by_group[g]), |
| 441 | + ), |
| 442 | + ) |
| 443 | + ), |
| 444 | + cdiv(load_end_tokens, group_block_size), |
438 | 445 | ) |
439 | | - |
440 | | - aligned_group_gpu_ids = self._align_group_block_ids( |
441 | | - g, |
442 | | - block_ids_by_group[g], |
443 | | - end_block, |
444 | | - ) |
445 | | - for block_idx in range(start_block, end_block): |
446 | | - cpu_block_id = group_cpu_ids[block_idx] |
447 | | - gpu_block_id = aligned_group_gpu_ids[block_idx] |
448 | | - if cpu_block_id <= 0 or gpu_block_id <= 0: |
| 446 | + if end_block == start_block: |
449 | 447 | continue |
450 | | - cpu_block_ids.append(cpu_block_id) |
451 | | - gpu_block_ids.append(gpu_block_id) |
| 448 | + if end_block < start_block: |
| 449 | + raise RuntimeError( |
| 450 | + "Recompute H2D produced an empty block range: " |
| 451 | + f"req_id={request.request_id}, group={g}, " |
| 452 | + f"start_block={start_block}, end_block={end_block}, " |
| 453 | + f"gpu_blocks={len(block_ids_by_group[g])}, " |
| 454 | + f"cpu_blocks={len(group_cpu_ids)}" |
| 455 | + ) |
| 456 | + |
| 457 | + aligned_group_gpu_ids = self._align_group_block_ids( |
| 458 | + g, |
| 459 | + block_ids_by_group[g], |
| 460 | + end_block, |
| 461 | + ) |
| 462 | + for block_idx in range(start_block, end_block): |
| 463 | + cpu_block_id = group_cpu_ids[block_idx] |
| 464 | + gpu_block_id = aligned_group_gpu_ids[block_idx] |
| 465 | + if cpu_block_id <= 0 or gpu_block_id <= 0: |
| 466 | + continue |
| 467 | + cpu_block_ids.append(cpu_block_id) |
| 468 | + gpu_block_ids.append(gpu_block_id) |
452 | 469 |
|
453 | 470 | if not cpu_block_ids or len(cpu_block_ids) != len(gpu_block_ids): |
454 | 471 | raise RuntimeError( |
|
0 commit comments