Repository navigation
feat(inference): router replay with KV cache offload #3831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+225
−14
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
a827863
feat(inference): router replay with disaggregated P/D on vLLM 0.31
faresobeid 28c9c7a
feat(inference): router replay with KV cache offload on vLLM 0.31
faresobeid b1b2742
refactor(inference): send P/D decode routing rows from their first fo…
faresobeid dffda53
Merge branch 'feat/router-replay-v2-pd' into feat/router-replay-kv-of…
faresobeid 1dc6dc3
perf(inference): don't clamp KV offload loads for preempted requests
faresobeid 978146c
refactor(inference): take the end-anchored routing start from the pac…
faresobeid f6cd446
Merge branch 'feat/router-replay-v2-pd' into feat/router-replay-kv-of…
faresobeid efab8d0
chore(deps): pin vllm-router to v0.2.3
faresobeid 8f9345e
Merge branch 'feat/router-replay-v2-pd' into feat/router-replay-kv-of…
faresobeid 3396556
Merge remote-tracking branch 'origin/main' into feat/router-replay-v2-pd
faresobeid 32e8f03
Merge branch 'feat/router-replay-v2-pd' into feat/router-replay-kv-of…
faresobeid b7ef693
Merge remote-tracking branch 'origin/main' into kv
faresobeid File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| from types import SimpleNamespace | ||
|
|
||
| import pytest | ||
|
|
||
| from prime_rl.inference.patches import _first_loaded_block, _routed_experts_load_limit, _track_externally_loaded_blocks | ||
|
|
||
|
|
||
| def _request(prompt_start, kv_transfer_params=None, num_output_tokens=0): | ||
| return SimpleNamespace( | ||
| sampling_params=SimpleNamespace(routed_experts_prompt_start=prompt_start), | ||
| kv_transfer_params=kv_transfer_params, | ||
| num_output_tokens=num_output_tokens, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| ("prompt_start", "num_computed_tokens", "kv_transfer_params", "num_output_tokens", "limit"), | ||
| [ | ||
| (0, 0, None, 0, 0), # first turn: no external load | ||
| (100, 0, None, 0, 64), # multi-turn: load up to the block before prompt_start | ||
| (100, 64, None, 0, 0), # local prefix hit already covers it | ||
| (200, 64, None, 0, 128), | ||
| (200, 0, {"do_remote_decode": True, "remote_engine_id": None}, 0, 192), # P/D prefill | ||
| (0, 64, {"do_remote_prefill": False, "remote_engine_id": "p0"}, 0, None), # P/D decode | ||
| (0, 0, None, 5, None), # resumed after preemption | ||
| ], | ||
| ) | ||
| def test_routed_experts_load_limit(prompt_start, num_computed_tokens, kv_transfer_params, num_output_tokens, limit): | ||
| request = _request(prompt_start, kv_transfer_params, num_output_tokens) | ||
| assert _routed_experts_load_limit(request, num_computed_tokens, block_size=64) == limit | ||
|
|
||
|
|
||
| def test_externally_loaded_blocks_cut_lower_start_hits(): | ||
| import torch | ||
| from vllm.sampling_params import SamplingParams | ||
| from vllm.utils.hashing import sha256 | ||
| from vllm.v1.core.kv_cache_manager import KVCacheManager | ||
| from vllm.v1.core.kv_cache_utils import get_request_block_hasher, init_none_hash | ||
| from vllm.v1.kv_cache_interface import FullAttentionSpec, KVCacheConfig, KVCacheGroupSpec | ||
| from vllm.v1.request import Request | ||
|
|
||
| bs = 16 | ||
| init_none_hash(sha256) | ||
| spec = FullAttentionSpec(block_size=bs, num_kv_heads=1, head_size=8, dtype=torch.float16) | ||
| config = KVCacheConfig(num_blocks=12, kv_cache_tensors=[], kv_cache_groups=[KVCacheGroupSpec(["l0"], spec)]) | ||
| manager = KVCacheManager(config, max_model_len=1024, scheduler_block_size=bs, hash_block_size=bs) | ||
| loaded = _track_externally_loaded_blocks(manager, gid=0, block_size=bs) | ||
| hasher = get_request_block_hasher(bs, sha256) | ||
|
|
||
| def request(rid, tokens, start): | ||
| params = SamplingParams(max_tokens=1, routed_experts_prompt_start=start) | ||
| return Request(rid, tokens, params, None, block_hasher=hasher) | ||
|
|
||
| # A multi-turn request loads its 64-token prefix externally (the clamp allows it: start=64)... | ||
| prefix = list(range(64)) | ||
| a = request("a", prefix + list(range(500, 520)), start=64) | ||
| manager.allocate_slots(a, 20, num_external_computed_tokens=64) | ||
| a.num_computed_tokens = a.num_tokens | ||
| manager.free(a) | ||
| # ...so a first-turn request (start=0) hitting that prefix locally must cut the hit at block 0, | ||
| # while a later turn (start=64) keeps it. | ||
| blocks, num_local, _ = manager.get_computed_blocks(request("b", prefix + [7, 7], start=0)) | ||
| assert num_local == 64 | ||
| assert _first_loaded_block(blocks.get_block_ids()[0], loaded, 0, bs) == 0 | ||
| assert _first_loaded_block(blocks.get_block_ids()[0], loaded, 64, bs) is None | ||
| # Reallocating the blocks to a new owner clears them. | ||
| assert manager.allocate_slots(request("c", list(range(1000, 1176)), start=0), 176) is not None | ||
| assert not loaded |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.