Skip to content

Commit 462e915

Browse files
njhilllulmer
authored andcommitted
[V1] Simplify stats logging (vllm-project#14082)
Signed-off-by: Nick Hill <[email protected]> Signed-off-by: Louis Ulmer <[email protected]>
1 parent 0842e1e commit 462e915

File tree

3 files changed

+30
-37
lines changed

3 files changed

+30
-37
lines changed

vllm/v1/engine/async_llm.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: Apache-2.0
22

33
import asyncio
4+
import logging
45
import os
56
from collections.abc import AsyncGenerator, Mapping
67
from typing import Optional, Union
@@ -57,10 +58,9 @@ def __init__(
5758
self.log_stats = log_stats
5859
self.stat_loggers: list[StatLoggerBase] = []
5960
if self.log_stats:
60-
self.stat_loggers.extend([
61-
LoggingStatLogger(),
62-
PrometheusStatLogger(vllm_config),
63-
])
61+
if logger.isEnabledFor(logging.INFO):
62+
self.stat_loggers.append(LoggingStatLogger())
63+
self.stat_loggers.append(PrometheusStatLogger(vllm_config))
6464

6565
# Tokenizer (+ ensure liveness if running in another process).
6666
self.tokenizer = init_tokenizer_from_configs(
@@ -287,7 +287,7 @@ async def _run_output_handler(self):
287287
# 4) Logging.
288288
# TODO(rob): make into a coroutine and launch it in
289289
# background thread once Prometheus overhead is non-trivial.
290-
self._log_stats(
290+
self._record_stats(
291291
scheduler_stats=outputs.scheduler_stats,
292292
iteration_stats=iteration_stats,
293293
)
@@ -306,7 +306,7 @@ async def abort(self, request_id: str) -> None:
306306
if self.log_requests:
307307
logger.info("Aborted request %s.", request_id)
308308

309-
def _log_stats(
309+
def _record_stats(
310310
self,
311311
scheduler_stats: Optional[SchedulerStats],
312312
iteration_stats: Optional[IterationStats],
@@ -316,9 +316,9 @@ def _log_stats(
316316

317317
assert scheduler_stats is not None
318318
assert iteration_stats is not None
319-
for logger in self.stat_loggers:
320-
logger.log(scheduler_stats=scheduler_stats,
321-
iteration_stats=iteration_stats)
319+
for stat_logger in self.stat_loggers:
320+
stat_logger.record(scheduler_stats=scheduler_stats,
321+
iteration_stats=iteration_stats)
322322

323323
def encode(
324324
self,
@@ -354,7 +354,8 @@ async def do_log_stats(
354354
scheduler_outputs=None,
355355
model_output=None,
356356
) -> None:
357-
logger.debug("Called do_log_stats.")
357+
for stat_logger in self.stat_loggers:
358+
stat_logger.log()
358359

359360
async def check_health(self) -> None:
360361
logger.debug("Called check_health.")

vllm/v1/engine/core.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -316,19 +316,10 @@ def run_busy_loop(self):
316316
# Loop until process is sent a SIGINT or SIGTERM
317317
while True:
318318
# 1) Poll the input queue until there is work to do.
319-
if not self.scheduler.has_unfinished_requests():
320-
while True:
321-
try:
322-
req = self.input_queue.get(timeout=POLLING_TIMEOUT_S)
323-
self._handle_client_request(*req)
324-
break
325-
except queue.Empty:
326-
logger.debug("EngineCore busy loop waiting.")
327-
# Break out the loop so we can log_stats in step().
328-
if self.log_stats:
329-
break
330-
except BaseException:
331-
raise
319+
while not self.scheduler.has_unfinished_requests():
320+
logger.debug("EngineCore busy loop waiting.")
321+
req = self.input_queue.get()
322+
self._handle_client_request(*req)
332323

333324
# 2) Handle any new client requests.
334325
while not self.input_queue.empty():

vllm/v1/metrics/loggers.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,19 @@
2121
class StatLoggerBase(ABC):
2222

2323
@abstractmethod
24-
def log(self, scheduler_stats: SchedulerStats,
25-
iteration_stats: IterationStats):
24+
def record(self, scheduler_stats: SchedulerStats,
25+
iteration_stats: IterationStats):
2626
...
2727

28+
def log(self): # noqa
29+
pass
30+
2831

2932
class LoggingStatLogger(StatLoggerBase):
3033

3134
def __init__(self):
3235
self._reset(time.monotonic())
36+
self.last_scheduler_stats = SchedulerStats()
3337

3438
def _reset(self, now):
3539
self.last_log_time = now
@@ -41,11 +45,6 @@ def _reset(self, now):
4145
# Prefix cache metrics. TODO: Make the interval configurable.
4246
self.prefix_caching_metrics = PrefixCachingMetrics()
4347

44-
def _local_interval_elapsed(self, now: float) -> bool:
45-
# Log every _LOCAL_LOGGING_INTERVAL_SEC.
46-
elapsed_time = now - self.last_log_time
47-
return elapsed_time > _LOCAL_LOGGING_INTERVAL_SEC
48-
4948
def _track_iteration_stats(self, iteration_stats: IterationStats):
5049
# Save tracked stats for token counters.
5150
self.num_prompt_tokens.append(iteration_stats.num_prompt_tokens)
@@ -56,24 +55,26 @@ def _get_throughput(self, tracked_stats: list[int], now: float) -> float:
5655
# Compute summary metrics for tracked stats
5756
return float(np.sum(tracked_stats) / (now - self.last_log_time))
5857

59-
def log(self, scheduler_stats: SchedulerStats,
60-
iteration_stats: IterationStats):
58+
def record(self, scheduler_stats: SchedulerStats,
59+
iteration_stats: IterationStats):
6160
"""Log Stats to standard output."""
6261

6362
self._track_iteration_stats(iteration_stats)
6463

6564
self.prefix_caching_metrics.observe(scheduler_stats.prefix_cache_stats)
6665

67-
now = time.monotonic()
68-
if not self._local_interval_elapsed(now):
69-
return
66+
self.last_scheduler_stats = scheduler_stats
7067

68+
def log(self):
69+
now = time.monotonic()
7170
prompt_throughput = self._get_throughput(self.num_prompt_tokens, now)
7271
generation_throughput = self._get_throughput(
7372
self.num_generation_tokens, now)
7473

7574
self._reset(now)
7675

76+
scheduler_stats = self.last_scheduler_stats
77+
7778
# Format and print output.
7879
logger.info(
7980
"Avg prompt throughput: %.1f tokens/s, "
@@ -274,8 +275,8 @@ def log_metrics_info(self, type: str, config_obj: SupportsMetricsInfo):
274275
labelnames=metrics_info.keys()).labels(**metrics_info)
275276
info_gauge.set(1)
276277

277-
def log(self, scheduler_stats: SchedulerStats,
278-
iteration_stats: IterationStats):
278+
def record(self, scheduler_stats: SchedulerStats,
279+
iteration_stats: IterationStats):
279280
"""Log to prometheus."""
280281
self.gauge_scheduler_running.set(scheduler_stats.num_running_reqs)
281282
self.gauge_scheduler_waiting.set(scheduler_stats.num_waiting_reqs)

0 commit comments

Comments
 (0)