worker_utils.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. # SPDX-License-Identifier: Apache-2.0
  2. # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
  3. import math
  4. from collections import defaultdict
  5. from collections.abc import Iterable, Mapping, Sequence
  6. from dataclasses import dataclass, field
  7. from itertools import product as iprod
  8. from typing import Any
  9. import numpy as np
  10. import torch
  11. from vllm.config import CacheConfig, VllmConfig
  12. from vllm.logger import init_logger
  13. from vllm.model_executor.layers.attention import Attention
  14. from vllm.model_executor.layers.mamba.mamba_mixer2 import share_replayssm_ring_trackers
  15. from vllm.model_executor.layers.utils import warmup_rocm_skinny_gemm_workspaces
  16. from vllm.model_executor.models.interfaces import MultiModalEmbeddings
  17. from vllm.model_executor.models.utils import extract_layer_index
  18. from vllm.platforms import current_platform
  19. from vllm.triton_utils import tl, triton
  20. from vllm.utils.mem_utils import MemorySnapshot, format_gib
  21. from vllm.utils.torch_utils import async_tensor_h2d
  22. from vllm.v1.attention.backend import (
  23. AttentionBackend,
  24. AttentionMetadataBuilder,
  25. MultipleOf,
  26. )
  27. from vllm.v1.core.kv_cache_utils import (
  28. DraftSlidingWindowSpec,
  29. KVCacheBlockCopy,
  30. _glm5_next_draft_pool_enabled,
  31. )
  32. from vllm.v1.kv_cache_interface import (
  33. AttentionSpec,
  34. EncoderOnlyAttentionSpec,
  35. KVCacheConfig,
  36. KVCacheGroupSpec,
  37. KVCacheLayout,
  38. KVCacheSpec,
  39. MambaSpec,
  40. MLAAttentionSpec,
  41. UniformTypeKVCacheSpecs,
  42. create_kv_cache_views,
  43. )
  44. from vllm.v1.worker.block_table import get_block_table_width
  45. logger = init_logger(__name__)
  46. def raise_if_nan_logits(num_nans_in_logits: Mapping[str, int]) -> None:
  47. if not any(num_nans_in_logits.values()):
  48. return
  49. corrupted_requests = {
  50. req_id: num_nans
  51. for req_id, num_nans in num_nans_in_logits.items()
  52. if num_nans > 0
  53. }
  54. raise RuntimeError(f"NaNs detected in logits: {corrupted_requests}")
  55. @triton.jit
  56. def _zero_kv_blocks_kernel(
  57. seg_addrs_ptr,
  58. seg_block_strides_ptr,
  59. seg_page_sizes_ptr,
  60. block_ids_ptr,
  61. BLOCK_SIZE: tl.constexpr,
  62. ):
  63. """Zero KV cache blocks across all segments in a single launch.
  64. Each segment is a contiguous region of one block's data. Layer-compact
  65. layouts have one segment per layer buffer; dimensions physically outside
  66. the block dim (separate head groups under LHBNC) and virtual block splits
  67. each get their own segment.
  68. Segments may have different block strides and page sizes (e.g. packed
  69. KV views or models with multiple KV cache groups like MLA + DSA
  70. indexer). Each segment's block stride determines where a logical block
  71. begins, while its page size determines how many elements are cleared.
  72. seg_addrs_ptr holds absolute byte addresses (int64) for each segment,
  73. allowing segments to live in different CUDA allocations.
  74. Programs are mapped directly onto a 3-D grid as
  75. (block_index, seg_index, chunk_index).
  76. """
  77. block_index = tl.program_id(0)
  78. seg_index = tl.program_id(1)
  79. chunk_index = tl.program_id(2)
  80. block_stride_el = tl.load(seg_block_strides_ptr + seg_index)
  81. page_size_el = tl.load(seg_page_sizes_ptr + seg_index)
  82. chunk_offset = chunk_index.to(tl.int64) * BLOCK_SIZE
  83. if chunk_offset >= page_size_el:
  84. return
  85. block_id = tl.load(block_ids_ptr + block_index)
  86. seg_addr = tl.load(seg_addrs_ptr + seg_index)
  87. ptr = tl.cast(seg_addr, tl.pointer_type(tl.int32))
  88. block_offset = block_id.to(tl.int64) * block_stride_el.to(tl.int64)
  89. cols = chunk_offset + tl.arange(0, BLOCK_SIZE).to(tl.int64)
  90. tl.store(
  91. ptr + block_offset + cols,
  92. tl.zeros([BLOCK_SIZE], dtype=tl.int32),
  93. mask=cols < page_size_el,
  94. )
  95. class KVBlockZeroer:
  96. """Manages efficient zeroing of KV cache blocks via a Triton kernel.
  97. Construct once after KV caches are allocated to precompute segment
  98. addresses, then call :meth:`zero_block_ids` each step to zero
  99. newly-allocated blocks.
  100. """
  101. def __init__(
  102. self,
  103. device: torch.device,
  104. attn_groups_iter: Iterable["AttentionGroup"],
  105. kernel_block_sizes: list[int],
  106. static_forward_context: dict[str, Any],
  107. num_blocks: int,
  108. runner_only_attn_layers: set[str] | None = None,
  109. ) -> None:
  110. """Precompute the absolute-address table for the Triton zeroing kernel.
  111. Each entry is the absolute byte address of a segment start on the
  112. GPU, so segments in different CUDA allocations work correctly.
  113. Per-layer views are standardized ``[B, H, N, C]`` with blocks at dim 0; dims
  114. physically outside B (separate head groups under LHBNC) each get their own
  115. segment. A segment's page spans everything inside its block, so under BHLNC it
  116. also covers the block's other layers -- safe, since block IDs are global pool
  117. indices and a newly allocated block owns its whole tile.
  118. Block IDs from the scheduler reference logical blocks whose size
  119. may differ from the kernel block size (virtual block splitting).
  120. Each virtual block is represented as an independent segment so its
  121. physical block stride and zeroed page span remain independent.
  122. Only AttentionSpec layers are processed; Mamba layers are skipped.
  123. """
  124. self.device = device
  125. self._meta: (
  126. tuple[torch.Tensor, torch.Tensor, torch.Tensor, int, int, int] | None
  127. ) = None
  128. if runner_only_attn_layers is None:
  129. runner_only_attn_layers = set()
  130. # Overlaid layers (packed layouts) share a base address but may have
  131. # different page sizes; keep the widest span per address so newly
  132. # allocated blocks are fully zeroed for every overlaying group.
  133. seen_ptrs: dict[int, int] = {}
  134. seg_addrs: list[int] = []
  135. seg_block_strides: list[int] = []
  136. seg_page_sizes: list[int] = []
  137. for group in attn_groups_iter:
  138. spec = group.kv_cache_spec
  139. if not isinstance(spec, AttentionSpec):
  140. continue
  141. if group.kv_cache_group_id >= len(kernel_block_sizes):
  142. continue
  143. kernel_bs = kernel_block_sizes[group.kv_cache_group_id]
  144. assert spec.block_size % kernel_bs == 0
  145. for layer_name in group.layer_names:
  146. if layer_name in runner_only_attn_layers:
  147. continue
  148. kv = static_forward_context[layer_name].kv_cache
  149. if not isinstance(kv, torch.Tensor):
  150. continue
  151. if kv.device.type != self.device.type:
  152. continue
  153. dp = kv.data_ptr()
  154. # GLM53-DRAFT-POOL: the drafter's layers live on their own
  155. # pool, whose new blocks are never sent for zeroing. Tell them
  156. # by their spec, not by size: at TP=3 the 256-token draft
  157. # pages outnumber the 3584-token target blocks (705 to 567).
  158. if (isinstance(spec, DraftSlidingWindowSpec)
  159. and _glm5_next_draft_pool_enabled()):
  160. continue
  161. assert kv.shape[0] % num_blocks == 0, (
  162. f"{layer_name}: {kv.shape[0]} kernel blocks is not a "
  163. f"multiple of {num_blocks} logical blocks"
  164. )
  165. ratio = kv.shape[0] // num_blocks
  166. el = kv.element_size()
  167. block_stride_bytes = kv.stride(0) * el
  168. assert block_stride_bytes % 4 == 0
  169. assert kv.shape[0] % ratio == 0
  170. outer_dims = [
  171. d
  172. for d in range(1, kv.ndim)
  173. if kv.stride(d) * el > block_stride_bytes
  174. ]
  175. outer_strides = [kv.stride(d) * el for d in outer_dims]
  176. inner_dims = [d for d in range(1, kv.ndim) if d not in outer_dims]
  177. kernel_page_bytes = el + sum(
  178. (kv.shape[d] - 1) * kv.stride(d) * el for d in inner_dims
  179. )
  180. assert kernel_page_bytes % 4 == 0
  181. logical_block_stride_bytes = block_stride_bytes * ratio
  182. for outer in iprod(*(range(kv.shape[d]) for d in outer_dims)):
  183. off_bytes = sum(i * s for i, s in zip(outer, outer_strides))
  184. assert (dp + off_bytes) % 4 == 0
  185. for virtual_index in range(ratio):
  186. addr = dp + off_bytes + virtual_index * block_stride_bytes
  187. if (idx := seen_ptrs.get(addr)) is not None:
  188. assert (
  189. seg_block_strides[idx]
  190. == logical_block_stride_bytes // 4
  191. )
  192. seg_page_sizes[idx] = max(
  193. seg_page_sizes[idx], kernel_page_bytes // 4
  194. )
  195. continue
  196. seen_ptrs[addr] = len(seg_addrs)
  197. seg_addrs.append(addr)
  198. seg_block_strides.append(logical_block_stride_bytes // 4)
  199. seg_page_sizes.append(kernel_page_bytes // 4)
  200. if not seg_addrs:
  201. self._meta = None
  202. return
  203. max_page_size_el = max(seg_page_sizes)
  204. blk_size = min(1 << (max_page_size_el - 1).bit_length(), 1024)
  205. self._meta = (
  206. torch.tensor(seg_addrs, dtype=torch.uint64, device=self.device),
  207. torch.tensor(seg_block_strides, dtype=torch.int64, device=self.device),
  208. torch.tensor(seg_page_sizes, dtype=torch.int64, device=self.device),
  209. (max_page_size_el + blk_size - 1) // blk_size,
  210. blk_size,
  211. len(seg_addrs),
  212. )
  213. def zero_block_ids(self, block_ids: list[int]) -> None:
  214. """Zero the KV cache memory for the given block IDs."""
  215. if not block_ids or self._meta is None:
  216. return
  217. (
  218. seg_addrs,
  219. seg_block_strides,
  220. seg_page_sizes,
  221. max_chunks,
  222. blk_size,
  223. n_segs,
  224. ) = self._meta
  225. n_blocks = len(block_ids)
  226. idx = async_tensor_h2d(block_ids, device=self.device, dtype=torch.int64)
  227. grid = (n_blocks, n_segs, max_chunks)
  228. _zero_kv_blocks_kernel[grid](
  229. seg_addrs,
  230. seg_block_strides,
  231. seg_page_sizes,
  232. idx,
  233. BLOCK_SIZE=blk_size,
  234. )
  235. def warmup(self, num_kv_blocks: int) -> None:
  236. """JIT-compile the zeroing kernel before the first real request."""
  237. if num_kv_blocks > 0:
  238. self.zero_block_ids([0])
  239. @dataclass
  240. class AttentionGroup:
  241. backend: type[AttentionBackend]
  242. layer_names: list[str]
  243. kv_cache_spec: KVCacheSpec
  244. kv_cache_group_id: int
  245. # When ubatching is enabled we will have a metadata builder for each ubatch
  246. # so that if they use internal persistent buffers for cudagraphs, and they
  247. # won't have to worry about conflicting with the other ubatches.
  248. metadata_builders: list[AttentionMetadataBuilder] = field(
  249. default_factory=lambda: []
  250. )
  251. def create_metadata_builders(
  252. self,
  253. vllm_config,
  254. device,
  255. kernel_block_size: int | None = None,
  256. num_metadata_builders: int = 1,
  257. ):
  258. if kernel_block_size is None:
  259. kv_cache_spec_builder = self.kv_cache_spec
  260. elif (
  261. isinstance(self.kv_cache_spec, MLAAttentionSpec)
  262. and self.kv_cache_spec.storage_block_size is not None
  263. ):
  264. kv_cache_spec_builder = self.kv_cache_spec.copy_with_new_block_size(
  265. self.kv_cache_spec.storage_block_size
  266. )
  267. else:
  268. kv_cache_spec_builder = self.kv_cache_spec.copy_with_new_block_size(
  269. kernel_block_size
  270. )
  271. builder_cls = self.backend.get_builder_cls()
  272. builder_kwargs = {}
  273. if builder_cls.requires_block_table_width:
  274. max_num_blocks = self.kv_cache_spec.max_num_blocks_per_req(
  275. vllm_config, vllm_config.model_config.max_model_len
  276. )
  277. builder_kwargs["block_table_width"] = get_block_table_width(
  278. max_num_blocks, self.kv_cache_spec.block_size, kernel_block_size
  279. )
  280. self.metadata_builders = [
  281. builder_cls(
  282. kv_cache_spec_builder,
  283. self.layer_names,
  284. vllm_config,
  285. device,
  286. **builder_kwargs,
  287. )
  288. for _ in range(num_metadata_builders)
  289. ]
  290. if kernel_block_size is not None:
  291. for builder in self.metadata_builders:
  292. builder.set_kernel_block_size(kernel_block_size)
  293. def get_metadata_builder(self, ubatch_id: int = 0) -> AttentionMetadataBuilder:
  294. assert len(self.metadata_builders) > ubatch_id
  295. return self.metadata_builders[ubatch_id]
  296. @property
  297. def supports_draft_decode_metadata_update(self) -> bool:
  298. return self.get_metadata_builder().supports_draft_decode_metadata_update
  299. def update_draft_decode_metadata(
  300. self,
  301. attn_metadata: Mapping[str, Any],
  302. ) -> None:
  303. metadata = attn_metadata[self.layer_names[0]]
  304. self.get_metadata_builder().update_draft_decode_metadata(metadata)
  305. def select_common_block_size(
  306. kv_manager_block_size: int,
  307. backends: list[type[AttentionBackend]],
  308. ) -> int:
  309. """Select a block size that is supported by all backends and is a factor of
  310. kv_manager_block_size.
  311. If kv_manager_block_size is supported by all backends, return it directly.
  312. Otherwise, return the max supported size.
  313. Args:
  314. kv_manager_block_size: Block size of KV cache.
  315. backends: List of attention backend classes.
  316. Returns:
  317. The selected block size.
  318. Raises:
  319. ValueError: If no valid block size found.
  320. """
  321. def block_size_is_supported(
  322. backends: list[type[AttentionBackend]], block_size: int
  323. ) -> bool:
  324. """Check if the block size is supported by all backends."""
  325. for backend in backends:
  326. is_supported = False
  327. for supported_size in backend.get_supported_kernel_block_sizes():
  328. if isinstance(supported_size, int):
  329. if block_size == supported_size:
  330. is_supported = True
  331. elif isinstance(supported_size, MultipleOf):
  332. if block_size % supported_size.base == 0:
  333. is_supported = True
  334. else:
  335. raise ValueError(f"Unknown supported size: {supported_size}")
  336. if not is_supported:
  337. return False
  338. return True
  339. if block_size_is_supported(backends, kv_manager_block_size):
  340. return kv_manager_block_size
  341. # MultipleOf constraints also accept the manager size if they accept a divisor.
  342. # Any remaining candidate must therefore be an explicit size from a backend.
  343. candidates = {
  344. size
  345. for backend in backends
  346. for size in backend.get_supported_kernel_block_sizes()
  347. if isinstance(size, int) and kv_manager_block_size % size == 0
  348. }
  349. for size in sorted(candidates, reverse=True):
  350. if block_size_is_supported(backends, size):
  351. return size
  352. raise ValueError(f"No common block size for {kv_manager_block_size}.")
  353. def allocate_kv_cache(
  354. kv_cache_config: KVCacheConfig,
  355. device: torch.device,
  356. layout: KVCacheLayout,
  357. kernel_block_sizes: list[int] | None = None,
  358. ) -> dict[str, torch.Tensor]:
  359. """Allocate the KV cache and view it as ``[B, H, N, C]`` per layer.
  360. Every KVCacheTensor places its layers in the same backing allocation: layer ``l`` of
  361. block ``b`` starts at ``offset + l * layer_stride + b * block_stride``. Cache
  362. groups overlay each other, so tensors may address the same bytes.
  363. """
  364. if not kv_cache_config.kv_cache_tensors:
  365. return {}
  366. sizes = {tensor.size for tensor in kv_cache_config.kv_cache_tensors}
  367. assert len(sizes) == 1, "KV cache tensors must share one backing allocation."
  368. raw_size = sizes.pop()
  369. # wvSplitKrc's process-lifetime static workspaces (csrc/rocm/skinny_gemms.cu)
  370. # are created lazily on the first qualifying GEMM. Force that now, before
  371. # the giant backing allocation below: if one landed in this segment's
  372. # rounding tail it would pin the whole segment at engine shutdown.
  373. if current_platform.is_rocm():
  374. warmup_rocm_skinny_gemm_workspaces(device)
  375. # Pad to the page granularity MoRIIO needs to register the shared
  376. # backing as a single RDMA memory region. Other platforms keep the
  377. # exact-size allocation (see #53974), so anything reading
  378. # storage.nbytes() has to tolerate the tail on ROCm alone.
  379. page_size = 4096
  380. buf_size = ((raw_size + page_size - 1) // page_size) * page_size
  381. else:
  382. buf_size = raw_size
  383. buf = torch.zeros(buf_size, dtype=torch.int8, device=device)
  384. kv_caches: dict[str, torch.Tensor] = {}
  385. for tensor in kv_cache_config.kv_cache_tensors:
  386. layer_name = tensor.layers[0]
  387. group_id, group = next(
  388. (group_id, group)
  389. for group_id, group in enumerate(kv_cache_config.kv_cache_groups)
  390. if layer_name in group.layer_names
  391. )
  392. spec = group.kv_cache_spec
  393. if isinstance(spec, UniformTypeKVCacheSpecs):
  394. spec = spec.kv_cache_specs[layer_name]
  395. if not spec.has_layer_views:
  396. kv_caches.update((name, buf) for name in tensor.layers)
  397. continue
  398. num_blocks = kv_cache_config.num_blocks_of(tensor)
  399. kernel_block_size = None
  400. if kernel_block_sizes is not None and group_id < len(kernel_block_sizes):
  401. kernel_block_size = kernel_block_sizes[group_id]
  402. if isinstance(spec, MLAAttentionSpec) and spec.storage_block_size is not None:
  403. kernel_block_size = spec.storage_block_size
  404. views = create_kv_cache_views(
  405. buf,
  406. spec,
  407. num_blocks,
  408. layout,
  409. tensor,
  410. kernel_block_size=kernel_block_size,
  411. )
  412. kv_caches.update(zip(tensor.layers, views))
  413. return kv_caches
  414. def prepare_kernel_block_sizes(
  415. kv_cache_config: KVCacheConfig, attn_groups: list[list[AttentionGroup]]
  416. ) -> list[int]:
  417. """Generate kernel_block_sizes that matches each block_size.
  418. For attention backends that support virtual block splitting,
  419. use the supported block sizes from the backend.
  420. For other backends (like Mamba), use the same block size (no splitting).
  421. Args:
  422. kv_cache_config: The KV cache configuration.
  423. attn_groups: Attention groups indexed by KV cache group id.
  424. Returns:
  425. List of kernel block sizes for each cache group.
  426. """
  427. kernel_block_sizes = []
  428. for kv_cache_gid, kv_cache_group in enumerate(kv_cache_config.kv_cache_groups):
  429. kv_cache_spec = kv_cache_group.kv_cache_spec
  430. if isinstance(kv_cache_spec, UniformTypeKVCacheSpecs):
  431. # All layers in the UniformTypeKVCacheSpecs have the same type,
  432. # pick an arbitrary one to dispatch.
  433. kv_cache_spec = next(iter(kv_cache_spec.kv_cache_specs.values()))
  434. if isinstance(kv_cache_spec, EncoderOnlyAttentionSpec):
  435. continue
  436. if not kv_cache_spec.has_layer_views:
  437. kernel_block_sizes.append(kv_cache_spec.block_size)
  438. elif isinstance(kv_cache_spec, AttentionSpec):
  439. # This is an attention backend that supports virtual block splitting.
  440. kv_manager_block_size = kv_cache_group.kv_cache_spec.block_size
  441. group_backends = [g.backend for g in attn_groups[kv_cache_gid]]
  442. selected_kernel_size = select_common_block_size(
  443. kv_manager_block_size, group_backends
  444. )
  445. kernel_block_sizes.append(selected_kernel_size)
  446. elif isinstance(kv_cache_spec, MambaSpec):
  447. # This is likely Mamba or other non-attention cache, no splitting.
  448. kernel_block_sizes.append(kv_cache_spec.block_size)
  449. else:
  450. raise NotImplementedError(
  451. f"unknown kv cache spec {kv_cache_group.kv_cache_spec}"
  452. )
  453. return kernel_block_sizes
  454. def sanity_check_mm_encoder_outputs(
  455. mm_embeddings: MultiModalEmbeddings,
  456. expected_num_items: int,
  457. ) -> None:
  458. """Perform sanity checks for the result of
  459. [`vllm.model_executor.models.SupportsMultiModal.embed_multimodal`][].
  460. """
  461. assert isinstance(mm_embeddings, (list, tuple, torch.Tensor)), (
  462. "Expected multimodal embeddings to be a list/tuple of 2D tensors, "
  463. f"or a single 3D tensor, but got {type(mm_embeddings)} "
  464. "instead. This is most likely due to incorrect implementation "
  465. "of the model's `embed_multimodal` method."
  466. )
  467. assert len(mm_embeddings) == expected_num_items, (
  468. "Expected number of multimodal embeddings to match number of "
  469. f"input items: {expected_num_items}, but got {len(mm_embeddings)=} "
  470. "instead. This is most likely due to incorrect implementation "
  471. "of the model's `embed_multimodal` method."
  472. )
  473. assert all(e.ndim == 2 for e in mm_embeddings), (
  474. "Expected multimodal embeddings to be a sequence of 2D tensors, "
  475. f"but got tensors with shapes {[e.shape for e in mm_embeddings]} "
  476. "instead. This is most likely due to incorrect implementation "
  477. "of the model's `embed_multimodal` method."
  478. )
  479. def request_memory(init_snapshot: MemorySnapshot, cache_config: CacheConfig) -> int:
  480. """Calculate the amount of memory required by vLLM, then validate
  481. that the current amount of free memory is sufficient for that.
  482. """
  483. requested_memory = math.ceil(
  484. init_snapshot.total_memory * cache_config.gpu_memory_utilization
  485. )
  486. if init_snapshot.free_memory < requested_memory:
  487. raise ValueError(
  488. f"Free memory on device {init_snapshot.device_} "
  489. f"({format_gib(init_snapshot.free_memory)}/"
  490. f"{format_gib(init_snapshot.total_memory)} GiB) on startup "
  491. f"is less than desired GPU memory utilization "
  492. f"({cache_config.gpu_memory_utilization}, "
  493. f"{format_gib(requested_memory)} GiB). Decrease GPU memory "
  494. f"utilization or reduce GPU memory used by other processes."
  495. )
  496. return requested_memory
  497. def add_kv_sharing_layers_to_kv_cache_groups(
  498. shared_kv_cache_layers: dict[str, str],
  499. kv_cache_groups: list[KVCacheGroupSpec],
  500. runner_only_attn_layers: set[str] | None = None,
  501. ) -> None:
  502. """Sets up KV cache sharing by reusing the allocated KV caches in `kv_caches`
  503. for layers that do not allocate its own KV cache, based on the mapping in
  504. `shared_kv_cache_layers`. Adds these layers to the corresponding KV cache
  505. group, which is needed to ensure that attention metadata is assigned later.
  506. Args:
  507. shared_kv_cache_layers: Layer pairings for cross-layer KV sharing.
  508. If an Attention layer `layer_name` is in the keys of this dict, it
  509. means this layer will perform attention using the keys and values
  510. from the KV cache of `shared_kv_cache_layers[layer_name]`.
  511. kv_cache_groups: The KV cache groups of the model.
  512. runner_only_attn_layers: Attention layers handled by the runner only,
  513. which are excluded from the KV cache groups.
  514. """
  515. if not shared_kv_cache_layers:
  516. return
  517. layer_to_kv_cache_group: dict[str, KVCacheGroupSpec] = {}
  518. for kv_cache_group in kv_cache_groups:
  519. for layer_name in kv_cache_group.layer_names:
  520. layer_to_kv_cache_group[layer_name] = kv_cache_group
  521. for layer_name, target_layer_name in shared_kv_cache_layers.items():
  522. tgt_kv_cache_group = layer_to_kv_cache_group[target_layer_name]
  523. tgt_kv_cache_group.layer_names.append(layer_name)
  524. if runner_only_attn_layers is not None:
  525. runner_only_attn_layers.add(layer_name)
  526. def bind_kv_cache(
  527. kv_caches: dict[str, torch.Tensor],
  528. forward_context: dict[str, Attention],
  529. runner_kv_caches: list[torch.Tensor],
  530. num_attn_module: int = 1,
  531. kv_cache_groups: Sequence[KVCacheGroupSpec] | None = None,
  532. ) -> None:
  533. """Bind the allocated KV cache to both ModelRunner and forward context so
  534. that the KV cache can be used in the forward pass.
  535. This function:
  536. 1) Fills the ModelRunner's kv cache list (`runner_kv_caches`) with
  537. kv_caches.
  538. 2) Associates each attention layer in the `forward_context` with its
  539. corresponding KV cache in kv_caches.
  540. Args:
  541. kv_caches: The allocated kv_caches with layer names as keys.
  542. num_attn_module: Number of attention modules per layer entry.
  543. forward_context: The global forward context containing all Attention
  544. layers with layer names as keys.
  545. runner_kv_caches: The kv_cache declared by ModelRunner.
  546. kv_cache_groups: The KV cache groups of the model, used to resolve
  547. layers that share a KV cache.
  548. """
  549. # Bind kv_caches to ModelRunner
  550. assert len(runner_kv_caches) == 0
  551. # Convert kv_caches dict to a list of tensors in the order of layer_index.
  552. index2name = defaultdict(list)
  553. for layer_name in kv_caches:
  554. index2name[extract_layer_index(layer_name, num_attn_module)].append(layer_name)
  555. for layer_index in sorted(index2name.keys()):
  556. layer_names = index2name[layer_index]
  557. if len(layer_names) > 1:
  558. # One typical case is encoder-decoder model, e.g., bart.
  559. # The cross attention and self attention in the same decoder layer
  560. # has different layer_name but the same layer_index.
  561. # TODO - analyze where runner_kv_caches is used and the right
  562. # way to ensure it properly reflects multiple attention layers
  563. # in the same decoder block.
  564. current_platform.check_runner_kv_caches_multi_layer()
  565. for layer_name in layer_names:
  566. runner_kv_caches.append(kv_caches[layer_name])
  567. bind_kv_cache_to_layers(
  568. kv_caches, forward_context, num_attn_module, kv_cache_groups
  569. )
  570. def bind_kv_cache_to_layers(
  571. kv_caches: dict[str, torch.Tensor],
  572. forward_context: dict[str, Attention],
  573. num_attn_module: int = 1,
  574. kv_cache_groups: Sequence[KVCacheGroupSpec] | None = None,
  575. ) -> None:
  576. """Bind layer caches and share ReplaySSM trackers in model-layer order."""
  577. # Bind kv_caches to forward context. Each layer's bind_kv_cache unpacks
  578. # its raw allocation into the per-layer view(s) it needs (e.g. Mamba
  579. # splits conv/ssm), so the kv_caches dict can hold a single tensor per
  580. # layer for the KV connector to register.
  581. for layer_name, kv_cache in kv_caches.items():
  582. forward_context[layer_name].bind_kv_cache(kv_cache)
  583. ordered_layer_names = sorted(
  584. kv_caches, key=lambda name: extract_layer_index(name, num_attn_module)
  585. )
  586. share_replayssm_ring_trackers(ordered_layer_names, forward_context, kv_cache_groups)
  587. def clear_layer_kv_caches(layers: Iterable[Any]) -> None:
  588. """Detach the KV/state cache tensors installed by bind_kv_cache().
  589. The model object can outlive the runner (e.g. LLMEngine's finalizer keeps
  590. it reachable until engine deletion), so dropping the runner's references
  591. alone does not release the KV cache memory on teardown paths.
  592. """
  593. for layer in layers:
  594. if not hasattr(layer, "kv_cache"):
  595. continue
  596. kv_cache = layer.kv_cache
  597. layer.kv_cache = torch.tensor([]) if isinstance(kv_cache, torch.Tensor) else []
  598. # Clean up quantized KV cache scale views
  599. # (int8_per_token_head, fp8_per_token_head)
  600. if hasattr(layer, "impl"):
  601. if hasattr(layer.impl, "_k_scale_cache"):
  602. layer.impl._k_scale_cache = None
  603. if hasattr(layer.impl, "_v_scale_cache"):
  604. layer.impl._v_scale_cache = None
  605. def copy_kv_cache_blocks_inplace(
  606. kv_caches: Iterable[torch.Tensor],
  607. num_blocks: int,
  608. kv_cache_block_copies: Sequence[KVCacheBlockCopy],
  609. ) -> None:
  610. if not kv_cache_block_copies:
  611. return
  612. indices_np = np.array(kv_cache_block_copies, dtype=np.int64)
  613. indices: torch.Tensor | None = None
  614. seen: set[tuple[torch.device, int]] = set()
  615. copied_storages: set[tuple[torch.device, int]] = set()
  616. for cache in kv_caches:
  617. # Layers sharing KV (cross-layer sharing) alias the same view; copy it
  618. # once. data_ptr distinguishes per-layer views of a shared allocation.
  619. key = (cache.device, cache.data_ptr())
  620. if key in seen:
  621. continue
  622. seen.add(key)
  623. if indices is None:
  624. indices = async_tensor_h2d(indices_np, device=cache.device)
  625. assert cache.device == indices.device
  626. src, dst = indices.unbind(dim=1)
  627. kernel_blocks_per_block, remainder = divmod(cache.shape[0], num_blocks)
  628. assert remainder == 0, (
  629. f"{cache.shape[0]} kernel blocks not divisible by "
  630. f"{num_blocks} scheduler blocks"
  631. )
  632. storage = cache.untyped_storage()
  633. storage_key = (cache.device, storage.data_ptr())
  634. scheduler_block_stride = (
  635. cache.stride(0) * cache.element_size() * kernel_blocks_per_block
  636. )
  637. if storage.nbytes() == num_blocks * scheduler_block_stride:
  638. if storage_key in copied_storages:
  639. continue
  640. copied_storages.add(storage_key)
  641. blocks = torch.empty(0, dtype=torch.uint8, device=cache.device)
  642. blocks.set_(storage)
  643. blocks = blocks.view(num_blocks, -1)
  644. else:
  645. # Fold virtual block splitting into the shape so that dim 0 counts
  646. # scheduler blocks; unflatten of dim 0 is always a view.
  647. blocks = cache.unflatten(0, (num_blocks, kernel_blocks_per_block))
  648. blocks[dst] = blocks[src]
  649. def is_uniform_query_len(num_reqs: int, num_tokens: int, max_query_len: int) -> bool:
  650. """Whether every request in the batch has the same query length.
  651. Shape test only; use ``get_uniform_decode_token_count`` to classify a
  652. scheduled batch, since a prompt chunk can have a decode batch's shape.
  653. """
  654. return num_reqs > 0 and num_tokens == max_query_len * num_reqs
  655. def get_uniform_decode_token_count(
  656. num_reqs: int, num_tokens: int, max_query_len: int, has_prefill: bool
  657. ) -> int | None:
  658. """Per-request token count of a uniform decode batch, or None."""
  659. if not has_prefill and is_uniform_query_len(num_reqs, num_tokens, max_query_len):
  660. return max_query_len
  661. return None
  662. def is_residual_scattered_for_sp(
  663. vllm_config: VllmConfig, num_input_tokens: int
  664. ) -> bool:
  665. """Check if the residual tensor is scattered for sequence parallelism.
  666. The residual tensor is scattered across tensor parallel ranks when sequence
  667. parallelism and tensor parallelism is enabled. SP is only supported in
  668. full-graph compilation mode.
  669. """
  670. if not vllm_config.compilation_config.pass_config.enable_sp:
  671. return False
  672. tp = vllm_config.parallel_config.tensor_parallel_size
  673. if tp == 1:
  674. return False
  675. assert (
  676. vllm_config.compilation_config.use_inductor_graph_partition
  677. or not vllm_config.compilation_config.splitting_ops
  678. ), "Sequence parallelism requires full-graph compilation"
  679. # When sequence parallelism is enabled, we always pad num_input_tokens
  680. # to be a multiple of tensor_parallel_size (tp) earlier.
  681. assert num_input_tokens % tp == 0
  682. return True
  683. @dataclass
  684. class EncoderTimingStats:
  685. """Per-request timing statistics for encoder forward pass."""
  686. encoder_forward_secs: float = 0.0
  687. """Time spent in vision encoder forward pass (seconds)."""
  688. num_encoder_calls: int = 0
  689. """Number of times encoder was called for this request."""
  690. def to_dict(self) -> dict[str, float | int]:
  691. return {
  692. "encoder_forward_secs": self.encoder_forward_secs,
  693. "num_encoder_calls": self.num_encoder_calls,
  694. }