[diffusion] fix: make warmup image initialization rank-safe (#21817)

This commit is contained in:
HuangJi
2026-04-08 15:51:09 +08:00
committed by GitHub
parent de0cfed159
commit c3c13dd5e3
@@ -4,12 +4,13 @@
import asyncio import asyncio
import os import os
import pickle import pickle
import tempfile
from collections import deque from collections import deque
from typing import Any, List from typing import Any, List
import zmq import zmq
from sglang.multimodal_gen.configs.pipeline_configs.base import ModelTaskType from sglang.multimodal_gen.runtime.distributed import get_world_group
from sglang.multimodal_gen.runtime.entrypoints.openai.utils import ( from sglang.multimodal_gen.runtime.entrypoints.openai.utils import (
_parse_size, _parse_size,
save_image_to_path, save_image_to_path,
@@ -198,32 +199,24 @@ class Scheduler:
# insert warmup reqs constructed with each warmup-resolution # insert warmup reqs constructed with each warmup-resolution
self._warmup_total = len(self.server_args.warmup_resolutions) self._warmup_total = len(self.server_args.warmup_resolutions)
self._warmup_processed = 0 self._warmup_processed = 0
task_type = self.server_args.pipeline_config.task_type
requires_warmup_image = task_type.accepts_image_input()
warmup_input_path = None
if requires_warmup_image:
warmup_input_path = self._prepare_shared_warmup_image_path()
for resolution in self.server_args.warmup_resolutions: for resolution in self.server_args.warmup_resolutions:
width, height = _parse_size(resolution) width, height = _parse_size(resolution)
task_type = self.server_args.pipeline_config.task_type
if task_type in ( if requires_warmup_image:
ModelTaskType.I2I,
ModelTaskType.TI2I,
ModelTaskType.I2V,
ModelTaskType.TI2V,
):
uploads_dir = os.path.join("outputs", "uploads")
os.makedirs(uploads_dir, exist_ok=True)
input_path = asyncio.run(
save_image_to_path(
MINIMUM_PICTURE_BASE64_FOR_WARMUP,
os.path.join(uploads_dir, "warmup_image.jpg"),
)
)
req = Req( req = Req(
data_type=task_type.data_type(), data_type=task_type.data_type(),
width=width, width=width,
height=height, height=height,
prompt="", prompt="",
negative_prompt="", negative_prompt="",
image_path=[input_path], image_path=[warmup_input_path],
) )
else: else:
req = Req( req = Req(
@@ -237,6 +230,53 @@ class Scheduler:
# if server is warmed-up, set this flag to avoid req-based warmup # if server is warmed-up, set this flag to avoid req-based warmup
self.warmed_up = True self.warmed_up = True
def _prepare_shared_warmup_image_path(self) -> str:
world_group = get_world_group()
src_rank = world_group.ranks[0]
warmup_sync: dict[str, str | None]
if world_group.rank == src_rank:
try:
if self.server_args.input_save_path is not None:
uploads_dir = self.server_args.input_save_path
os.makedirs(uploads_dir, exist_ok=True)
else:
uploads_dir = tempfile.mkdtemp(prefix="sglang_input_")
warmup_image_base = os.path.join(uploads_dir, "warmup_image")
input_path = asyncio.run(
save_image_to_path(
MINIMUM_PICTURE_BASE64_FOR_WARMUP,
warmup_image_base,
)
)
warmup_sync = {"input_path": input_path, "error": None}
except Exception as e:
warmup_sync = {"input_path": None, "error": str(e)}
else:
warmup_sync = {}
# Sync rank 0's warmup-image write result (path or error) to all ranks.
warmup_sync = broadcast_pyobj(
warmup_sync,
world_group.rank,
world_group.cpu_group,
src=src_rank,
)
if not isinstance(warmup_sync, dict):
raise RuntimeError("Invalid warmup sync payload received across ranks")
error = warmup_sync.get("error")
if error is not None:
raise RuntimeError(
f"Warmup image preparation failed on rank {src_rank}: {error}"
)
input_path = warmup_sync.get("input_path")
if not isinstance(input_path, str) or not input_path:
raise RuntimeError("Warmup image preparation returned empty input path")
return input_path
def process_received_reqs_with_req_based_warmup( def process_received_reqs_with_req_based_warmup(
self, recv_reqs: List[tuple[bytes, Any]] self, recv_reqs: List[tuple[bytes, Any]]
) -> List[tuple[bytes, Any]]: ) -> List[tuple[bytes, Any]]:
@@ -406,7 +446,7 @@ class Scheduler:
f"Warmup req ({self._warmup_processed}/{self._warmup_total}) processing failed" f"Warmup req ({self._warmup_processed}/{self._warmup_total}) processing failed"
) )
else: else:
logger.info(f"Warmup req processing failed") logger.info("Warmup req processing failed")
# TODO: Support sending back to multiple identities if batched # TODO: Support sending back to multiple identities if batched
self.return_result(output_batch, identities[0], is_warmup=is_warmup) self.return_result(output_batch, identities[0], is_warmup=is_warmup)