-
-
Notifications
You must be signed in to change notification settings - Fork 21k
[ModelRunner V2] Speculative Decoding NGram GPU Implementations #40704
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
58aa569
4bfb745
02185ac
242fb82
8569425
b4f1db9
f22224a
3437791
324fe81
95dffa8
3af6547
2527013
199b525
480614b
81a1915
7d5deb7
00b071a
a285ef6
fcdab55
3374d6f
34fa287
b0eb7ad
aabb68f
a9101c7
5986d2b
07641bc
b906e89
29b8a6c
a880d05
7a7ea14
29cb65e
98cc7da
9d6fd07
4824b4a
430e18a
445322d
1474690
98a0809
4ea6d81
3cb1b37
6fea8f2
37cc9c5
cb77a14
5228bd6
eb4dc4e
2d0bab6
af498ea
e66fbb8
6dd58ad
2754fe3
709e437
3e20869
172b2bd
4033877
52e79a8
2d5f8f9
4702930
028e77a
6097cba
e9805e6
80205e7
ad4bd48
66e1ade
919a826
4ae1b28
223ebc3
3064455
e1a05d6
9ff65bc
d84087e
b8a3834
e052b8c
ee72c7a
6c5dfc9
bc5e16d
2e39f8c
8797fe6
7e5703a
ffeadf6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1671,9 +1671,12 @@ def _free_encoder_inputs(self, request: Request) -> None: | |||||||||||
| self.encoder_cache_manager.free_encoder_input(request, input_id) | ||||||||||||
|
|
||||||||||||
| def update_draft_token_ids(self, draft_token_ids: DraftTokenIds) -> None: | ||||||||||||
| for req_id, spec_token_ids in zip( | ||||||||||||
| draft_token_ids.req_ids, | ||||||||||||
| draft_token_ids.draft_token_ids, | ||||||||||||
| num_valid_list = draft_token_ids.num_valid_draft_tokens | ||||||||||||
| for i, (req_id, spec_token_ids) in enumerate( | ||||||||||||
| zip( | ||||||||||||
| draft_token_ids.req_ids, | ||||||||||||
| draft_token_ids.draft_token_ids, | ||||||||||||
| ) | ||||||||||||
| ): | ||||||||||||
| request = self.requests.get(req_id) | ||||||||||||
| if request is None or request.is_finished(): | ||||||||||||
|
|
@@ -1686,6 +1689,12 @@ def update_draft_token_ids(self, draft_token_ids: DraftTokenIds) -> None: | |||||||||||
| request.spec_token_ids = [] | ||||||||||||
| continue | ||||||||||||
|
|
||||||||||||
| # Variable-length drafters: truncate to the number of drafts | ||||||||||||
| if num_valid_list is not None: | ||||||||||||
| num_valid = num_valid_list[i] | ||||||||||||
| if num_valid < len(spec_token_ids): | ||||||||||||
| spec_token_ids = spec_token_ids[:num_valid] | ||||||||||||
|
|
||||||||||||
| # Add newly generated spec token ids to the request. | ||||||||||||
| if self.structured_output_manager.should_advance(request): | ||||||||||||
| metadata = request.structured_output_request | ||||||||||||
|
|
@@ -1696,11 +1705,14 @@ def update_draft_token_ids_in_output( | |||||||||||
| self, draft_token_ids: DraftTokenIds, scheduler_output: SchedulerOutput | ||||||||||||
| ) -> None: | ||||||||||||
| num_invalid_spec_tokens: dict[str, int] = {} | ||||||||||||
| num_valid_list = draft_token_ids.num_valid_draft_tokens | ||||||||||||
|
|
||||||||||||
| sched_spec_tokens = scheduler_output.scheduled_spec_decode_tokens | ||||||||||||
| for req_id, spec_token_ids in zip( | ||||||||||||
| draft_token_ids.req_ids, | ||||||||||||
| draft_token_ids.draft_token_ids, | ||||||||||||
| for i, (req_id, spec_token_ids) in enumerate( | ||||||||||||
| zip( | ||||||||||||
| draft_token_ids.req_ids, | ||||||||||||
| draft_token_ids.draft_token_ids, | ||||||||||||
| ) | ||||||||||||
|
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. can collapse
Suggested change
|
||||||||||||
| ): | ||||||||||||
| request = self.requests.get(req_id) | ||||||||||||
| if request is None or request.is_finished(): | ||||||||||||
|
|
@@ -1714,7 +1726,14 @@ def update_draft_token_ids_in_output( | |||||||||||
| orig_num_spec_tokens = len(placeholder_spec_tokens) | ||||||||||||
| # Trim drafts to scheduled number of spec tokens | ||||||||||||
| # (needed for chunked prefill case for example). | ||||||||||||
| del spec_token_ids[orig_num_spec_tokens:] | ||||||||||||
| effective_num_spec_tokens = orig_num_spec_tokens | ||||||||||||
| if num_valid_list is not None: | ||||||||||||
| effective_num_spec_tokens = max( | ||||||||||||
| 0, | ||||||||||||
| min(num_valid_list[i], orig_num_spec_tokens), | ||||||||||||
|
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.
Suggested change
|
||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| del spec_token_ids[effective_num_spec_tokens:] | ||||||||||||
| # Filter out spec tokens which do not adhere to the grammar. | ||||||||||||
| if self.structured_output_manager.should_advance(request): | ||||||||||||
| metadata = request.structured_output_request | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -72,7 +72,7 @@ | |||||||||||||
| from vllm.v1.executor import Executor | ||||||||||||||
| from vllm.v1.kv_cache_interface import KVCacheConfig | ||||||||||||||
| from vllm.v1.metrics.stats import SchedulerStats | ||||||||||||||
| from vllm.v1.outputs import ModelRunnerOutput | ||||||||||||||
| from vllm.v1.outputs import DraftTokenIds, ModelRunnerOutput | ||||||||||||||
| from vllm.v1.request import Request, RequestStatus | ||||||||||||||
| from vllm.v1.serial_utils import MsgpackDecoder, MsgpackEncoder | ||||||||||||||
| from vllm.v1.structured_output import StructuredOutputManager | ||||||||||||||
|
|
@@ -427,7 +427,26 @@ def step(self) -> tuple[dict[int, EngineCoreOutputs], bool]: | |||||||||||||
| scheduler_output, model_output | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| return engine_core_outputs, scheduler_output.total_num_scheduled_tokens > 0 | ||||||||||||||
| model_executed = scheduler_output.total_num_scheduled_tokens > 0 | ||||||||||||||
| self._maybe_update_async_draft_token_ids(model_executed) | ||||||||||||||
|
|
||||||||||||||
| return engine_core_outputs, model_executed | ||||||||||||||
|
|
||||||||||||||
| def _maybe_update_async_draft_token_ids( | ||||||||||||||
| self, model_executed: bool | ||||||||||||||
| ) -> "DraftTokenIds | None": | ||||||||||||||
| """ | ||||||||||||||
| Consume variable-length draft metadata from the just-completed | ||||||||||||||
| batch and apply it to scheduler request state. | ||||||||||||||
| """ | ||||||||||||||
| if not (self.async_scheduling and self.use_spec_decode and model_executed): | ||||||||||||||
| return None | ||||||||||||||
| draft_token_ids = self.model_executor.take_draft_token_ids() | ||||||||||||||
| if draft_token_ids is None: | ||||||||||||||
| return None | ||||||||||||||
| if draft_token_ids.num_valid_draft_tokens is not None: | ||||||||||||||
| self.scheduler.update_draft_token_ids(draft_token_ids) | ||||||||||||||
|
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.
Suggested change
|
||||||||||||||
| return draft_token_ids | ||||||||||||||
|
|
||||||||||||||
| def post_step(self, model_executed: bool) -> None: | ||||||||||||||
| # When using async scheduling we can't get draft token ids in advance, | ||||||||||||||
|
|
@@ -531,6 +550,11 @@ def step_with_batch_queue( | |||||||||||||
| scheduler_output, model_output | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| popped_batch_executed = scheduler_output.total_num_scheduled_tokens > 0 | ||||||||||||||
| async_draft_token_ids = self._maybe_update_async_draft_token_ids( | ||||||||||||||
| popped_batch_executed | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| # NOTE(nick): We can either handle the deferred tasks here or save | ||||||||||||||
| # in a field and do it immediately once step_with_batch_queue is | ||||||||||||||
| # re-called. The latter slightly favors TTFT over TPOT/throughput. | ||||||||||||||
|
|
@@ -539,7 +563,11 @@ def step_with_batch_queue( | |||||||||||||
| # we need to get the draft token ids from the prior step before | ||||||||||||||
| # we can compute the grammar bitmask for the deferred request. | ||||||||||||||
| if self.use_spec_decode: | ||||||||||||||
| draft_token_ids = self.model_executor.take_draft_token_ids() | ||||||||||||||
| draft_token_ids = ( | ||||||||||||||
| async_draft_token_ids | ||||||||||||||
| if async_draft_token_ids is not None | ||||||||||||||
| else self.model_executor.take_draft_token_ids() | ||||||||||||||
|
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.
Suggested change
|
||||||||||||||
| ) | ||||||||||||||
| assert draft_token_ids is not None | ||||||||||||||
| # Update the draft token ids in the scheduler output to | ||||||||||||||
| # filter out the invalid spec tokens, which will be padded | ||||||||||||||
|
|
||||||||||||||
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.
can collapse