[DLLM] Basic dLLM scheduling strategy and implementation (#17484)

Signed-off-by: Zehuan Li <lizehuan.lzh@antgroup.com>
This commit is contained in:
Zehuan Li
2026-02-10 16:54:15 +08:00
committed by GitHub
parent 8da14aea88
commit 26f2b3798d
9 changed files with 461 additions and 210 deletions

View File

@@ -0,0 +1,67 @@
from __future__ import annotations
import enum
from typing import TYPE_CHECKING, Optional
from sglang.srt.dllm.config import DllmConfig
if TYPE_CHECKING:
from sglang.srt.managers.schedule_batch import Req
class DllmReqPhase(str, enum.Enum):
STAGING_PREFILL = "staging_prefill"
STAGING_DECODE = "staging_decode"
INCOMING_PREFILL = "incoming_prefill"
INCOMING_DECODE = "incoming_decode"
class ReqDllmMixin:
def init_diffusion_llm(self: Req, dllm_config: DllmConfig):
self.dllm_phase: Optional[DllmReqPhase] = None
self.dllm_ids = []
self.dllm_block_offset = 0
self.dllm_config = dllm_config
if self.dllm_config is not None:
if len(self.origin_input_ids) < self.dllm_config.block_size:
self.dllm_phase = DllmReqPhase.INCOMING_DECODE
else:
self.dllm_phase = DllmReqPhase.INCOMING_PREFILL
def is_dllm(self: Req) -> bool:
return self.dllm_config is not None
def is_dllm_prefill(self: Req) -> bool:
return self.dllm_phase in [
DllmReqPhase.STAGING_PREFILL,
DllmReqPhase.INCOMING_PREFILL,
]
def determine_dllm_phase(self: Req):
prefix_length = len(self.prefix_indices)
min_required_length = prefix_length + self.dllm_config.block_size
if len(self.fill_ids) < min_required_length:
# still incoming stage
return
input_block = self.fill_ids[prefix_length:min_required_length]
is_prefill_phase = self.dllm_config.mask_id not in input_block
if is_prefill_phase:
self.dllm_phase = DllmReqPhase.STAGING_PREFILL
else:
self.dllm_phase = DllmReqPhase.STAGING_DECODE
def _init_fill_ids_for_dllm(self: Req):
if not self.dllm_ids:
self.dllm_ids = (
self.origin_input_ids
+ [self.dllm_config.mask_id] * self.dllm_config.block_size
)
else:
self.dllm_block_offset += self.dllm_config.block_size
self.dllm_ids += [self.dllm_config.mask_id] * self.dllm_config.block_size
self.fill_ids = self.dllm_ids

View File

@@ -0,0 +1,313 @@
from __future__ import annotations
import logging
import time
from typing import TYPE_CHECKING, List, Optional, Set, Union
from sglang.srt.dllm.config import DllmConfig
from sglang.srt.dllm.mixin.req import DllmReqPhase
from sglang.srt.managers.schedule_batch import Req, RequestStage, ScheduleBatch
from sglang.srt.managers.schedule_policy import AddReqResult, PrefillAdder
from sglang.srt.model_executor.forward_batch_info import ForwardMode
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
from sglang.srt.managers.scheduler import Scheduler
class SchedulerDllmMixin:
def init_diffusion_llm(self: Scheduler):
self.dllm_config = (
DllmConfig.from_server_args(self.server_args)
if self.server_args.dllm_algorithm is not None
else None
)
self.dllm_manager = DllmManager(dllm_config=self.dllm_config)
def get_new_batch_dllm(self: Scheduler) -> Optional[ScheduleBatch]:
"""Generate a new batch for DLLM (Diffusion LLM) scheduling."""
if self.try_preemption:
self.running_batch.batch_is_full = False
# Early exit if batch is full or no requests available
if self._should_skip_prefill():
return None
running_bs = len(self.running_batch.reqs)
self.policy.calc_priority(self.waiting_queue)
# Create prefill adder with resource constraints
adder = self._create_dllm_prefill_adder(running_bs)
# Initialize DLLM manager and transfer requests
self.dllm_manager.init_next_round()
self._fetch_waiting_reqs()
# Process batches
forward_mode = self._process_dllm_batches(adder)
can_run_list = adder.can_run_list
if not can_run_list:
return None
# Record metrics and update state
self._update_metrics_and_state_for_batch(can_run_list, adder, running_bs)
# Create and prepare batch
new_batch = self._create_dllm_batch(can_run_list, forward_mode)
return new_batch
def _fetch_waiting_reqs(self: Scheduler):
# Calculate how many requests can be added to DLLM manager
max_dllm_capacity = self.server_args.max_running_requests - len(
self.dllm_manager.waiting_queue
)
num_requests_to_add = min(max_dllm_capacity, len(self.waiting_queue))
if num_requests_to_add > 0:
requests_to_add = self.waiting_queue[:num_requests_to_add]
self.dllm_manager.add_waiting_reqs(requests_to_add)
self.waiting_queue = self.waiting_queue[num_requests_to_add:]
def _should_skip_prefill(self: Scheduler) -> bool:
"""Check if DLLM prefill should be skipped."""
if (
self.running_batch.batch_is_full or not self.waiting_queue
) and self.dllm_manager.is_empty():
return True
running_bs = len(self.running_batch.reqs)
if (
self.get_num_allocatable_reqs(running_bs) <= 0
and self.dllm_manager.is_empty()
and not self.try_preemption
):
self.running_batch.batch_is_full = True
return True
return False
def _create_dllm_prefill_adder(self: Scheduler, running_bs: int) -> PrefillAdder:
"""Create a prefill adder configured for DLLM scheduling."""
return PrefillAdder(
self.page_size,
self.tree_cache,
self.token_to_kv_pool_allocator,
self.running_batch,
self.new_token_ratio,
self.max_prefill_tokens,
self.chunked_prefill_size,
running_bs if self.is_mixed_chunk else 0,
self.priority_scheduling_preemption_threshold,
prefill_max_requests=self.server_args.prefill_max_requests,
dllm_config=self.dllm_config,
)
def _process_dllm_batches(self: Scheduler, adder: PrefillAdder) -> ForwardMode:
"""Process prefill or decode batches for DLLM."""
forward_mode = ForwardMode.DLLM_EXTEND
# Try prefill batch first
prefill_reqs = self.dllm_manager.get_prefill_requests()
if prefill_reqs:
self._process_batch_by_phase(
adder,
prefill_reqs,
DllmReqPhase.STAGING_PREFILL,
DllmReqPhase.INCOMING_PREFILL,
)
else:
# Fall back to decode batch
decode_reqs = self.dllm_manager.get_decode_requests()
self._process_batch_by_phase(
adder,
decode_reqs,
DllmReqPhase.STAGING_DECODE,
DllmReqPhase.INCOMING_DECODE,
)
return forward_mode
def _process_batch_by_phase(
self,
adder: PrefillAdder,
batch: List[Req],
staging_phase: DllmReqPhase,
incoming_phase: DllmReqPhase,
) -> None:
"""Process a batch, separating staging and incoming requests."""
staging_reqs = [req for req in batch if req.dllm_phase == staging_phase]
if staging_reqs:
staging_result = self.process_dllm_staging_reqs(adder, staging_reqs)
if staging_result != AddReqResult.CONTINUE:
return
incoming_reqs = [req for req in batch if req.dllm_phase == incoming_phase]
if incoming_reqs:
self.process_dllm_incoming_reqs(adder, incoming_reqs)
def _update_metrics_and_state_for_batch(
self: Scheduler, can_run_list: List[Req], adder: PrefillAdder, running_bs: int
) -> None:
"""Update metrics and state for the batch."""
if self.enable_metrics:
for req in can_run_list:
req.add_latency(RequestStage.PREFILL_WAITING)
if adder.preempt_list:
for req in adder.preempt_list:
self._add_request_to_queue(req)
if can_run_list:
self.dllm_manager.add_staging_reqs(can_run_list)
self.dllm_manager.increment_chunked_count()
self.adder = adder
self.can_run_list = can_run_list
self.running_bs = len(self.running_batch.reqs)
for req in can_run_list:
if req.time_stats.forward_entry_time == 0:
req.time_stats.forward_entry_time = time.perf_counter()
if self.enable_metrics:
self.metrics_collector.observe_queue_time(
req.time_stats.get_queueing_time(),
)
def _create_dllm_batch(
self: Scheduler, can_run_list: List[Req], forward_mode: ForwardMode
) -> ScheduleBatch:
"""Create and prepare a new DLLM batch."""
new_batch = ScheduleBatch.init_new(
can_run_list,
self.req_to_token_pool,
self.token_to_kv_pool_allocator,
self.tree_cache,
self.model_config,
self.enable_overlap,
self.spec_algorithm,
dllm_config=self.dllm_config,
)
new_batch.prepare_for_extend()
new_batch.forward_mode = forward_mode
new_batch.decoding_reqs = None
return new_batch
def process_dllm_incoming_reqs(
self: Scheduler, adder: PrefillAdder, reqs: List[Req]
) -> AddReqResult:
"""Process incoming DLLM requests with resource allocation and preemption."""
res = AddReqResult.CONTINUE
for req in reqs:
# Check if batch is full
running_bs = len(self.running_batch.reqs)
if len(adder.can_run_list) >= self.get_num_allocatable_reqs(running_bs):
self.running_batch.batch_is_full = True
# Try preemption if batch is full
if self.running_batch.batch_is_full:
if not self.try_preemption or not adder.preempt_to_schedule(
req, self.server_args
):
break
# Prepare and add request
req.init_next_round_input(self.tree_cache)
res = adder.add_one_req(
req,
has_chunked_req=True,
truncation_align_size=self.truncation_align_size,
)
if res != AddReqResult.CONTINUE:
if res == AddReqResult.NO_TOKEN:
self.running_batch.batch_is_full = True
break
return res
def process_dllm_staging_reqs(
self: Scheduler, adder: PrefillAdder, reqs: List[Req]
) -> AddReqResult:
"""Process staging DLLM requests with resource allocation."""
for req in reqs:
res = adder.add_dllm_staging_req(req)
if res == AddReqResult.NO_TOKEN:
return res
return AddReqResult.CONTINUE
class DllmManager:
"""
Manager for Diffusion LLM request scheduling.
Maintains two queues:
- waiting_queue: The requests waiting to be scheduled with max running requests limit
- staging_queue: Requests allocated resources by PrefillAdder
"""
def __init__(self, dllm_config: Optional[DllmConfig] = None):
self.dllm_config = dllm_config
self.max_running_reqs = (
dllm_config.max_running_requests if dllm_config is not None else 1
)
self.waiting_queue: List[Req] = []
self.staging_queue: List[Req] = []
def get_prefill_requests(self) -> List[Req]:
"""Get all prefill requests from waiting queue."""
return [req for req in self.waiting_queue if req.is_dllm_prefill()]
def get_decode_requests(self) -> List[Req]:
"""Get all decode requests from waiting queue."""
return [req for req in self.waiting_queue if not req.is_dllm_prefill()]
def add_waiting_reqs(self, reqs: Union[Req, List[Req]]) -> None:
"""Add requests to waiting queue with redundancy check."""
assert self.dllm_config is not None, "Diffusion LLM config is not set."
reqs_to_add = reqs if isinstance(reqs, list) else [reqs]
# Check for duplicate request IDs
if self._has_duplicate_reqs(reqs_to_add):
raise RuntimeError("Redundant requests detected in dLLM requests.")
self.waiting_queue.extend(reqs_to_add)
def add_staging_reqs(self, reqs: Union[Req, List[Req]]) -> None:
"""Add requests to staging queue (allocated by PrefillAdder)."""
reqs_to_add = reqs if isinstance(reqs, list) else [reqs]
self.staging_queue.extend(reqs_to_add)
def _has_duplicate_reqs(self, reqs: List[Req]) -> bool:
"""Check if any request ID already exists in waiting queue."""
existing_rids: Set[str] = {r.rid for r in self.waiting_queue}
return any(req.rid in existing_rids for req in reqs)
def any_staging_reqs(self) -> bool:
"""Check if there are requests in staging queue."""
return self.dllm_config is not None and len(self.staging_queue) > 0
def is_empty(self) -> bool:
"""Check if both queues are empty or DLLM is not configured."""
if self.dllm_config is None:
return True
return len(self.waiting_queue) == 0
def increment_chunked_count(self) -> None:
"""Increment chunked count for all staging requests."""
for req in self.staging_queue:
req.is_chunked += 1
def filter_finished_reqs(self) -> None:
"""Remove finished requests from both queues."""
self.waiting_queue = [req for req in self.waiting_queue if not req.finished()]
self.staging_queue = [req for req in self.staging_queue if not req.finished()]
def init_next_round(self) -> None:
"""Initialize staging requests for next round and clear staging queue."""
for req in self.staging_queue:
req.init_next_round_input()
self.staging_queue = []

View File

@@ -58,6 +58,7 @@ from sglang.srt.disaggregation.decode_schedule_batch_mixin import (
)
from sglang.srt.disaggregation.utils import DisaggregationMode
from sglang.srt.distributed.parallel_state import get_tensor_model_parallel_rank
from sglang.srt.dllm.mixin.req import ReqDllmMixin
from sglang.srt.environ import envs
from sglang.srt.layers.attention.fla.chunk_delta_h import CHUNK_SIZE as FLA_CHUNK_SIZE
from sglang.srt.mem_cache.allocator import BaseTokenToKVPoolAllocator
@@ -507,7 +508,7 @@ class RequestStage(str, enum.Enum):
DECODE_QUICK_FINISH = "quick_finish"
class Req:
class Req(ReqDllmMixin):
"""The input and output status of a request."""
def __init__(
@@ -804,9 +805,7 @@ class Req:
self.dimensions = dimensions
# For diffusion LLM
self.dllm_ids = []
self.dllm_block_offset = 0
self.dllm_config = dllm_config
self.init_diffusion_llm(dllm_config)
@property
def seqlen(self) -> int:
@@ -868,23 +867,10 @@ class Req:
# Whether request reached finished condition
return self.finished_reason is not None
def is_dllm(self):
return self.dllm_config is not None
def _init_fill_ids_for_dllm(self):
if not self.dllm_ids:
self.dllm_ids = (
self.origin_input_ids
+ [self.dllm_config.mask_id] * self.dllm_config.block_size
)
else:
self.dllm_block_offset += self.dllm_config.block_size
self.dllm_ids += [self.dllm_config.mask_id] * self.dllm_config.block_size
self.fill_ids = self.dllm_ids
def init_next_round_input(self, tree_cache: Optional[BasePrefixCache] = None):
if self.is_dllm():
self._init_fill_ids_for_dllm()
self.determine_dllm_phase()
else:
self.fill_ids = self.origin_input_ids + self.output_ids
@@ -1194,62 +1180,6 @@ class Req:
)
class DllmStagingReqs:
def __init__(self, dllm_config: Optional[DllmConfig] = None):
self.dllm_config = dllm_config
self.max_running_reqs = (
dllm_config.max_running_requests if dllm_config is not None else 1
)
self.reqs: List[Req] = []
def add_reqs(self, req: Union[Req, List[Req], "DllmStagingReqs"]):
assert self.dllm_config is not None, "Diffusion LLM config is not set."
if isinstance(req, DllmStagingReqs):
reqs_to_add = req.reqs
elif isinstance(req, list):
reqs_to_add = req
else:
reqs_to_add = [req]
num_to_add = len(reqs_to_add)
# Sanity check:
if self.check_redundant_reqs(reqs_to_add):
raise RuntimeError("Redundant requests detected in dLLM requests.")
if len(self.reqs) + num_to_add > self.max_running_reqs:
raise RuntimeError(
f"Exceeding maximum number of concurrent diffusion LLM requests: {self.max_running_reqs}"
)
self.reqs.extend(reqs_to_add)
def check_redundant_reqs(self, reqs: List[Req]) -> bool:
existing_rids: Set[str] = {r.rid for r in self.reqs}
return any(req.rid in existing_rids for req in reqs)
def init_next_round(self):
for req in self.reqs:
req.init_next_round_input()
def non_empty(self) -> bool:
return self.dllm_config is not None and len(self.reqs) > 0
def empty(self) -> bool:
return self.dllm_config is None or len(self.reqs) == 0
def update_chunked_status(self):
for req in self.reqs:
req.is_chunked += 1
def filter_finished_reqs(self):
self.reqs = [req for req in self.reqs if not req.finished()]
def __iter__(self):
return iter(self.reqs)
@dataclasses.dataclass
class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
"""Store all information of a batch on the scheduler."""
@@ -1370,7 +1300,6 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
hicache_consumer_index: int = -1
# Diffusion LLM
dllm_staging_reqs: Optional[DllmStagingReqs] = None
dllm_config: Optional[DllmConfig] = None
# Metrics
@@ -1387,7 +1316,6 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
enable_overlap: bool,
spec_algorithm: SpeculativeAlgorithm,
chunked_req: Optional[Req] = None,
dllm_staging_reqs: Optional[DllmStagingReqs] = None,
dllm_config: Optional[DllmConfig] = None,
):
return_logprob = any(req.return_logprob for req in reqs)
@@ -1413,7 +1341,6 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
return_routed_experts=any(req.return_routed_experts for req in reqs),
is_prefill_only=all(req.is_prefill_only for req in reqs),
chunked_req=chunked_req,
dllm_staging_reqs=dllm_staging_reqs,
dllm_config=dllm_config,
)

