[diffusion] log: unify generation performance logging (#14117)

This commit is contained in:
Mick
2025-11-29 12:21:59 +08:00
committed by GitHub
parent 143b57b805
commit 0a362d653f
3 changed files with 128 additions and 73 deletions

View File

@@ -34,6 +34,8 @@ from sglang.multimodal_gen.runtime.server_args import PortArgs, ServerArgs
from sglang.multimodal_gen.runtime.sync_scheduler_client import sync_scheduler_client
from sglang.multimodal_gen.runtime.utils.logging_utils import (
init_logger,
log_batch_completion,
log_generation_timer,
suppress_loggers,
suppress_other_loggers,
)
@@ -287,73 +289,54 @@ class DiffGenerator:
# 2. send requests to scheduler, one at a time
# TODO: send batch when supported
for request_idx, req in enumerate(requests):
logger.info(
"Processing prompt %d/%d: %s",
request_idx + 1,
len(requests),
req.prompt[:100],
)
try:
start_time = time.perf_counter()
output_batch = self._send_to_scheduler_and_wait_for_response([req])
gen_time = time.perf_counter() - start_time
if output_batch.error:
raise Exception(f"{output_batch.error}")
with log_generation_timer(
logger, req.prompt, request_idx + 1, len(requests)
) as timer:
output_batch = self._send_to_scheduler_and_wait_for_response([req])
if output_batch.error:
raise Exception(f"{output_batch.error}")
# FIXME: in generate mode, an internal assertion error won't raise an error
logger.info(
"Pixel data generated successfully in %.2f seconds",
gen_time,
)
if output_batch.output is None:
logger.error(
"Received empty output from scheduler for prompt %d",
request_idx + 1,
)
continue
for output_idx, sample in enumerate(output_batch.output):
num_outputs = len(output_batch.output)
frames = self.post_process_sample(
sample,
fps=req.fps,
save_output=req.save_output,
save_file_path=req.output_file_path(
num_outputs, output_idx
),
data_type=req.data_type,
)
if output_batch.output is None:
logger.error(
"Received empty output from scheduler for prompt %d",
request_idx + 1,
)
continue
for output_idx, sample in enumerate(output_batch.output):
num_outputs = len(output_batch.output)
frames = self.post_process_sample(
sample,
fps=req.fps,
save_output=req.save_output,
save_file_path=req.output_file_path(num_outputs, output_idx),
data_type=req.data_type,
)
result_item: dict[str, Any] = {
"samples": sample,
"frames": frames,
"prompts": req.prompt,
"size": (req.height, req.width, req.num_frames),
"generation_time": gen_time,
"timings": (
output_batch.timings.to_dict()
if output_batch.timings
else {}
),
"trajectory": output_batch.trajectory_latents,
"trajectory_timesteps": output_batch.trajectory_timesteps,
"trajectory_decoded": output_batch.trajectory_decoded,
"prompt_index": output_idx,
}
results.append(result_item)
except Exception as e:
logger.error(
"Failed to generate output for prompt %d: %s",
request_idx + 1,
e,
exc_info=True,
)
result_item: dict[str, Any] = {
"samples": sample,
"frames": frames,
"prompts": req.prompt,
"size": (req.height, req.width, req.num_frames),
"generation_time": timer.duration,
"timings": (
output_batch.timings.to_dict()
if output_batch.timings
else {}
),
"trajectory": output_batch.trajectory_latents,
"trajectory_timesteps": output_batch.trajectory_timesteps,
"trajectory_decoded": output_batch.trajectory_decoded,
"prompt_index": output_idx,
}
results.append(result_item)
except Exception:
continue
total_gen_time = time.perf_counter() - total_start_time
logger.info(
"Completed batch processing. Generated %d outputs in %.2f seconds.",
len(results),
total_gen_time,
)
log_batch_completion(logger, len(results), total_gen_time)
if len(results) == 0:
return None

View File

@@ -1,6 +1,7 @@
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
import dataclasses
import os
import time
from typing import Optional
import imageio
@@ -11,7 +12,11 @@ from einops import rearrange
from fastapi import UploadFile
from sglang.multimodal_gen.configs.sample.base import DataType
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
from sglang.multimodal_gen.runtime.utils.logging_utils import (
init_logger,
log_batch_completion,
log_generation_timer,
)
logger = init_logger(__name__)
@@ -98,16 +103,23 @@ async def process_generation_batch(
scheduler_client,
batch,
):
result = await scheduler_client.forward([batch])
if result.output is None:
raise RuntimeError("Model generation returned no output.")
total_start_time = time.perf_counter()
with log_generation_timer(logger, batch.prompt):
result = await scheduler_client.forward([batch])
if result.output is None:
raise RuntimeError("Model generation returned no output.")
save_file_path = str(os.path.join(batch.output_path, batch.output_file_name))
post_process_sample(
result.output[0],
batch.data_type,
batch.fps,
batch.save_output,
save_file_path,
)
total_time = time.perf_counter() - total_start_time
log_batch_completion(logger, 1, total_time)
save_file_path = str(os.path.join(batch.output_path, batch.output_file_name))
post_process_sample(
result.output[0],
batch.data_type,
batch.fps,
batch.save_output,
save_file_path,
)
return save_file_path

View File

@@ -8,6 +8,7 @@ import datetime
import logging
import os
import sys
import time
import warnings
from contextlib import contextmanager
from functools import lru_cache, partial
@@ -420,3 +421,62 @@ def suppress_other_loggers(not_suppress_on_main_rank: bool = False):
if should_suppress:
for logger_name, level in original_levels.items():
logging.getLogger(logger_name).setLevel(level)
class GenerationTimer:
def __init__(self):
self.start_time = 0.0
self.end_time = 0.0
self.duration = 0.0
@contextmanager
def log_generation_timer(
logger: logging.Logger,
prompt: str,
request_idx: int | None = None,
total_requests: int | None = None,
):
if request_idx is not None and total_requests is not None:
logger.info(
"Processing prompt %d/%d: %s",
request_idx,
total_requests,
prompt[:100],
)
else:
max_len = 100
suffix = "..." if len(prompt) > max_len else ""
logger.info(f"Processing prompt: {prompt[:100]}{suffix}")
timer = GenerationTimer()
timer.start_time = time.perf_counter()
try:
yield timer
timer.end_time = time.perf_counter()
timer.duration = timer.end_time - timer.start_time
logger.info("Pixel data generated successfully in %.2f seconds", timer.duration)
except Exception as e:
if request_idx is not None:
logger.error(
"Failed to generate output for prompt %d: %s",
request_idx,
e,
exc_info=True,
)
else:
logger.error(
f"Failed to generate output for prompt: {e}",
exc_info=True,
)
raise
def log_batch_completion(
logger: logging.Logger, num_outputs: int, total_time: float
) -> None:
logger.info(
"Completed batch processing. Generated %d outputs in %.2f seconds.",
num_outputs,
total_time,
)