Skip to content

Commit 417bb7c

Browse files
committed
Merge remote-tracking branch 'origin/develop' into aio-strict-timeout
# Conflicts: # ucm/integration/vllm/hma_connector.py
2 parents 9a050bc + bd2967f commit 417bb7c

18 files changed

Lines changed: 268 additions & 40 deletions

File tree

ucm/integration/vllm/hma_connector.py

Lines changed: 113 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ def __init__(
295295
self.is_ascend_layout = False
296296
self.fa_group_ids, self.window_group_ids = [], []
297297
self.group_metas: dict[int, KVCacheGroupMeta] = {}
298+
self.file_size = {}
299+
300+
# The maximum token block size across all groups, used for aligning the number of computed tokens in the scheduler.
301+
self.max_token_block_size = 0
298302
self._init_group_metas()
299303
self.fa_store: Optional[UcmKVStoreBaseV1] = None
300304
self.wa_store: Optional[UcmKVStoreBaseV1] = None
@@ -395,7 +399,6 @@ def _init_group_metas(self) -> None:
395399
window_size = getattr(spec, "sliding_window", None)
396400
compress_ratio = getattr(spec, "compress_ratio", 1)
397401
token_block_size = kv_cache_spec.block_size
398-
399402
if self.is_ascend_layout:
400403
# Ascend compressed groups expose a logical block span scaled by
401404
# the compression ratio.
@@ -419,12 +422,53 @@ def _init_group_metas(self) -> None:
419422
self.window_group_ids.append(group_id)
420423

421424
tail_blocks = max(tail_tokens // token_block_size, 1)
425+
self.max_token_block_size = max(self.max_token_block_size, token_block_size)
422426
self.group_metas[group_id] = KVCacheGroupMeta(
423427
group_id=group_id,
424428
token_block_size=token_block_size,
425429
tail_blocks=tail_blocks,
426430
tail_tokens=tail_tokens,
427431
)
432+
logger.info_once(
433+
f"max token_block_size of all groups: {self.max_token_block_size}"
434+
)
435+
assert self.max_token_block_size % self.DEFAULT_HASH_BLOCK_SIZE == 0
436+
# get file size for block gc
437+
if len(layer_compress_ratios) < 61:
438+
# for dsv4 flash
439+
num_c4a_layers = 21
440+
num_c128a_layers = 20
441+
# TODO only support for dp tp
442+
num_total_layers = 43
443+
else:
444+
# for dsv4 pro
445+
num_c4a_layers = 30
446+
num_c128a_layers = 31
447+
num_total_layers = 61
448+
449+
if (
450+
self._vllm_config.speculative_config is not None
451+
and self._vllm_config.speculative_config.num_speculative_tokens > 0
452+
):
453+
num_total_layers += 1
454+
455+
# TODO we should get file size in worker thread
456+
if self.is_ascend_layout:
457+
self.file_size["FA"] = (
458+
131072 + 16384 + 256
459+
) * num_c4a_layers + 4096 * num_c128a_layers
460+
self.file_size["WA"] = (
461+
131072 * num_total_layers + (32768 + 8192) * num_c4a_layers
462+
)
463+
else:
464+
self.file_size["FA"] = (
465+
37376 + 8448
466+
) * num_c4a_layers + 1168 * num_c128a_layers
467+
self.file_size["WA"] = (37376 * 2) * num_total_layers + (
468+
8192 + 32768
469+
) * num_c4a_layers
470+
self.file_size["FA"] = round_up(self.file_size["FA"], 4096)
471+
self.file_size["WA"] = round_up(self.file_size["WA"], 4096)
428472

429473
def _create_fa_store(
430474
self,
@@ -540,10 +584,17 @@ def _create_store(
540584
padded_size = round_up(sum(tensor_size_list), aligned_size)
541585
config["shard_size"] = padded_size
542586
config["block_size"] = padded_size
587+
if self.file_size[label] != padded_size:
588+
logger.info_once(
589+
f"GC file size of {label} does not match real file size. "
590+
f"Worker: {padded_size}, Scheduler: {self.file_size[label]}"
591+
)
543592
# MLA stores aggregate TP shards under one logical rank group.
544593
config["local_rank_size"] = self.tp_size if self.is_mla else 1
545594
if cpu_affinity_cores:
546595
config["cpu_affinity_cores"] = list(cpu_affinity_cores)
596+
else:
597+
config["block_size"] = self.file_size[label]
547598
logger.info(
548599
f"create FAWA {label} {name} with config: "
549600
f"{self._summarize_store_config(config)}"
@@ -655,20 +706,17 @@ def get_num_new_matched_tokens(
655706
request: "Request",
656707
num_computed_tokens: int,
657708
) -> tuple[int, bool]:
658-
if num_computed_tokens % self.hash_block_size != 0:
659-
raise RuntimeError(
660-
f"FAWA requires aligned computed tokens, got "
661-
f"{num_computed_tokens} with block size {self.hash_block_size}."
662-
)
663-
hbm_hit_block_num = num_computed_tokens // self.hash_block_size
709+
wa_hbm_hit_block_num = num_computed_tokens // self.hash_block_size
710+
wa_computed_tokens = wa_hbm_hit_block_num * self.hash_block_size
711+
664712
canonical_hashes = self.generate_hash(
665713
self.hash_block_size, request.all_token_ids, self._seed
666714
)
667715

668716
if self.persist_token_threshold > request.num_tokens:
669717
return 0, False
670718

671-
external_keys = canonical_hashes[hbm_hit_block_num:]
719+
external_keys = canonical_hashes[wa_hbm_hit_block_num:]
672720
if not external_keys:
673721
return 0, False
674722

@@ -682,24 +730,34 @@ def get_num_new_matched_tokens(
682730
)
683731
self._record_counter("connector_lookup_errors_total")
684732

685-
total_hit_block_num = hbm_hit_block_num + external_hit_blocks
686-
external_hit_tokens = external_hit_blocks * self.hash_block_size
687-
num_total_hit_tokens = total_hit_block_num * self.hash_block_size
733+
total_hit_block_num = wa_hbm_hit_block_num + external_hit_blocks
734+
num_total_hit_tokens = (
735+
external_hit_blocks * self.hash_block_size + wa_computed_tokens
736+
)
737+
external_hit_tokens = num_total_hit_tokens - num_computed_tokens
738+
688739
if num_total_hit_tokens == request.num_tokens:
689740
external_hit_tokens -= 1
690741

742+
if external_hit_blocks == 0:
743+
external_hit_tokens = 0
744+
num_total_hit_tokens = num_computed_tokens
745+
746+
# TODO :for HMA, vllm should offer all kv group's prefix block hits,so that more FA blocks can be reused
691747
self.requests_meta[request.request_id] = FAWARequestMeta(
692748
ucm_block_ids=canonical_hashes,
693-
hbm_hit_block_num=hbm_hit_block_num,
749+
hbm_hit_block_num=wa_hbm_hit_block_num,
694750
total_hit_block_num=total_hit_block_num,
695-
num_token_ids=len(request.all_token_ids),
751+
num_token_ids=request.num_tokens,
696752
token_processed=num_total_hit_tokens,
697753
)
698754
logger.info_once(
699755
f"FAWA request_id: {request.request_id}, "
700-
f"total_blocks_num: {len(canonical_hashes)}, "
701-
f"hit hbm: {hbm_hit_block_num}, "
702-
f"hit external: {external_hit_blocks}"
756+
f"total tokens: {request.num_tokens}, "
757+
f"hit hbm tokens: {num_computed_tokens}, "
758+
f"hit external tokens: {external_hit_tokens}, "
759+
f"load blocks: {total_hit_block_num - wa_hbm_hit_block_num}, "
760+
f"dump blocks: {len(canonical_hashes) - total_hit_block_num}, "
703761
)
704762
return external_hit_tokens, False
705763

@@ -1018,7 +1076,11 @@ def start_load_kv(self, forward_context: "ForwardContext", **kwargs) -> None:
10181076
for request_id, request in metadata.request_meta.items():
10191077
if not request.load_keys:
10201078
continue
1021-
group0_vllm_block_ids = set(request.load_vllm_block_ids[0])
1079+
all_group_vllm_block_ids = {
1080+
block_id
1081+
for block_ids in request.load_vllm_block_ids
1082+
for block_id in block_ids
1083+
}
10221084
try:
10231085
if self.fa_store is None:
10241086
raise RuntimeError("FA store is not initialized.")
@@ -1039,7 +1101,7 @@ def start_load_kv(self, forward_context: "ForwardContext", **kwargs) -> None:
10391101
self.fa_store,
10401102
request.load_keys,
10411103
fa_ptrs,
1042-
group0_vllm_block_ids,
1104+
all_group_vllm_block_ids,
10431105
)
10441106
)
10451107

@@ -1056,7 +1118,7 @@ def start_load_kv(self, forward_context: "ForwardContext", **kwargs) -> None:
10561118
self.wa_store,
10571119
window_keys,
10581120
window_ptrs,
1059-
group0_vllm_block_ids,
1121+
all_group_vllm_block_ids,
10601122
)
10611123
)
10621124
except Exception as e:
@@ -1066,7 +1128,7 @@ def start_load_kv(self, forward_context: "ForwardContext", **kwargs) -> None:
10661128
)
10671129
self._record_load_error(
10681130
"connector_load_submit_errors_total",
1069-
group0_vllm_block_ids,
1131+
all_group_vllm_block_ids,
10701132
)
10711133

