-
-
Notifications
You must be signed in to change notification settings - Fork 20.4k
[BugFix] Make PD work with Ray #21072
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
Merged
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
951096e
wip
kouroshHakha b629b86
wip
kouroshHakha c0f9c92
wip
kouroshHakha 80d861e
wip
kouroshHakha 1c63f8e
wip
kouroshHakha 913cd52
wip
kouroshHakha 9d4c583
wip
kouroshHakha ac43f24
Merge branch 'main' into kh/fix-ray-pd
kouroshHakha 2013ef6
wip
kouroshHakha 7e4bf72
wip
kouroshHakha c6c48c5
Merge branch 'main' of https://github.com/vllm-project/vllm into kh/f…
kouroshHakha 5e97ce6
wip
kouroshHakha f04be9f
wip
kouroshHakha 23409ae
fixed ray tests
kouroshHakha c70c5c1
addressing ci
kouroshHakha ee04a92
Merge branch 'main' of https://github.com/vllm-project/vllm into kh/f…
kouroshHakha 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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # SPDX-FileCopyrightText: Copyright contributors to the vLLM project | ||
| from concurrent.futures import Future | ||
| from typing import Optional | ||
|
|
||
| from vllm.distributed.kv_transfer.kv_connector.utils import KVOutputAggregator | ||
| from vllm.v1.outputs import ModelRunnerOutput | ||
|
|
||
|
|
||
| class DummyModelRunnerOutput(ModelRunnerOutput): | ||
|
|
||
| def __init__(self, | ||
| finished_sending: Optional[set[str]] = None, | ||
| finished_recving: Optional[set[str]] = None): | ||
| self.finished_sending = finished_sending | ||
| self.finished_recving = finished_recving | ||
|
|
||
|
|
||
| def test_aggregate_workers_output(): | ||
| aggregator = KVOutputAggregator(world_size=2) | ||
|
|
||
| output1 = DummyModelRunnerOutput(finished_sending={'req1'}, | ||
| finished_recving={'req2'}) | ||
| output2 = DummyModelRunnerOutput(finished_sending=None, | ||
| finished_recving=None) | ||
|
|
||
| aggregated = aggregator.aggregate([output1, output2]) | ||
|
|
||
| assert aggregated is output1 | ||
| assert aggregated.finished_sending is None | ||
| assert aggregated.finished_recving is None | ||
|
|
||
| output1 = DummyModelRunnerOutput(finished_sending=None, | ||
| finished_recving=None) | ||
| output2 = DummyModelRunnerOutput(finished_sending={'req1'}, | ||
| finished_recving=None) | ||
|
|
||
| aggregated = aggregator.aggregate([output1, output2]) | ||
|
|
||
| assert aggregated is output1 | ||
| assert aggregated.finished_sending == {'req1'} | ||
| assert aggregated.finished_recving is None | ||
|
|
||
| output1 = DummyModelRunnerOutput(finished_sending=None, | ||
| finished_recving=None) | ||
| output2 = DummyModelRunnerOutput(finished_sending={'req1'}, | ||
| finished_recving={'req2'}) | ||
|
|
||
| aggregated = aggregator.aggregate([output1, output2]) | ||
|
|
||
| assert aggregated is output1 | ||
| assert aggregated.finished_sending is None | ||
| assert aggregated.finished_recving == {'req2'} | ||
|
|
||
|
|
||
| def test_async_aggregate_workers_output(): | ||
| aggregator = KVOutputAggregator(world_size=2) | ||
|
|
||
| future1: Future[DummyModelRunnerOutput] = Future() | ||
| future2: Future[DummyModelRunnerOutput] = Future() | ||
| result_future = aggregator.async_aggregate([future1, future2]) | ||
|
|
||
| output1 = DummyModelRunnerOutput(finished_sending={'req1'}, | ||
| finished_recving={'req2'}) | ||
| output2 = DummyModelRunnerOutput(finished_sending=None, | ||
| finished_recving=None) | ||
| future1.set_result(output1) | ||
| future2.set_result(output2) | ||
|
|
||
| assert result_future.done() | ||
| aggregated = result_future.result() | ||
| assert aggregated is output1 | ||
| assert aggregated.finished_sending is None | ||
| assert aggregated.finished_recving is None | ||
|
|
||
| future1 = Future() | ||
| future2 = Future() | ||
| result_future = aggregator.async_aggregate([future1, future2]) | ||
|
|
||
| output1 = DummyModelRunnerOutput(finished_sending=None, | ||
| finished_recving=None) | ||
| output2 = DummyModelRunnerOutput(finished_sending={'req1'}, | ||
| finished_recving=None) | ||
| future1.set_result(output1) | ||
| future2.set_result(output2) | ||
|
|
||
| assert result_future.done() | ||
| aggregated = result_future.result() | ||
| assert aggregated is output1 | ||
| assert aggregated.finished_sending == {'req1'} | ||
| assert aggregated.finished_recving is None | ||
|
|
||
| future1 = Future() | ||
| future2 = Future() | ||
| result_future = aggregator.async_aggregate([future1, future2]) | ||
|
|
||
| output1 = DummyModelRunnerOutput(finished_sending=None, | ||
| finished_recving=None) | ||
| output2 = DummyModelRunnerOutput(finished_sending={'req1'}, | ||
| finished_recving={'req2'}) | ||
| future1.set_result(output1) | ||
| future2.set_result(output2) | ||
|
|
||
| assert result_future.done() | ||
| aggregated = result_future.result() | ||
| assert aggregated is output1 | ||
| assert aggregated.finished_sending is None | ||
| assert aggregated.finished_recving == {'req2'} | ||
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 |
|---|---|---|
|
|
@@ -3,12 +3,18 @@ | |
| """ | ||
| KV cache helper for store. | ||
| """ | ||
| from collections import defaultdict | ||
| from collections.abc import Sequence | ||
| from concurrent.futures import CancelledError, Future | ||
| from typing import Optional, cast | ||
|
|
||
| import torch | ||
|
|
||
| import vllm.envs as envs | ||
| from vllm import _custom_ops as ops | ||
| from vllm.config import VllmConfig, get_current_vllm_config | ||
| from vllm.logger import init_logger | ||
| from vllm.v1.outputs import ModelRunnerOutput | ||
|
|
||
| logger = init_logger(__name__) | ||
|
|
||
|
|
@@ -107,3 +113,84 @@ def get_kv_connector_cache_layout(): | |
| "layout to HND for better xfer performance.") | ||
| return "HND" | ||
| return "NHD" | ||
|
|
||
|
|
||
| class KVOutputAggregator: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This utility class LGTM |
||
| """Utility class to aggregate the output of all workers into a single | ||
| output corresponding to Rank 0 for scheduler.""" | ||
|
|
||
| def __init__(self, world_size: int): | ||
| self.world_size = world_size | ||
|
njhill marked this conversation as resolved.
Outdated
|
||
| # Complete transfer tracker. Used by to track finished requests | ||
| # [req_id -> n_finished_workers] | ||
| self._recv_remaining_count = defaultdict[str, int](lambda: world_size) | ||
| self._send_remaining_count = defaultdict[str, int](lambda: world_size) | ||
|
|
||
| def aggregate(self, | ||
| outputs: list[ModelRunnerOutput], | ||
| output_rank: int = 0) -> ModelRunnerOutput: | ||
| # aggregate finished_sending, finished_recving from all workers | ||
|
|
||
| def update_finished_set(req_ids: Optional[set[str]], | ||
|
kouroshHakha marked this conversation as resolved.
|
||
| remaining_count_dict: dict[str, int], | ||
| finished_set: set[str]) -> None: | ||
| for req_id in req_ids or (): | ||
| new_count = remaining_count_dict[req_id] - 1 | ||
| if new_count == 0: | ||
| finished_set.add(req_id) | ||
| del remaining_count_dict[req_id] | ||
| else: | ||
| remaining_count_dict[req_id] = new_count | ||
|
|
||
| finished_sending = set[str]() | ||
| finished_recving = set[str]() | ||
| for output in outputs: | ||
| update_finished_set(output.finished_sending, | ||
| self._send_remaining_count, finished_sending) | ||
| update_finished_set(output.finished_recving, | ||
| self._recv_remaining_count, finished_recving) | ||
|
|
||
| # select output of the worker specified by output_rank | ||
| output = outputs[output_rank] | ||
|
|
||
| # set the aggregated finished_sending / finished_recving | ||
| output.finished_sending = finished_sending if finished_sending else None | ||
| output.finished_recving = finished_recving if finished_recving else None | ||
|
|
||
| return output | ||
|
|
||
| def async_aggregate(self, | ||
| output_futures: Sequence[Future[ModelRunnerOutput]], | ||
| output_rank: int = 0) -> Future[ModelRunnerOutput]: | ||
| """Takes a list of futures and returns a single future which resolves | ||
| to the respective list of outputs.""" | ||
| result_future: Future[ModelRunnerOutput] = Future() | ||
|
|
||
| outputs: list[Optional[ModelRunnerOutput]] = [None | ||
| ] * len(output_futures) | ||
|
|
||
| def make_callback(idx): | ||
|
|
||
| def callback(fut): | ||
| if result_future.done(): | ||
| return | ||
|
|
||
| try: | ||
| outputs[idx] = fut.result() | ||
| except CancelledError: | ||
| result_future.cancel() | ||
| except Exception as e: | ||
| result_future.set_exception(e) | ||
|
|
||
| # this check assumes io_thread_pool uses a single thread | ||
| if all(outputs): | ||
| result_future.set_result( | ||
| self.aggregate(cast(list[ModelRunnerOutput], outputs), | ||
| output_rank)) | ||
|
|
||
| return callback | ||
|
|
||
| for i, output_future in enumerate(output_futures): | ||
| output_future.add_done_callback(make_callback(i)) | ||
|
|
||
| return result_future | ||
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
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding the tests and bug fix from #21048
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's now been merged to main so can rebase.