#!/usr/bin/env python3 """ Apply (or revert) PD-sep profiling instrumentation to vLLM. Inserts time.perf_counter_ns() emit calls into mooncake_connector.py at: - get_num_new_matched_tokens() [D: cache hit, delta] - update_state_after_alloc() [D: blocks allocated] - send_kv_to_decode() [P: zmq from D arrived] - record_send_reqs() [P: prefill ready event set] - _send_blocks() [P: RDMA start/end + bytes] - process_pulling_result() [D: RDMA recv complete] - request_finished() [common: request lifecycle end] And into scheduler.py at: - _try_promote_blocked_waiting_request() [D: request promoted] Marker comment "# PD_PROFILE_PATCH" is added so revert can locate inserts. Usage: python apply_patches.py [--apply | --revert] [--vllm-root PATH] The patches are idempotent: --apply on already-patched code is a no-op. """ import argparse import re import sys from pathlib import Path MARKER = "# PD_PROFILE_PATCH" # Default location: venv vLLM 0.18.1 on dash0 DEFAULT_VLLM_ROOT = Path.home() / "agentic-kv/.venv/lib/python3.12/site-packages/vllm" def _find_block(text: str, signature: str) -> tuple[int, int] | None: """Find the start/end line numbers of a function or method definition.""" lines = text.splitlines() for i, line in enumerate(lines): if signature in line: # find indent stripped = line.lstrip() indent = len(line) - len(stripped) # find function end: next line with <= indent and not blank for j in range(i + 1, len(lines)): next_line = lines[j] if next_line.strip() == "": continue next_indent = len(next_line) - len(next_line.lstrip()) if next_indent <= indent and not next_line.lstrip().startswith("#"): return i, j return i, len(lines) return None def _insert_after_line(text: str, line_no: int, snippet: str) -> str: """Insert snippet after line_no (1-indexed). snippet should not include trailing newline.""" lines = text.splitlines() lines.insert(line_no, snippet) return "\n".join(lines) + ("\n" if text.endswith("\n") else "") def _already_patched(text: str) -> bool: return MARKER in text def _revert(text: str) -> str: """Remove all lines containing the marker.""" lines = text.splitlines() out = [l for l in lines if MARKER not in l] return "\n".join(out) + ("\n" if text.endswith("\n") else "") # ─── Patch definitions ────────────────────────────────────────────────────── def patch_mooncake_connector(text: str) -> str: """Apply patches to mooncake_connector.py.""" if _already_patched(text): print(" mooncake_connector.py already patched, skipping") return text # 1. Add import at top (after first 'import' block) import_snippet = ( "from vllm.distributed.kv_transfer.kv_connector.v1.mooncake " "import _pd_profile as _pdp " + MARKER ) # Insert after the last 'import' statement near the top lines = text.splitlines() last_import_line = 0 for i, line in enumerate(lines[:80]): if line.startswith("import ") or line.startswith("from "): last_import_line = i lines.insert(last_import_line + 1, import_snippet) text = "\n".join(lines) + "\n" # 2. Patch get_num_new_matched_tokens (D side, scheduler) # Inject right before "return count, True" and "return 0, False" text = re.sub( r"(\n if count > 0:\n return count, True\n)", r"\n if count > 0:\n _pdp.emit('D_get_num_matched', " r"req_id=request.request_id, role='kv_consumer', " r"num_local_cached=num_computed_tokens, prompt_tokens=len(token_ids), " r"remote_total=remote_total, delta_to_pull=count) " + MARKER + "\n" r" return count, True\n", text, count=1, ) # 3. Patch update_state_after_alloc (D side) — add emit for blocks allocated # The function is around line 348. Find "self._reqs_need_recv[" assignment. text = re.sub( r"(\n self\._reqs_need_recv\[request_id\] = PullReqMeta\()", r"\n _pdp.emit('D_alloc_blocks', req_id=request_id, " r"role='kv_consumer', num_local_blocks=len(local_block_ids), " r"num_external_tokens=num_external_tokens) " + MARKER + r"\n self._reqs_need_recv[request_id] = PullReqMeta(", text, count=1, ) # 4. Patch send_kv_to_decode entry (P side) — ZMQ message received text = re.sub( r"( async def send_kv_to_decode\(\n" r" self, identity: bytes, sock: zmq\.asyncio\.Socket, meta: MooncakeXferMetadata\n" r" \):\n)", r"\1 _pdp.emit('P_zmq_received', role='kv_producer', " r"num_reqs=len(meta.req_blocks), remote_host=meta.remote_hostname, " r"transfer_ids=[tid for tid, _ in meta.req_blocks.values()]) " + MARKER + "\n", text, count=1, ) # 5. Patch _send_blocks (P side) — wrap RDMA write # Inject before and after self.engine.batch_transfer_sync_write text = re.sub( r"( start_time = time\.perf_counter\(\)\n" r" ret_value = self\.engine\.batch_transfer_sync_write\(\n" r" remote_session, src_ptrs, dst_ptrs, lengths\n" r" \))", r" _pdp.emit('P_rdma_start', role='kv_producer', " r"num_ops=len(src_ptrs), bytes_total=sum(lengths), remote=str(remote_session)) " + MARKER + "\n" r"\1\n" r" _pdp.emit('P_rdma_end', role='kv_producer', " r"num_ops=len(src_ptrs), bytes_total=sum(lengths), ret=ret_value) " + MARKER, text, count=1, ) # 6. Patch process_pulling_result (D side) — RDMA recv complete (success path) # Match the specific pattern inside `if pull_meta.pull_tasks_count == 0:` text = re.sub( r"(pull_meta\.pull_tasks_count -= 1\n" r" if pull_meta\.pull_tasks_count == 0:\n" r")( self\.finished_recving_reqs\.add\(pull_meta\.d_req_id\))", r"\1 _pdp.emit('D_recv_complete', req_id=pull_meta.d_req_id, " r"transfer_id=pull_meta.transfer_id, role='kv_consumer') " + MARKER + "\n" r"\2", text, count=1, ) # 7. Patch request_finished (P side) — when prefill blocks are marked ready to send # Find: self._reqs_need_send[request.request_id] = (request, send_block_ids) text = re.sub( r"(\n if delay_free_blocks:\n" r" self\._reqs_need_send\[request\.request_id\] = \(request, send_block_ids\))", r"\n if delay_free_blocks:\n" r" _pdp.emit('P_prefill_done', req_id=request.request_id, " r"transfer_id=params.get('transfer_id', ''), role='kv_producer', " r"num_send_blocks=len(send_block_ids), num_prompt_tokens=request.num_prompt_tokens) " + MARKER + "\n" r" self._reqs_need_send[request.request_id] = (request, send_block_ids)", text, count=1, ) return text def patch_scheduler(text: str) -> str: """Patch v1/core/sched/scheduler.py for D-side request promotion.""" if _already_patched(text): print(" scheduler.py already patched, skipping") return text # Add import at the top of the file lines = text.splitlines() last_import_line = 0 for i, line in enumerate(lines[:100]): if line.startswith("import ") or line.startswith("from "): last_import_line = i import_snippet = ( "try:\n" " from vllm.distributed.kv_transfer.kv_connector.v1.mooncake import _pd_profile as _pdp\n" "except Exception:\n" " class _pdp: # fallback no-op\n" " @staticmethod\n" " def emit(*a, **kw): pass\n" " @staticmethod\n" " def enabled(): return False\n" f"{MARKER}" ) lines.insert(last_import_line + 1, import_snippet) text = "\n".join(lines) + "\n" # Patch _update_waiting_for_remote_kv — match exact `request: Request` (no quotes) text = re.sub( r"( def _update_waiting_for_remote_kv\(self, request: Request\) -> None:\n)", r"\1 _pdp.emit('D_request_promoted', req_id=request.request_id, " r"role='kv_consumer', num_computed_tokens=request.num_computed_tokens) " + MARKER + "\n", text, count=1, ) return text # ─── Driver ──────────────────────────────────────────────────────────────── def apply_to_file(path: Path, patcher) -> bool: if not path.exists(): print(f" SKIP {path} (not found)") return False original = path.read_text() patched = patcher(original) if patched == original: print(f" unchanged: {path}") return False path.write_text(patched) n_marks = patched.count(MARKER) print(f" patched ({n_marks} marks): {path}") return True def revert_file(path: Path) -> bool: if not path.exists(): return False original = path.read_text() reverted = _revert(original) if reverted == original: print(f" no marks found: {path}") return False path.write_text(reverted) print(f" reverted: {path}") return True def install_profile_module(vllm_root: Path) -> None: """Copy pd_profile.py to mooncake/_pd_profile.py inside vLLM.""" src = Path(__file__).parent / "pd_profile.py" dst = vllm_root / "distributed/kv_transfer/kv_connector/v1/mooncake/_pd_profile.py" dst.parent.mkdir(parents=True, exist_ok=True) dst.write_text(src.read_text()) print(f" installed: {dst}") def main(): ap = argparse.ArgumentParser() ap.add_argument("--apply", action="store_true") ap.add_argument("--revert", action="store_true") ap.add_argument("--vllm-root", type=Path, default=DEFAULT_VLLM_ROOT) args = ap.parse_args() if not (args.apply or args.revert) or (args.apply and args.revert): ap.error("Specify exactly one of --apply or --revert") root = args.vllm_root if not root.exists(): print(f"ERROR: vLLM root not found: {root}") sys.exit(1) mc_path = root / "distributed/kv_transfer/kv_connector/v1/mooncake/mooncake_connector.py" sched_path = root / "v1/core/sched/scheduler.py" if args.apply: print(f"Applying PD-sep profile patches to {root}") install_profile_module(root) apply_to_file(mc_path, patch_mooncake_connector) apply_to_file(sched_path, patch_scheduler) else: print(f"Reverting PD-sep profile patches from {root}") revert_file(mc_path) revert_file(sched_path) # also remove the module prof_module = root / "distributed/kv_transfer/kv_connector/v1/mooncake/_pd_profile.py" if prof_module.exists(): prof_module.unlink() print(f" removed: {prof_module}") if __name__ == "__main__": main()