@@ -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
0 commit comments