Add overlap scheduling for embeddings code path (#14032)

Co-authored-by: Satyam Kumar <satyamk@linkedin.com>
This commit is contained in:
satyamk7054
2025-12-24 18:24:18 -08:00
committed by GitHub
parent ecd2d09a6c
commit 38dd4fbb66
6 changed files with 63 additions and 19 deletions

View File

@@ -1996,7 +1996,10 @@ class ScheduleBatch(ScheduleBatchDisaggregationDecodeMixin):
self.orig_seq_lens = self.orig_seq_lens[keep_indices_device]
self.out_cache_loc = None
self.seq_lens_sum = self.seq_lens.sum().item()
self.output_ids = self.output_ids[keep_indices_device]
if self.output_ids is not None:
self.output_ids = self.output_ids[keep_indices_device]
self.mamba_track_indices = None
self.mamba_track_mask = None
self.mamba_track_seqlens = None

View File

@@ -210,6 +210,25 @@ GRAMMAR_TIMEOUT = float(os.environ.get("SGLANG_GRAMMAR_TIMEOUT", 300))
@dataclass
class EmbeddingBatchResult:
embeddings: torch.Tensor
copy_done: Optional[torch.cuda.Event] = None
def copy_to_cpu(self):
"""Copy embeddings tensor to CPU in overlap scheduling."""
if isinstance(self.embeddings, torch.Tensor):
self.copy_done = torch.get_device_module(self.embeddings.device).Event()
self.embeddings = self.embeddings.to("cpu", non_blocking=True)
else:
assert isinstance(self.embeddings, list)
if len(self.embeddings) == 0:
return
self.copy_done = torch.get_device_module(self.embeddings[0].device).Event()
self.embeddings = [
emb.to("cpu", non_blocking=True) for emb in self.embeddings
]
self.copy_done.record()
class Scheduler(
@@ -1083,7 +1102,9 @@ class Scheduler(
@DynamicGradMode()
def event_loop_overlap(self):
"""A scheduler loop that overlaps the CPU processing and GPU computation."""
self.result_queue: Deque[Tuple[ScheduleBatch, GenerationBatchResult]] = deque()
self.result_queue: Deque[
Tuple[ScheduleBatch, Union[GenerationBatchResult, EmbeddingBatchResult]]
] = deque()
def pop_and_process():
# Process the results of the last batch
@@ -1124,7 +1145,8 @@ class Scheduler(
# Run sample of the current batch
# It depends on the result of the last batch (e.g., grammar), so we run it after the last batch is processed.
self.launch_batch_sample_if_needed(batch_result)
if self.is_generation:
self.launch_batch_sample_if_needed(batch_result)
# Update last_batch
self.last_batch = batch
@@ -2249,8 +2271,19 @@ class Scheduler(
ret = batch_result
else: # embedding or reward model
model_worker_batch = batch.get_model_worker_batch()
embeddings = self.tp_worker.forward_batch_embedding(model_worker_batch)
ret = EmbeddingBatchResult(embeddings=embeddings)
if self.enable_overlap:
self.record_batch_in_overlap(model_worker_batch)
with self.forward_stream_ctx:
self.forward_stream.wait_stream(self.default_stream)
embeddings = self.tp_worker.forward_batch_embedding(
model_worker_batch
)
ret = EmbeddingBatchResult(embeddings=embeddings)
ret.copy_to_cpu()
else:
embeddings = self.tp_worker.forward_batch_embedding(model_worker_batch)
ret = EmbeddingBatchResult(embeddings=embeddings)
# Capture prefill end time for EXTEND mode
if batch.forward_mode == ForwardMode.EXTEND:
@@ -2262,7 +2295,7 @@ class Scheduler(
def launch_batch_sample_if_needed(
self, batch_result: GenerationBatchResult
) -> Union[GenerationBatchResult, EmbeddingBatchResult]:
) -> Union[GenerationBatchResult]:
# TODO(lsyin): make the delayed sample a default behavior after
# unifying the forward_batch_generation interface (related to spec V2).
if batch_result is None or batch_result.delay_sample_func is None:

View File

@@ -224,6 +224,9 @@ class SchedulerOutputProcessorMixin:
)
else: # embedding or reward model
if result.copy_done is not None:
result.copy_done.synchronize()
is_sparse = envs.SGLANG_EMBEDDINGS_SPARSE_HEAD.is_set()
embeddings = result.embeddings

View File

@@ -1431,9 +1431,11 @@ class ServerArgs:
self.disable_radix_cache = True
self.disable_overlap_schedule = False
if not self.get_model_config().is_generation:
if envs.SGLANG_EMBEDDINGS_SPARSE_HEAD.is_set():
self.disable_overlap_schedule = True
logger.warning("Overlap scheduler is disabled for embedding models.")
logger.warning(
f"Overlap scheduler is disabled when using sparse head for embedding model."
)
# TRTLLM AllReduce Fusion supports SM90/100/120, enable it by default
# for models with explicit support (DeepseekV3, GptOss, Glm4Moe, Qwen3Moe)