View File

@@ -34,7 +34,7 @@ import torch
from sglang.srt.dllm.config import DllmConfig
from sglang.srt.layers.attention.nsa.utils import is_nsa_prefill_cp_in_seq_split
from sglang.srt.managers.schedule_batch import DllmStagingReqs, Req, ScheduleBatch
from sglang.srt.managers.schedule_batch import Req, ScheduleBatch
from sglang.srt.mem_cache.base_prefix_cache import (
BasePrefixCache,
InsertParams,
@@ -435,7 +435,6 @@ class PrefillAdder:
max_running_reqs = dllm_config.max_running_requests
self.rem_dllm_tokens = max_running_reqs * self.dllm_block_size
self.dllm_staging_reqs = DllmStagingReqs(dllm_config=dllm_config)
def _get_running_request_total_token_offset(self, req: Req) -> int:
return (
@@ -551,7 +550,6 @@ class PrefillAdder:
req.fill_ids = req.fill_ids[: prefix_len + trunc_len]
self.can_run_list.append(req)
self.dllm_staging_reqs.add_reqs(req)
self._update_prefill_budget(prefix_len, trunc_len, 0)
@@ -562,6 +560,34 @@ class PrefillAdder:
else:
self.tree_cache.inc_lock_ref(req.last_node)
def add_dllm_staging_req(self, req: Req):
assert self.dllm_config is not None
_rem_tokens = self._get_dllm_remain_tokens()
if _rem_tokens <= 0:
return AddReqResult.NO_TOKEN
# Truncate input length to available tokens and update request metadata
truncated = req.extend_input_len > _rem_tokens
req.extend_input_len = min(req.extend_input_len, _rem_tokens)
req.fill_ids = req.fill_ids[: len(req.prefix_indices) + req.extend_input_len]
self.can_run_list.append(req)
# Update budget: reserve max_new_tokens only if not truncated
max_new_tokens = (
min(req.sampling_params.max_new_tokens, CLIP_MAX_NEW_TOKENS)
if not truncated
else 0
)
self._update_prefill_budget(0, req.extend_input_len, max_new_tokens)
# Return based on remaining token availability
return (
AddReqResult.NO_TOKEN
if self._get_dllm_remain_tokens() <= 0
else AddReqResult.CONTINUE
)
def add_chunked_req(self, req: Req):
if self.dllm_config is not None:
_rem_tokens = self._get_dllm_remain_tokens()

View File

@@ -58,7 +58,7 @@ from sglang.srt.disaggregation.utils import (
)
from sglang.srt.distributed import get_pp_group, get_world_group
from sglang.srt.distributed.parallel_state import get_tp_group
from sglang.srt.dllm.config import DllmConfig
from sglang.srt.dllm.mixin.scheduler import SchedulerDllmMixin
from sglang.srt.environ import envs
from sglang.srt.eplb.expert_distribution import get_global_expert_distribution_recorder
from sglang.srt.layers.dp_attention import (
@@ -144,7 +144,6 @@ from sglang.srt.managers.schedule_batch import (
)
from sglang.srt.managers.schedule_policy import (
AddReqResult,
DllmStagingReqs,
PrefillAdder,
SchedulePolicy,
)
@@ -255,6 +254,7 @@ class Scheduler(
SchedulerRuntimeCheckerMixin,
SchedulerPPMixin,
SchedulerDPAttnMixin,
SchedulerDllmMixin,
):
"""A scheduler that manages a tensor parallel GPU worker."""
@@ -399,11 +399,6 @@ class Scheduler(
def init_model_config(self):
self.model_config = ModelConfig.from_server_args(self.server_args)
self.dllm_config = ( # For diffusion LLM
DllmConfig.from_server_args(self.server_args)
if self.server_args.dllm_algorithm is not None
else None
)
def init_ipc_channels(self, port_args: PortArgs):
context = zmq.Context(2)
@@ -763,9 +758,6 @@ class Scheduler(
)
self.enable_dynamic_chunking = False
def init_diffusion_llm(self):
self.dllm_staging_reqs = DllmStagingReqs(dllm_config=self.dllm_config)
def init_schedule_policy(self):
# Init schedule policy and new token estimation
self.policy = SchedulePolicy(
@@ -1323,7 +1315,7 @@ class Scheduler(
# If it is a health check generation request and there are running requests, ignore it.
if is_health_check_generate_req(recv_req) and (
self.chunked_req is not None
or self.dllm_staging_reqs.non_empty()
or self.dllm_manager.any_staging_reqs()
or not self.running_batch.is_empty()
or len(self.offload_tags) > 0
):
@@ -1830,20 +1822,15 @@ class Scheduler(
def get_next_batch_to_run(self) -> Optional[ScheduleBatch]:
self._abort_on_queued_timeout()
if self.dllm_config is not None:
self.dllm_staging_reqs.filter_finished_reqs()
self.dllm_manager.filter_finished_reqs()
# Merge the prefill batch into the running batch
chunked_req_to_exclude = set()
if self.dllm_config is not None:
assert (
self.chunked_req is None
), "chunked_req should be None when dllm_config is set"
if self.dllm_staging_reqs.non_empty():
chunked_req_to_exclude.update(self.dllm_staging_reqs)
for req in self.dllm_staging_reqs:
self.stash_chunked_request(req)
if self.dllm_config is not None and self.dllm_manager.any_staging_reqs():
chunked_req_to_exclude.update(self.dllm_manager.staging_queue)
for req in self.dllm_manager.staging_queue:
self.stash_chunked_request(req)
if self.chunked_req is not None:
# Move the chunked request out of the batch so that we can merge
@@ -1857,8 +1844,8 @@ class Scheduler(
# We need to discard it.
chunked_req_to_exclude.add(self.last_batch.chunked_req)
if self.last_batch.dllm_staging_reqs.non_empty():
chunked_req_to_exclude.update(self.last_batch.dllm_staging_reqs)
if self.dllm_config is not None and self.last_batch.reqs:
chunked_req_to_exclude.update(self.last_batch.reqs)
# Filter batch
last_bs = self.last_batch.batch_size()
@@ -1877,7 +1864,10 @@ class Scheduler(
# Merge running_batch with prefill batch
self.running_batch.merge_batch(self.last_batch)
new_batch = self.get_new_batch_prefill()
if self.dllm_config is not None:
new_batch = self.get_new_batch_dllm()
else:
new_batch = self.get_new_batch_prefill()
need_mlp_sync = self.require_mlp_sync
if need_mlp_sync and not self.spec_algorithm.is_none():
@@ -1947,9 +1937,9 @@ class Scheduler(
# Reset batch_is_full to try preemption with a prefill adder.
self.running_batch.batch_is_full = False
if (self.running_batch.batch_is_full or len(self.waiting_queue) == 0) and (
not self.dllm_staging_reqs.non_empty() and self.chunked_req is None
):
if (
self.running_batch.batch_is_full or len(self.waiting_queue) == 0
) and self.chunked_req is None:
return None
running_bs = len(self.running_batch.reqs)
@@ -1960,7 +1950,7 @@ class Scheduler(
# Instead, we should always allow chunked requests to be added, otherwise, there will be a memory leak.
if (
self.get_num_allocatable_reqs(running_bs) <= 0
and (self.dllm_staging_reqs.empty() or self.chunked_req is not None)
and self.chunked_req is not None
and not self.try_preemption
):
self.running_batch.batch_is_full = True
@@ -2002,17 +1992,6 @@ class Scheduler(
dllm_config=self.dllm_config,
)
if self.dllm_config is not None:
assert (
self.chunked_req is None
), "chunked_req should be None when dllm_config is set"
if self.dllm_staging_reqs.non_empty():
self.dllm_staging_reqs.init_next_round()
for req in self.dllm_staging_reqs:
adder.add_chunked_req(req)
self.dllm_staging_reqs.add_reqs(adder.dllm_staging_reqs)
if self.chunked_req is not None:
self.chunked_req.init_next_round_input()
self.chunked_req = adder.add_chunked_req(self.chunked_req)
@@ -2066,9 +2045,7 @@ class Scheduler(
req.init_next_round_input(self.tree_cache)
res = adder.add_one_req(
req,
has_chunked_req=(
self.dllm_staging_reqs.non_empty() or self.chunked_req is not None
),
has_chunked_req=(self.chunked_req is not None),
truncation_align_size=self.truncation_align_size,
)
@@ -2103,14 +2080,6 @@ class Scheduler(
for req in adder.preempt_list:
self._add_request_to_queue(req)
if self.dllm_config is not None:
assert (
self.chunked_req is None
), "chunked_req should be None when dllm_config is set"
if adder.dllm_staging_reqs.non_empty():
self.dllm_staging_reqs.add_reqs(adder.dllm_staging_reqs)
if adder.new_chunked_req is not None:
# Update chunked prefill
assert self.chunked_req is None
@@ -2119,9 +2088,6 @@ class Scheduler(
if self.chunked_req is not None:
self.chunked_req.is_chunked += 1
if self.dllm_staging_reqs.non_empty():
self.dllm_staging_reqs.update_chunked_status()
# Record for logging prefill stats after forward
self.adder = adder
self.can_run_list = can_run_list
@@ -2146,8 +2112,6 @@ class Scheduler(
self.enable_overlap,
self.spec_algorithm,
chunked_req=self.chunked_req,
dllm_staging_reqs=self.dllm_staging_reqs,
dllm_config=self.dllm_config,
)
if self.enable_hierarchical_cache:
# todo (zhiqiang): disable cuda graph execution if hicache loading triggered

View File

@@ -95,7 +95,7 @@ class ForwardMode(IntEnum):
# Split Prefill for PD multiplexing
SPLIT_PREFILL = auto()
# Used in diffusion LLM inference
# Used in dLLM
DLLM_EXTEND = auto()
def is_prefill(self):

View File

@@ -2758,6 +2758,24 @@ class ServerArgs:
)
self.pp_size = 1
if self.enable_lora:
logger.warning(
"Currently LoRA is not supported by diffusion LLM inference."
)
self.enable_lora = False
if self.disaggregation_mode != "null":
logger.warning(
"Currently disaggregation is not supported by diffusion LLM inference."
)
self.disaggregation_mode = "null"
if self.enable_mixed_chunk:
logger.warning(
"Mixed chunked prefill is disabled because of using diffusion LLM inference."
)
self.enable_mixed_chunk = False
def _handle_other_validations(self):
# Handle model inference tensor dump.
if self.debug_tensor_dump_output_folder is not None: