A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 11, 15:00 UTC
[Core] Refactor `_build_attention_metadata` (#29628)
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Lucas Wilkinson committed
Dec 11, 2025 at 20:54 UTC
042da732445f5cef93cb83e1045333544e61a0a1
1 file changed
+124
-126
vllm/v1/worker/gpu_model_runner.py
+124
-126
@@ -1534,28 +1534,13 @@ class GPUModelRunner(
1534
"""
1535
:return: tuple[attn_metadata, spec_decode_common_attn_metadata]
1536
"""
1537
+ # Attention metadata is not needed for attention free models
1538
+ if len(self.kv_cache_config.kv_cache_groups) == 0:
1539
+ return {}, None
1540
+
1541
num_tokens_padded = num_tokens_padded or num_tokens
1542
num_reqs_padded = num_reqs_padded or num_reqs
1539
-
1540
- logits_indices_padded = None
1541
- num_logits_indices = None
1542
- if logits_indices is not None:
1543
- num_logits_indices = logits_indices.size(0)
1544
- if self.cache_config.kv_sharing_fast_prefill:
1545
- logits_indices_padded = self._prepare_kv_sharing_fast_prefill(
1546
- logits_indices
1547
- )
1548
-
1549
- # update seq_lens of decode reqs under DCP.
1550
- if self.dcp_world_size > 1:
1551
- self.dcp_local_seq_lens.cpu[:num_reqs] = get_dcp_local_seq_lens(
1552
- self.seq_lens.cpu[:num_reqs],
1553
- self.dcp_world_size,
1554
- self.dcp_rank,
1555
- self.parallel_config.cp_kv_cache_interleave_size,
1556
- )
1557
- self.dcp_local_seq_lens.cpu[num_reqs:].fill_(0)
1558
- self.dcp_local_seq_lens.copy_to_gpu(num_reqs_padded)
1543
+ assert num_reqs_padded is not None and num_tokens_padded is not None
1544
1545
attn_metadata: PerLayerAttnMetadata = {}
1546
if ubatch_slices is not None:
@@ -1576,36 +1561,12 @@ class GPUModelRunner(
1561
self.num_accepted_tokens.np[num_reqs:].fill(1)
1562
self.num_accepted_tokens.copy_to_gpu()
1563
1579
- # Used in the below loop, uses padded shapes
1580
- query_start_loc = self.query_start_loc.gpu[: num_reqs_padded + 1]
1581
- query_start_loc_cpu = self.query_start_loc.cpu[: num_reqs_padded + 1]
1582
- seq_lens = self.seq_lens.gpu[:num_reqs_padded]
1583
- seq_lens_cpu = self.seq_lens.cpu[:num_reqs_padded]
1584
- num_computed_tokens_cpu = self.input_batch.num_computed_tokens_cpu_tensor[
1585
- :num_reqs_padded
1586
- ]
1587
-
1588
- dcp_local_seq_lens, dcp_local_seq_lens_cpu = None, None
1589
- if self.dcp_world_size > 1:
1590
- dcp_local_seq_lens = self.dcp_local_seq_lens.gpu[:num_reqs_padded]
1591
- dcp_local_seq_lens_cpu = self.dcp_local_seq_lens.cpu[:num_reqs_padded]
1564
+ kv_cache_groups = self.kv_cache_config.kv_cache_groups
1565
1593
- spec_decode_common_attn_metadata = None
1594
-
1595
- # Prepare the attention metadata for each KV cache group and make layers
1596
- # in the same group share the same metadata.
1597
- for kv_cache_gid, kv_cache_group in enumerate(
1598
- self.kv_cache_config.kv_cache_groups
1599
- ):
1600
- encoder_seq_lens, encoder_seq_lens_cpu = self._get_encoder_seq_lens(
1601
- num_scheduled_tokens or {},
1602
- kv_cache_group.kv_cache_spec,
1603
- num_reqs_padded,
1604
- )
1605
-
1606
- if isinstance(kv_cache_group.kv_cache_spec, EncoderOnlyAttentionSpec):
1607
- # Encoder-only layers do not have KV cache, so we need to
1608
- # create a dummy block table and slot mapping for them.
1566
+ def _get_block_table_and_slot_mapping(kv_cache_gid: int):
1567
+ assert num_reqs_padded is not None and num_tokens_padded is not None
1568
+ kv_cache_spec = kv_cache_groups[kv_cache_gid].kv_cache_spec
1569
+ if isinstance(kv_cache_spec, EncoderOnlyAttentionSpec):
1570
blk_table_tensor = torch.zeros(
1571
(num_reqs_padded, 1),
1572
dtype=torch.int32,
@@ -1621,92 +1582,129 @@ class GPUModelRunner(
1582
blk_table_tensor = blk_table.get_device_tensor(num_reqs_padded)
1583
slot_mapping = blk_table.slot_mapping.gpu[:num_tokens_padded]
1584
1624
- # Fill unused with -1. Needed for reshape_and_cache in full cuda
1625
- # graph mode. `blk_table_tensor` -1 to match mamba PAD_SLOT_ID
1626
- slot_mapping[num_tokens:num_tokens_padded].fill_(-1)
1627
- blk_table_tensor[num_reqs:num_reqs_padded].fill_(-1)
1628
-
1629
- common_attn_metadata = CommonAttentionMetadata(
1630
- query_start_loc=query_start_loc,
1631
- query_start_loc_cpu=query_start_loc_cpu,
1632
- seq_lens=seq_lens,
1633
- _seq_lens_cpu=seq_lens_cpu,
1634
- _num_computed_tokens_cpu=num_computed_tokens_cpu,
1635
- num_actual_tokens=num_tokens_padded,
1636
- num_reqs=num_reqs_padded,
1637
- max_query_len=max_query_len,
1638
- max_seq_len=max_seq_len,
1639
- block_table_tensor=blk_table_tensor,
1640
- slot_mapping=slot_mapping,
1641
- logits_indices_padded=logits_indices_padded,
1642
- num_logits_indices=num_logits_indices,
1643
- causal=True,
1644
- encoder_seq_lens=encoder_seq_lens,
1645
- encoder_seq_lens_cpu=encoder_seq_lens_cpu,
1646
- dcp_local_seq_lens=dcp_local_seq_lens,
1647
- dcp_local_seq_lens_cpu=dcp_local_seq_lens_cpu,
1585
+ # Fill unused with -1. Needed for reshape_and_cache in full cuda
1586
+ # graph mode. `blk_table_tensor` -1 to match mamba PAD_SLOT_ID
1587
+ slot_mapping[num_tokens:num_tokens_padded].fill_(-1)
1588
+ blk_table_tensor[num_reqs:num_reqs_padded].fill_(-1)
1589
+
1590
+ return blk_table_tensor, slot_mapping
1591
+
1592
+ block_table_gid_0, slot_mapping_gid_0 = _get_block_table_and_slot_mapping(0)
1593
+ cm_base = CommonAttentionMetadata(
1594
+ query_start_loc=self.query_start_loc.gpu[: num_reqs_padded + 1],
1595
+ query_start_loc_cpu=self.query_start_loc.cpu[: num_reqs_padded + 1],
1596
+ seq_lens=self.seq_lens.gpu[:num_reqs_padded],
1597
+ _seq_lens_cpu=self.seq_lens.cpu[:num_reqs_padded],
1598
+ _num_computed_tokens_cpu=self.input_batch.num_computed_tokens_cpu_tensor[
1599
+ :num_reqs_padded
1600
+ ],
1601
+ num_reqs=num_reqs_padded,
1602
+ num_actual_tokens=num_tokens_padded,
1603
+ max_query_len=max_query_len,
1604
+ max_seq_len=max_seq_len,
1605
+ block_table_tensor=block_table_gid_0,
1606
+ slot_mapping=slot_mapping_gid_0,
1607
+ causal=True,
1608
+ )
1609
+
1610
+ if self.dcp_world_size > 1:
1611
+ self.dcp_local_seq_lens.cpu[:num_reqs] = get_dcp_local_seq_lens(
1612
+ self.seq_lens.cpu[:num_reqs],
1613
+ self.dcp_world_size,
1614
+ self.dcp_rank,
1615
+ self.parallel_config.cp_kv_cache_interleave_size,
1616
)
1617
+ self.dcp_local_seq_lens.cpu[num_reqs:].fill_(0)
1618
+ self.dcp_local_seq_lens.copy_to_gpu(num_reqs_padded)
1619
+
1620
+ cm_base.dcp_local_seq_lens = self.dcp_local_seq_lens.gpu[:num_reqs_padded]
1621
+ cm_base.dcp_local_seq_lens_cpu = self.dcp_local_seq_lens.cpu[
1622
+ :num_reqs_padded
1623
+ ]
1624
+
1625
+ if logits_indices is not None and self.cache_config.kv_sharing_fast_prefill:
1626
+ cm_base.num_logits_indices = logits_indices.size(0)
1627
+ cm_base.logits_indices_padded = self._prepare_kv_sharing_fast_prefill(
1628
+ logits_indices
1629
+ )
1630
+
1631
+ def _build_attn_group_metadata(
1632
+ kv_cache_gid: int,
1633
+ attn_gid: int,
1634
+ common_attn_metadata: CommonAttentionMetadata,
1635
+ ubid: int | None = None,
1636
+ ) -> None:
1637
+ attn_group = self.attn_groups[kv_cache_gid][attn_gid]
1638
+ cascade_attn_prefix_len = (
1639
+ cascade_attn_prefix_lens[kv_cache_gid][attn_gid]
1640
+ if cascade_attn_prefix_lens
1641
+ else 0
1642
+ )
1643
+
1644
+ builder = attn_group.get_metadata_builder(ubid or 0)
1645
+ extra_attn_metadata_args = {}
1646
+ if use_spec_decode and isinstance(builder, GDNAttentionMetadataBuilder):
1647
+ assert ubid is None, "UBatching not supported with GDN yet"
1648
+ extra_attn_metadata_args = dict(
1649
+ num_accepted_tokens=self.num_accepted_tokens.gpu[:num_reqs_padded],
1650
+ num_decode_draft_tokens_cpu=self.num_decode_draft_tokens.cpu[
1651
+ :num_reqs_padded
1652
+ ],
1653
+ )
1654
+
1655
+ if for_cudagraph_capture:
1656
+ attn_metadata_i = builder.build_for_cudagraph_capture(
1657
+ common_attn_metadata
1658
+ )
1659
+ else:
1660
+ attn_metadata_i = builder.build(
1661
+ common_prefix_len=cascade_attn_prefix_len,
1662
+ common_attn_metadata=common_attn_metadata,
1663
+ **extra_attn_metadata_args,
1664
+ )
1665
+
1666
+ if ubid is None:
1667
+ assert isinstance(attn_metadata, dict)
1668
+ attn_metadata_dict = attn_metadata
1669
+ else:
1670
+ assert isinstance(attn_metadata, list)
1671
+ attn_metadata_dict = attn_metadata[ubid]
1672
+
1673
+ for layer_name in attn_group.layer_names:
1674
+ attn_metadata_dict[layer_name] = attn_metadata_i
1675
+
1676
+ # Prepare the attention metadata for each KV cache group and make layers
1677
+ # in the same group share the same metadata.
1678
+ spec_decode_common_attn_metadata = None
1679
+ for kv_cache_gid, kv_cache_group in enumerate(kv_cache_groups):
1680
+ cm = copy(cm_base) # shallow copy
1681
+
1682
+ # Basically only the encoder seq_lens, block_table and slot_mapping change
1683
+ # for each kv_cache_group.
1684
+ cm.encoder_seq_lens, cm.encoder_seq_lens_cpu = self._get_encoder_seq_lens(
1685
+ num_scheduled_tokens or {},
1686
+ kv_cache_group.kv_cache_spec,
1687
+ num_reqs_padded,
1688
+ )
1689
+ if kv_cache_gid > 0:
1690
+ cm.block_table_tensor, cm.slot_mapping = (
1691
+ _get_block_table_and_slot_mapping(kv_cache_gid)
1692
+ )
1693
1694
if self.speculative_config and spec_decode_common_attn_metadata is None:
1695
if isinstance(self.drafter, EagleProposer):
1696
if self.drafter.attn_layer_names[0] in kv_cache_group.layer_names:
1653
- spec_decode_common_attn_metadata = common_attn_metadata
1697
+ spec_decode_common_attn_metadata = cm
1698
else:
1655
- spec_decode_common_attn_metadata = common_attn_metadata
1656
-
1657
- for attn_gid, attn_group in enumerate(self.attn_groups[kv_cache_gid]):
1658
- cascade_attn_prefix_len = (
1659
- cascade_attn_prefix_lens[kv_cache_gid][attn_gid]
1660
- if cascade_attn_prefix_lens
1661
- else 0
1662
- )
1663
- builder = attn_group.get_metadata_builder()
1664
-
1665
- extra_attn_metadata_args = {}
1666
- if use_spec_decode and isinstance(builder, GDNAttentionMetadataBuilder):
1667
- extra_attn_metadata_args = dict(
1668
- num_accepted_tokens=self.num_accepted_tokens.gpu[
1669
- :num_reqs_padded
1670
- ],
1671
- num_decode_draft_tokens_cpu=self.num_decode_draft_tokens.cpu[
1672
- :num_reqs_padded
1673
- ],
1674
- )
1699
+ spec_decode_common_attn_metadata = cm
1700
1701
+ for attn_gid in range(len(self.attn_groups[kv_cache_gid])):
1702
if ubatch_slices is not None:
1677
- common_attn_metadata_list = split_attn_metadata(
1678
- ubatch_slices, common_attn_metadata
1679
- )
1680
- for ubid, common_attn_metadata in enumerate(
1681
- common_attn_metadata_list
1682
- ):
1683
- builder = attn_group.get_metadata_builder(ubatch_id=ubid)
1684
- if for_cudagraph_capture:
1685
- attn_metadata_i = builder.build_for_cudagraph_capture(
1686
- common_attn_metadata
1687
- )
1688
- else:
1689
- attn_metadata_i = builder.build(
1690
- common_prefix_len=cascade_attn_prefix_len,
1691
- common_attn_metadata=common_attn_metadata,
1692
- )
1693
- for layer_name in kv_cache_group.layer_names:
1694
- assert type(attn_metadata) is list
1695
- attn_metadata[ubid][layer_name] = attn_metadata_i
1703
+ for ubid, _cm in enumerate(split_attn_metadata(ubatch_slices, cm)):
1704
+ _build_attn_group_metadata(kv_cache_gid, attn_gid, _cm, ubid)
1705
+
1706
else:
1697
- assert isinstance(attn_metadata, dict)
1698
- if for_cudagraph_capture:
1699
- attn_metadata_i = builder.build_for_cudagraph_capture(
1700
- common_attn_metadata
1701
- )
1702
- else:
1703
- attn_metadata_i = builder.build(
1704
- common_prefix_len=cascade_attn_prefix_len,
1705
- common_attn_metadata=common_attn_metadata,
1706
- **extra_attn_metadata_args,
1707
- )
1708
- for layer_name in attn_group.layer_names:
1709
- attn_metadata[layer_name] = attn_metadata_i
1707
+ _build_attn_group_metadata(kv_cache_gid, attn_gid, cm)
1708
1709
if self.is_mm_prefix_lm:
1710
req_doc_ranges = {}