[MRV2] Collect KV transfer results before the drafter runs (#62041)

Signed-off-by: Nick Hill <nickhill123@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Hill
2026-10-17 10:27:57 -07:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 763e3f7c91
commit 0c5dd18e7a
3 changed files with 52 additions and 10 deletions
+25 -3
View File
@@ -22,7 +22,12 @@ def _make_connector(
backend.bind_connector_metadata.side_effect = lambda _: events.append("bind")
backend.start_load_kv.side_effect = lambda *_args, **_kwargs: events.append("start")
backend.wait_for_save.side_effect = lambda: events.append("wait")
backend.get_transfer_results.return_value = KVConnectorTransferResults()
def get_transfer_results(_finished_req_ids):
events.append("results")
return KVConnectorTransferResults()
backend.get_transfer_results.side_effect = get_transfer_results
backend.get_block_ids_with_load_errors.return_value = set()
backend.get_kv_connector_stats.return_value = None
backend.get_kv_connector_kv_cache_events.return_value = None
@@ -73,7 +78,7 @@ def test_load_start_phase(
)
connector.post_forward(set())
assert events == ["handle", "bind", "start", "wait", "clear"]
assert events == ["handle", "bind", "start", "wait", "results", "clear"]
kwargs = connector.kv_connector.start_load_kv.call_args.kwargs
assert kwargs["request_state_indices"] is request_indices
@@ -94,4 +99,21 @@ def test_no_forward_starts_deferred_load_once(monkeypatch: pytest.MonkeyPatch):
connector.no_forward(_scheduler_output(False))
assert events == ["handle", "bind", "start", "wait", "clear"]
assert events == ["handle", "bind", "start", "wait", "results", "clear"]
def test_deferred_finalize_collects_results_before_saving(
monkeypatch: pytest.MonkeyPatch,
):
"""With a drafter, the transfer results are collected right after the
target forward, and the saves and metadata are finalized after the
drafter writes its KV."""
events: list[str] = []
connector = _make_connector(monkeypatch, events)
connector.pre_forward(_scheduler_output(False))
connector.post_forward(set(), defer_finalize=True)
assert events == ["handle", "bind", "start", "results"]
connector.finalize_step()
assert events == ["handle", "bind", "start", "results", "wait", "clear"]
+19 -4
View File
@@ -41,9 +41,14 @@ class KVConnector:
def finish_forward(self) -> None:
pass
def post_forward(self, finished_req_ids: set[str]) -> KVConnectorOutput | None:
def post_forward(
self, finished_req_ids: set[str], defer_finalize: bool = False
) -> KVConnectorOutput | None:
return None
def finalize_step(self) -> None:
pass
def no_forward(self, scheduler_output: "SchedulerOutput") -> ModelRunnerOutput:
return EMPTY_MODEL_RUNNER_OUTPUT
@@ -128,7 +133,9 @@ class ActiveKVConnector(KVConnector):
def reset_capture_state(self) -> None:
self.kv_connector.reset_capture_state()
def post_forward(self, finished_req_ids: set[str]) -> KVConnectorOutput | None:
def post_forward(
self, finished_req_ids: set[str], defer_finalize: bool = False
) -> KVConnectorOutput | None:
if self._disabled:
return None
@@ -136,7 +143,8 @@ class ActiveKVConnector(KVConnector):
self._start_load_kv()
output = KVConnectorOutput()
self.kv_connector.wait_for_save()
if not defer_finalize:
self.kv_connector.wait_for_save()
transfer_results = self.kv_connector.get_transfer_results(finished_req_ids)
output.finished_sending = transfer_results.finished_sending or None
output.finished_recving = transfer_results.finished_recving or None
@@ -147,9 +155,16 @@ class ActiveKVConnector(KVConnector):
output.kv_connector_worker_meta = (
self.kv_connector.build_connector_worker_meta()
)
self.kv_connector.clear_connector_metadata()
if not defer_finalize:
self.kv_connector.clear_connector_metadata()
return output
def finalize_step(self) -> None:
if self._disabled:
return
self.kv_connector.wait_for_save()
self.kv_connector.clear_connector_metadata()
def no_forward(self, scheduler_output: "SchedulerOutput") -> ModelRunnerOutput:
if self._disabled:
return EMPTY_MODEL_RUNNER_OUTPUT
+8 -3
View File
@@ -2207,6 +2207,12 @@ class GPUModelRunner(LoRAModelRunnerMixin):
pending_aux_output=pending_aux_output,
)
# Collect KV transfer results now; finalize saves after the drafter runs.
defer_kv_finalize = self.speculator is not None
model_runner_output.kv_connector_output = self.kv_connector.post_forward(
finished_req_ids, defer_finalize=defer_kv_finalize
)
mm_inputs: tuple[list[torch.Tensor], torch.Tensor] | None = None
# The encoder runner exists only on the first PP rank, so later ranks
# have no cached embeddings to gather.
@@ -2288,9 +2294,8 @@ class GPUModelRunner(LoRAModelRunnerMixin):
self.req_states.draft_tokens, input_batch
)
# Post-step KV connector related operations.
kv_connector_output = self.kv_connector.post_forward(finished_req_ids)
model_runner_output.kv_connector_output = kv_connector_output
if defer_kv_finalize:
self.kv_connector.finalize_step()
model_runner_output.ec_connector_output = ec_connector_output
return async_output