10721134
for load_task in tasks:
@@ -1082,6 +1144,8 @@ def wait_for_save(self) -> None:
10821144
if self.wa_store is None:
10831145
raise RuntimeError("WA store is not initialized.")
10841146

1147+
self._poll_completed_dump_tasks()
1148+
10851149
fa_dump_keys: list[bytes] = []
10861150
wa_dump_keys: list[bytes] = []
10871151
fa_ptr_rows: list[np.ndarray] = []
@@ -1224,6 +1288,35 @@ def wait_for_save(self) -> None:
12241288
logger.error(f"dump FAWA kv cache failed. {type(e).__name__}: {e}")
12251289
self._record_counter("connector_dump_submit_errors_total")
12261290

1291+
def _poll_completed_dump_tasks(self) -> None:
1292+
"""Reap completed FAWA dump tasks without waiting for in-flight tasks."""
1293+
1294+
for request_ids, dump_tasks in list(self.tp_dump_tasks.items()):
1295+
in_flight_tasks = []
1296+
for dump_task in dump_tasks:
1297+
task_finished = False
1298+
try:
1299+
task_finished = dump_task.store.check(dump_task.task)
1300+
if not task_finished:
1301+
in_flight_tasks.append(dump_task)
1302+
continue
1303+
1304+
dump_task.store.wait(dump_task.task)
1305+
except Exception as e:
1306+
logger.error(
1307+
"Best-effort FAWA dump task failed; external cache may miss. "
1308+
f"label={dump_task.label}, keys={dump_task.key_count}, "
1309+
f"{type(e).__name__}: {e}"
1310+
)
1311+
finally:
1312+
if task_finished:
1313+
self.device.destroy_event_handle(dump_task.event_handle)
1314+
1315+
if in_flight_tasks:
1316+
self.tp_dump_tasks[request_ids] = in_flight_tasks
1317+
else:
1318+
self.tp_dump_tasks.pop(request_ids, None)
1319+
12271320
def _drain_best_effort_dump_tasks(self, finished_req_ids: set[str]) -> None:
12281321
"""Best-effort wait for FAWA dump tasks.
12291322

ucm/integration/vllm/patch/apply_patch.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def get_vllm_version() -> Optional[str]:
114114

115115
def get_supported_versions() -> list[str]:
116116
"""Get patch-required vLLM versions."""
117-
return ["0.11.0", "0.17.0", "0.18.0", "0.19.1"]
117+
return ["0.11.0", "0.17.0", "0.18.0", "0.19.1", "0.20.2"]
118118

119119

120120
def apply_all_patches() -> None:
@@ -153,6 +153,9 @@ def apply_all_patches() -> None:
153153
case "0.19.1":
154154
logger.info("UCM patching vllm for pc...")
155155
import ucm.integration.vllm.patch.v0191.vllm.pc_patch
156+
case "0.20.2":
157+
logger.info("UCM patching vllm for pc...")
158+
import ucm.integration.vllm.patch.v0202.vllm.pc_patch
156159
case _:
157160
pass
158161

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from ucm.integration.vllm.patch.utils import patch_or_inject, when_imported
2+
from ucm.logger import init_logger
3+
4+
logger = init_logger(__name__)
5+
6+
7+
@when_imported("vllm.v1.core.sched.scheduler")
8+
def patch_core_sched_scheduler(mod):
9+
logger.debug(f"Patched {mod} called")
10+
11+
from ucm.integration.vllm.patch.v0202.vllm.v1.core.sched import scheduler
12+
13+
patch_or_inject(
14+
mod.Scheduler,
15+
"_update_requests_with_invalid_blocks",
16+
scheduler.Scheduler._update_requests_with_invalid_blocks,
17+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)