Skip to content

Commit 469f85c

Browse files
authored
[Core][Optimization] change copy-on-write from dict[int, list] to list (vllm-project#4648)
1 parent 10760da commit 469f85c

File tree

12 files changed

+44
-44
lines changed

12 files changed

+44
-44
lines changed

tests/core/block/test_block_table.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,7 @@ def test_cow(block_size: int, sequence_len: int, append_len: int,
410410
expected_src = static_block_table.physical_block_ids[cow_block_id]
411411
expected_dst = appender_block_table.physical_block_ids[cow_block_id]
412412

413-
assert expected_src in cows
414-
assert expected_dst in cows[expected_src]
413+
assert (expected_src, expected_dst) in cows
415414
else:
416415
# Otherwise, there should be no copy-on-write.
417416
assert not cows
@@ -490,8 +489,7 @@ def test_cow_lookahead_simple(block_size: int, sequence_len: int,
490489
expected_src = static_block_table.physical_block_ids[cow_block_id]
491490
expected_dst = appender_block_table.physical_block_ids[cow_block_id]
492491

493-
assert expected_src in cows
494-
assert expected_dst in cows[expected_src]
492+
assert (expected_src, expected_dst) in cows
495493

496494
static_block_table.free()
497495
appender_block_table.free()

tests/core/test_block_manager.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import time
2+
from collections import defaultdict
23
from typing import List
34

45
import pytest
@@ -155,7 +156,10 @@ def test_append_slot_cow():
155156

156157
cows = block_manager.append_slots(child)
157158
assert cows
158-
for src_block, dst_blocks in cows.items():
159+
dict_cows = defaultdict(list)
160+
for src_block, dst_block in cows:
161+
dict_cows[src_block].append(dst_block)
162+
for src_block, dst_blocks in dict_cows.items():
159163
assert src_block not in dst_blocks
160164

161165
after_blocks = block_manager.get_num_free_gpu_blocks()

tests/core/test_scheduler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ def test_schedule_decode_blocks_to_copy_update():
636636

637637
# The last request should be swapped out.
638638
scheduler.block_manager.append_slots = MagicMock()
639-
scheduler.block_manager.append_slots.return_value = {2: [3]}
639+
scheduler.block_manager.append_slots.return_value = [(2, 3)]
640640

641641
budget = create_token_budget()
642642
remaining_running, output = scheduler._schedule_running(
@@ -845,7 +845,7 @@ def test_schedule_swapped_blocks_to_copy():
845845

846846
# The last request should be swapped out.
847847
scheduler.block_manager.append_slots = MagicMock()
848-
scheduler.block_manager.append_slots.return_value = {2: [3]}
848+
scheduler.block_manager.append_slots.return_value = [(2, 3)]
849849

850850
budget = create_token_budget()
851851
remaining_swapped, output = scheduler._schedule_swapped(

vllm/core/block/common.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from collections import defaultdict
2-
from typing import Dict, Iterable, List, Optional, Protocol
1+
from typing import Dict, Iterable, List, Optional, Protocol, Tuple
32

43
from vllm.core.block.interfaces import Block, BlockAllocator
54

@@ -111,7 +110,7 @@ def __init__(
111110
refcounter: RefCounterProtocol,
112111
allocator: BlockAllocator,
113112
):
114-
self._copy_on_writes: Dict[BlockId, List[BlockId]] = defaultdict(list)
113+
self._copy_on_writes: List[Tuple[BlockId, BlockId]] = []
115114
self._refcounter = refcounter
116115
self._allocator = allocator
117116

@@ -152,25 +151,25 @@ def cow_block_if_not_appendable(self, block: Block) -> Optional[BlockId]:
152151
# Track src/dst copy.
153152
assert src_block_id is not None
154153
assert block_id is not None
155-
self._copy_on_writes[src_block_id].append(block_id)
154+
self._copy_on_writes.append((src_block_id, block_id))
156155

157156
return block_id
158157

159-
def clear_cows(self) -> Dict[BlockId, List[BlockId]]:
158+
def clear_cows(self) -> List[Tuple[BlockId, BlockId]]:
160159
"""Clears the copy-on-write tracking information and returns the current
161160
state.
162161
163-
This method returns a dictionary mapping source block indices to lists
164-
of destination block indices for the current copy-on-write operations.
162+
This method returns a list mapping source block indices to
163+
destination block indices for the current copy-on-write operations.
165164
It then clears the internal tracking information.
166165
167166
Returns:
168-
Dict[BlockId, List[BlockId]]: A dictionary mapping source
169-
block indices to lists of destination block indices for the
167+
List[Tuple[BlockId, BlockId]]: A list mapping source
168+
block indices to destination block indices for the
170169
current copy-on-write operations.
171170
"""
172-
cows = dict(self._copy_on_writes)
173-
self._copy_on_writes.clear()
171+
cows = self._copy_on_writes
172+
self._copy_on_writes = []
174173
return cows
175174

176175

vllm/core/block/cpu_gpu_block_allocator.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, FrozenSet, List, Optional
1+
from typing import Dict, FrozenSet, List, Optional, Tuple
22

33
from vllm.core.block.interfaces import (Block, BlockAllocator, BlockId,
44
DeviceAwareBlockAllocator)
@@ -185,13 +185,13 @@ def get_num_free_blocks(self, device: Device) -> int:
185185
def get_num_total_blocks(self, device: Device) -> int:
186186
return self._allocators[device].get_num_total_blocks()
187187

188-
def clear_copy_on_writes(self) -> Dict[int, List[int]]:
188+
def clear_copy_on_writes(self) -> List[Tuple[int, int]]:
189189
"""Clears the copy-on-write (CoW) state and returns the mapping of
190190
source to destination block IDs.
191191
192192
Returns:
193-
Dict[int, List[int]]: A dictionary mapping source block IDs to lists
194-
of destination block IDs.
193+
List[Tuple[int, int]]: A list mapping source block IDs to
194+
destination block IDs.
195195
"""
196196
# CoW only supported on GPU
197197
device = Device.GPU

vllm/core/block/interfaces.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC, abstractmethod
2-
from typing import Dict, FrozenSet, List, Optional, Protocol
2+
from typing import FrozenSet, List, Optional, Protocol, Tuple
33

44
from vllm.utils import Device
55

@@ -122,7 +122,7 @@ def all_block_ids(self) -> FrozenSet[int]:
122122
pass
123123

124124
@abstractmethod
125-
def clear_copy_on_writes(self) -> Dict[int, List[int]]:
125+
def clear_copy_on_writes(self) -> List[Tuple[int, int]]:
126126
pass
127127

128128
@abstractmethod
@@ -187,7 +187,7 @@ def all_block_ids(self) -> FrozenSet[int]:
187187
pass
188188

189189
@abstractmethod
190-
def clear_copy_on_writes(self) -> Dict[int, List[int]]:
190+
def clear_copy_on_writes(self) -> List[Tuple[int, int]]:
191191
pass
192192

193193
@abstractmethod

vllm/core/block/naive_block.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, FrozenSet, Iterable, List, Optional, Set
1+
from typing import FrozenSet, Iterable, List, Optional, Set, Tuple
22

33
from vllm.core.block.common import (CopyOnWriteTracker, RefCounter,
44
get_all_blocks_recursively)
@@ -175,12 +175,12 @@ def cow_block_if_not_appendable(self, block: Block) -> Optional[BlockId]:
175175
"""
176176
return self._cow_tracker.cow_block_if_not_appendable(block)
177177

178-
def clear_copy_on_writes(self) -> Dict[BlockId, List[BlockId]]:
178+
def clear_copy_on_writes(self) -> List[Tuple[BlockId, BlockId]]:
179179
"""Returns the copy-on-write source->destination mapping and clears it.
180180
181181
Returns:
182-
Dict[BlockId, List[BlockId]]: A dictionary mapping source
183-
block indices to lists of destination block indices.
182+
List[Tuple[BlockId, BlockId]]: A list mapping source
183+
block indices to destination block indices.
184184
"""
185185
return self._cow_tracker.clear_cows()
186186

vllm/core/block/prefix_caching_block.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Token blocks."""
22
from itertools import takewhile
33
from os.path import commonprefix
4-
from typing import Dict, FrozenSet, Iterable, List, Optional
4+
from typing import Dict, FrozenSet, Iterable, List, Optional, Tuple
55

66
from vllm.core.block.common import (CopyOnWriteTracker,
77
get_all_blocks_recursively)
@@ -337,12 +337,12 @@ def cow_block_if_not_appendable(self, block: Block) -> Optional[BlockId]:
337337
"""
338338
return self._cow_tracker.cow_block_if_not_appendable(block)
339339

340-
def clear_copy_on_writes(self) -> Dict[BlockId, List[BlockId]]:
340+
def clear_copy_on_writes(self) -> List[Tuple[BlockId, BlockId]]:
341341
"""Returns the copy-on-write source->destination mapping and clears it.
342342
343343
Returns:
344-
Dict[BlockId, List[BlockId]]: A dictionary mapping source
345-
block indices to lists of destination block indices.
344+
List[Tuple[BlockId, BlockId]]: A list mapping source
345+
block indices to destination block indices.
346346
"""
347347
return self._cow_tracker.clear_cows()
348348

vllm/core/block_manager_v1.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from os.path import commonprefix
66
from typing import Dict, List, Optional
77
from typing import Sequence as GenericSequence
8-
from typing import Set
8+
from typing import Set, Tuple
99

1010
from vllm.block import BlockTable, PhysicalTokenBlock
1111
from vllm.core.evictor_v1 import EvictionPolicy, Evictor, make_evictor
@@ -386,7 +386,7 @@ def append_slots(
386386
self,
387387
seq: Sequence,
388388
num_lookahead_slots: int = 0,
389-
) -> Dict[int, List[int]]:
389+
) -> List[Tuple[int, int]]:
390390
"""Allocate a physical slot for a new token."""
391391
logical_blocks = seq.logical_token_blocks
392392
block_table = self.block_tables[seq.seq_id]
@@ -405,7 +405,7 @@ def append_slots(
405405
# Allocate a new physical block.
406406
new_block = self._allocate_last_physical_block(seq)
407407
block_table.append(new_block)
408-
return {}
408+
return []
409409

410410
# We want to append the token to the last physical block.
411411
last_block = block_table[-1]
@@ -418,15 +418,15 @@ def append_slots(
418418
maybe_new_block = self._maybe_promote_last_block(
419419
seq, last_block)
420420
block_table[-1] = maybe_new_block
421-
return {}
421+
return []
422422
else:
423423
# The last block is shared with other sequences.
424424
# Copy on Write: Allocate a new block and copy the tokens.
425425
new_block = self._allocate_last_physical_block(seq)
426426

427427
block_table[-1] = new_block
428428
self.gpu_allocator.free(last_block)
429-
return {last_block.block_number: [new_block.block_number]}
429+
return [(last_block.block_number, new_block.block_number)]
430430

431431
def fork(self, parent_seq: Sequence, child_seq: Sequence) -> None:
432432
# NOTE: fork does not allocate a new physical block.

vllm/core/block_manager_v2.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""A block manager that manages token blocks."""
22
from typing import Dict, List, Optional
33
from typing import Sequence as GenericSequence
4+
from typing import Tuple
45

56
from vllm.core.block.block_table import BlockTable
67
from vllm.core.block.cpu_gpu_block_allocator import CpuGpuBlockAllocator
@@ -166,7 +167,7 @@ def append_slots(
166167
self,
167168
seq: Sequence,
168169
num_lookahead_slots: int,
169-
) -> Dict[int, List[int]]:
170+
) -> List[Tuple[int, int]]:
170171

171172
block_table = self.block_tables[seq.seq_id]
172173

0 commit comments

Comments
 (0)