Configurable decode retraction order (#30573)

Co-authored-by: tanujtiwari1998 <168470992+tanujtiwari1998@users.noreply.github.com>
This commit is contained in:
Lianmin Zheng
2026-07-09 17:55:26 -07:00
committed by GitHub
co-authored by tanujtiwari1998
parent 5be9c9f7c6
commit 32c8973ce8
2 changed files with 72 additions and 15 deletions
+54 -15
View File
@@ -40,6 +40,7 @@ import copy
import dataclasses import dataclasses
import logging import logging
import re import re
import sys
from array import array from array import array
from concurrent.futures import Future from concurrent.futures import Future
from enum import Enum, auto from enum import Enum, auto
@@ -2552,21 +2553,13 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
self, server_args: ServerArgs self, server_args: ServerArgs
) -> Tuple[List[Req], float, List[Req]]: ) -> Tuple[List[Req], float, List[Req]]:
"""Retract the decoding requests when there is not enough memory.""" """Retract the decoding requests when there is not enough memory."""
sorted_indices = list(range(len(self.reqs))) sorted_indices = self._get_decode_retraction_order(
self.reqs,
# TODO(lsyin): improve retraction policy for radix cache server_args,
# For spec decoding, filter_batch API can only filter allow_policy_sort=(
# requests from the back, so we can only retract from the back. self.spec_algorithm is None or self.spec_algorithm.is_none()
# TODO(sang): Clean up finish path and support better retract ),
# policy. )
if not server_args.speculative_algorithm:
sorted_indices.sort(
key=lambda i: (
len(self.reqs[i].output_ids),
-len(self.reqs[i].origin_input_ids),
),
reverse=True,
)
retracted_reqs = [] retracted_reqs = []
first_iter = True first_iter = True
@@ -2612,6 +2605,52 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
return retracted_reqs, new_estimate_ratio, reqs_to_abort return retracted_reqs, new_estimate_ratio, reqs_to_abort
@staticmethod
def _get_decode_retraction_order(
reqs: List[Req], server_args: ServerArgs, *, allow_policy_sort: bool
) -> List[int]:
"""Return indices ordered from most-preferred to least-preferred to keep.
The retraction loop pops from the end of this list, so the least-preferred
request is retracted first.
"""
sorted_indices = list(range(len(reqs)))
# TODO(lsyin): improve retraction policy for radix cache
# For spec decoding, filter_batch API can only filter requests from the
# back, so we can only retract from the back.
# TODO(sang): Clean up finish path and support better retract policy.
if not allow_policy_sort:
return sorted_indices
def length_key(req: Req) -> Tuple[int, int]:
return (len(req.output_ids), -len(req.origin_input_ids))
if server_args.retraction_policy == "priority":
priority_sign = 1 if server_args.schedule_low_priority_values_first else -1
def retraction_key(req: Req) -> Tuple[int, int, int]:
priority = req.priority
if priority is None:
priority = (
sys.maxsize
if server_args.schedule_low_priority_values_first
else -sys.maxsize - 1
)
return (priority * (-priority_sign), *length_key(req))
sorted_indices.sort(
key=lambda i: retraction_key(reqs[i]),
reverse=True,
)
return sorted_indices
sorted_indices.sort(
key=lambda i: length_key(reqs[i]),
reverse=True,
)
return sorted_indices
def release_req(self, idx: int, remaing_req_count: int, server_args: ServerArgs): def release_req(self, idx: int, remaing_req_count: int, server_args: ServerArgs):
release_req( release_req(
req=self.reqs[idx], req=self.reqs[idx],
+18
View File
@@ -292,6 +292,7 @@ FP4_GEMM_RUNNER_BACKEND_CHOICES = [
BF16_GEMM_BACKEND_CHOICES = ["auto", "cutedsl"] BF16_GEMM_BACKEND_CHOICES = ["auto", "cutedsl"]
RADIX_EVICTION_POLICY_CHOICES = ["lru", "lfu", "slru", "priority"] RADIX_EVICTION_POLICY_CHOICES = ["lru", "lfu", "slru", "priority"]
RETRACTION_POLICY_CHOICES = ["length", "priority"]
RL_ON_POLICY_TARGET_CHOICES = ["fsdp"] RL_ON_POLICY_TARGET_CHOICES = ["fsdp"]
@@ -736,6 +737,19 @@ class ServerArgs:
int, int,
"Minimum difference in priorities for an incoming request to have to preempt running request(s).", "Minimum difference in priorities for an incoming request to have to preempt running request(s).",
] = 10 ] = 10
retraction_policy: A[
str,
Arg(
help=(
"The decode retraction policy to use when the KV cache is full. "
"'length' preserves the existing behavior and retracts short-output, "
"long-input requests first. 'priority' retracts lower-priority "
"requests first, using the same priority direction as priority "
"scheduling."
),
choices=RETRACTION_POLICY_CHOICES,
),
] = "length"
schedule_conservativeness: A[ schedule_conservativeness: A[
float, float,
"How conservative the schedule policy is. A larger value means more conservative scheduling. Use a larger value if you see requests being retracted frequently.", "How conservative the schedule policy is. A larger value means more conservative scheduling. Use a larger value if you see requests being retracted frequently.",
@@ -7004,6 +7018,10 @@ class ServerArgs:
logger.warning( logger.warning(
"--default-priority-value has no effect without --enable-priority-scheduling" "--default-priority-value has no effect without --enable-priority-scheduling"
) )
if self.retraction_policy == "priority" and not self.enable_priority_scheduling:
raise ValueError(
"--retraction-policy priority requires --enable-priority-scheduling"
)
# Check hisparse # Check hisparse
# Moved to the resolution pipeline (arg_groups/overrides.py: # Moved to the resolution pipeline (arg_groups/overrides.py: