Add overlap scheduling for embeddings code path (#14032)
Co-authored-by: Satyam Kumar <satyamk@linkedin.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user