[Renderer] Warm up the renderer properly. (#50408)

Signed-off-by: wang.yuqi <yuqi.wang@daocloud.io>

wang.yuqi committed Jul 31, 2026 at 19:46 UTC f5ffc59b6abbf317708d4c319702bac2b2aa8e72
6 files changed +50 -46
vllm/entrypoints/generate/api_router.py
-2
@@ -140,8 +140,6 @@ async def init_generate_state(
140 if "generate" in supported_tasks
141 else None
142 )
143 - if state.openai_serving_chat is not None:
144 - state.openai_serving_chat.warmup()
143 state.openai_serving_completion = (
144 OpenAIServingCompletion(
145 engine_client,
vllm/entrypoints/llm.py
+3
@@ -55,6 +55,7 @@ from vllm.v1.engine import PauseMode
55 from vllm.v1.engine.llm_engine import LLMEngine
56 from vllm.v1.sample.logits_processor import LogitsProcessor
57
58 +from ..renderers import ChatParams
59 from .offline_utils import _O, _R, OfflineInferenceMixin
60
61 if TYPE_CHECKING:
@@ -352,6 +353,8 @@ class LLM(BeamSearchOfflineMixin, PoolingOfflineMixin, OfflineInferenceMixin):
353 self.chat_template = load_chat_template(chat_template)
354 self.input_processor = self.llm_engine.input_processor
355
356 + self.renderer.warmup(ChatParams(chat_template=self.chat_template))
357 +
358 # The renderer thread pool is only consumed by the async renderer
359 # path; the synchronous `LLM` entrypoint runs multimodal
360 # preprocessing serially. Warn so the setting is not a silent
vllm/entrypoints/openai/api_server.py
+2
@@ -426,6 +426,7 @@ async def init_app_state(
426 default_chat_template_kwargs=args.default_chat_template_kwargs,
427 log_error_stack=args.log_error_stack,
428 )
429 + state.online_renderer.warmup()
430
431 state.online_derenderer = OnlineDerenderer(
432 model_config=engine_client.model_config,
@@ -529,6 +530,7 @@ async def init_render_app_state(
530 default_chat_template_kwargs=args.default_chat_template_kwargs,
531 log_error_stack=args.log_error_stack,
532 )
533 + state.online_renderer.warmup()
534
535 state.online_derenderer = OnlineDerenderer(
536 model_config=vllm_config.model_config,
vllm/entrypoints/openai/chat_completion/serving.py
-10
@@ -60,7 +60,6 @@ from vllm.logprobs import Logprob
60 from vllm.outputs import RequestOutput
61 from vllm.parser import ParserManager
62 from vllm.parser.abstract_parser import Parser
63 -from vllm.renderers import ChatParams
63 from vllm.renderers.online_renderer import OnlineRenderer
64 from vllm.sampling_params import BeamSearchParams, SamplingParams
65 from vllm.tokenizers import TokenizerLike
@@ -178,15 +177,6 @@ class OpenAIServingChat(GenerateBaseServing):
177 self.supports_code_interpreter = False
178 self.python_tool = None
179
181 - def warmup(self) -> None:
182 - self.renderer.warmup(
183 - ChatParams(
184 - chat_template=self.chat_template,
185 - chat_template_content_format=self.chat_template_content_format,
186 - chat_template_kwargs=self.default_chat_template_kwargs,
187 - )
188 - )
189 -
180 def _effective_chat_template_kwargs(
181 self, request: ChatCompletionRequest
182 ) -> dict[str, Any]:
vllm/renderers/base.py
+35 -33
@@ -243,42 +243,44 @@ class BaseRenderer(ABC, Generic[_T]):
243 """
244 from vllm.entrypoints.chat_utils import ChatTemplateResolutionError
245
246 - try:
247 - logger.debug("Warming up chat template processing...")
248 - start_time = time.perf_counter()
249 -
250 - self.render_chat([[{"role": "user", "content": "warmup"}]], chat_params)
251 -
252 - elapsed = time.perf_counter() - start_time
253 - logger.debug("Chat template warmup completed in %.3fs", elapsed)
254 - except ChatTemplateResolutionError:
255 - logger.debug("This model does not support chat template.")
256 - except Exception:
257 - logger.warning("Chat template warmup failed", exc_info=True)
258 -
259 - if self.mm_processor:
246 + # prevent MM processor hangs
247 + with set_default_torch_num_threads(1):
248 try:
261 - logger.debug("Warming up multi-modal processing...")
262 - self._warmup_mm_processor(
263 - self.mm_processor,
264 - log_prefix="Multi-modal",
265 - )
266 - except Exception:
267 - logger.warning("Multi-modal warmup failed")
268 - finally:
269 - self.clear_mm_cache()
249 + logger.debug("Warming up chat template processing...")
250 + start_time = time.perf_counter()
251
271 - if self._readonly_mm_processor is not None:
272 - try:
273 - logger.debug("Warming up readonly multi-modal processing...")
274 - self._warmup_mm_processor(
275 - self._readonly_mm_processor,
276 - log_prefix="Readonly multi-modal",
277 - )
252 + self.render_chat([[{"role": "user", "content": "warmup"}]], chat_params)
253 +
254 + elapsed = time.perf_counter() - start_time
255 + logger.debug("Chat template warmup completed in %.3fs", elapsed)
256 + except ChatTemplateResolutionError:
257 + logger.debug("This model does not support chat template.")
258 except Exception:
279 - logger.warning("Readonly multi-modal warmup failed")
280 - finally:
281 - self._clear_processor_cache(self._readonly_mm_processor)
259 + logger.warning("Chat template warmup failed", exc_info=True)
260 +
261 + if self.mm_processor:
262 + try:
263 + logger.debug("Warming up multi-modal processing...")
264 + self._warmup_mm_processor(
265 + self.mm_processor,
266 + log_prefix="Multi-modal",
267 + )
268 + except Exception:
269 + logger.warning("Multi-modal warmup failed")
270 + finally:
271 + self.clear_mm_cache()
272 +
273 + if self._readonly_mm_processor is not None:
274 + try:
275 + logger.debug("Warming up readonly multi-modal processing...")
276 + self._warmup_mm_processor(
277 + self._readonly_mm_processor,
278 + log_prefix="Readonly multi-modal",
279 + )
280 + except Exception:
281 + logger.warning("Readonly multi-modal warmup failed")
282 + finally:
283 + self._clear_processor_cache(self._readonly_mm_processor)
284
285 async def clear_mm_cache_async(self) -> None:
286 """Serialize clear_mm_cache through the multimodal executor to avoid
vllm/renderers/online_renderer.py
+10 -1
@@ -36,7 +36,7 @@ from vllm.inputs import (
36 )
37 from vllm.logger import init_logger
38 from vllm.parser import Parser, ParserManager
39 -from vllm.renderers import BaseRenderer, merge_kwargs
39 +from vllm.renderers import BaseRenderer, ChatParams, merge_kwargs
40 from vllm.renderers.inputs.preprocess import (
41 parse_model_prompt,
42 prompt_to_seq,
@@ -105,6 +105,15 @@ class OnlineRenderer:
105 self.supports_browsing = False
106 self.supports_code_interpreter = False
107
108 + def warmup(self) -> None:
109 + self.renderer.warmup(
110 + ChatParams(
111 + chat_template=self.chat_template,
112 + chat_template_content_format=self.chat_template_content_format,
113 + chat_template_kwargs=self.default_chat_template_kwargs,
114 + )
115 + )
116 +
117 async def render_chat(
118 self,
119 request: ChatCompletionRequest,