Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions docs/cli/bench/serve.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,76 @@ This should be seen as an edge case, and if this behavior can be avoided by sett

</details>

### Image / video generation

For `/v1/images/generations`, `/v1/images/edits`, and `/v1/videos`, set `--endpoint` to that path and omit `--backend`. The endpoint path is registered as the bench request adapter, so a separate named backend is not required.

Example (`/v1/images/generations`):

```bash
vllm bench serve --omni \
--endpoint /v1/images/generations \
--dataset-name random \
--model ~/models/Qwen/Qwen-Image \
--tokenizer ~/models/Qwen/Qwen-Image/tokenizer \
--max-concurrency 1 \
--num-warmups 2 \
--num-prompts 10 \
--random-input-len 64 \
--random-output-len 1 \
--ignore-eos \
--percentile-metrics e2el \
--extra-body '{
"num_inference_steps": 20,
"seed": 42,
"true_cfg_scale": 4.0
}'
```

If successful, following output is like:

```text
============ Serving Benchmark Result ============
Successful requests: 3
Failed requests: 0
Maximum request concurrency: 1
Benchmark duration (s): 8.42
Request throughput (req/s): 0.36
Peak concurrent requests: 2.00
-------------------Peak Memory--------------------
Mean PEAK_MEMORY_MB (MB): 58832.00
Median PEAK_MEMORY_MB (MB): 58832.00
P99 PEAK_MEMORY_MB (MB): 58832.00
----------------End-to-end Latency----------------
Mean E2EL (ms): 2798.16
Median E2EL (ms): 2799.83
P99 E2EL (ms): 2800.03
================== Image Result ==================
Total images generated: 3
Image throughput (img/s): 0.36
Average pixels per image: 1048576.00
Mean denoise step latency (ms): 136.59
---------------- Image Generation ----------------
Mean IMAGE_GENERATION (ms): 2731.84
Median IMAGE_GENERATION (ms): 2732.02
P99 IMAGE_GENERATION (ms): 2734.65
==================================================
```

Use the same pattern with `--endpoint /v1/images/edits` or `--endpoint /v1/videos` (and model-specific `--extra-body` as needed). Pure image/video runs omit the Text Result section when there is no generated text.

`/v1/images/edits` defaults to **non-streaming JSON** (`stream=false`) so single-stage edit models are not rejected by the server. For multi-stage pipelines that need SSE (AR TTFT / image chunks), pass `"stream": true` in `--extra-body`.

`/v1/videos` is an async job API: the client creates a job, then polls until `completed`/`failed`. Measured **e2el** therefore includes client poll sleep and any overshoot after the job actually finishes. Tune polling via `--extra-body`:

- `poll_interval_s` (default `2.0`): sleep between status polls
- `poll_timeout_s` (default `21600`, i.e. 6 hours): give up waiting for the job

**VIDEO_RTF** prefers server-reported generation time when available:

- `video_rtf = video_generation_time_ms / 1000 / video_duration`, where `video_generation_time_ms` comes from response `stage_durations` (or `inference_time_s` as fallback)
- If generation time is missing, falls back to `e2el / video_duration` (this fallback *does* include poll overhead)

### Multi-Stage Benchmark

<details class="admonition abstract" markdown="1">
Expand Down
131 changes: 131 additions & 0 deletions tests/benchmarks/metrics/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,5 +457,136 @@ def test_text_benchmark_still_reports_ttft(capsys):
assert "Time to First Token" in out, "text benchmarks must keep TTFT"


# ============================================================================
# Suppress text metrics for pure image / video endpoints
# ============================================================================


def _make_image_output(prompt_len: int) -> MixRequestFuncOutput:
output = MixRequestFuncOutput()
output.success = True
output.prompt_len = prompt_len
output.output_tokens = 0
output.generated_text = ""
output.ttft = 0.0
output.text_latency = 0.0
output.latency = 2.5
output.start_time = 0.0
output.itl = []
output.image_count = 1
output.image_generation_time_ms = 2000.0
output.image_pixels = 1024 * 1024
output.input_audio_duration = 0.0
output.error = ""
return output


def _make_video_output(prompt_len: int) -> MixRequestFuncOutput:
output = MixRequestFuncOutput()
output.success = True
output.prompt_len = prompt_len
output.output_tokens = 0
output.generated_text = ""
output.ttft = 0.0
output.text_latency = 0.0
output.latency = 5.0
output.start_time = 0.0
output.itl = []
output.video_duration = 2.0
output.video_frames = 48
output.video_generation_time_ms = 4000.0
output.video_rtf = 2.0
output.input_audio_duration = 0.0
output.error = ""
return output


_MEDIA_PERCENTILE_METRICS = ["e2el", "ttft"]


def test_pure_image_benchmark_omits_text_result(capsys):
"""Pure image generation must not print Text Result or invent peak tok/s."""
outputs = [_make_image_output(64), _make_image_output(64)]

metrics, actual_output_lens = calculate_metrics(
input_requests=[],
outputs=outputs,
dur_s=5.0,
tokenizer=None,
selected_percentiles=[99.0],
goodput_config_dict={},
task_type=TaskType.GENERATION,
selected_percentile_metrics=_MEDIA_PERCENTILE_METRICS,
max_concurrency=None,
request_rate=float("inf"),
benchmark_duration=5.0,
)

out = capsys.readouterr().out
assert actual_output_lens == [0, 0]
assert metrics.total_output == 0
assert metrics.max_output_tokens_per_s == 0.0
assert " Text Result " not in out
assert "Peak output token throughput" not in out
assert "Time to First Token" not in out
assert " Image Result " in out


def test_pure_video_benchmark_omits_text_result(capsys):
"""Pure video generation must not print Text Result or invent peak tok/s."""
outputs = [_make_video_output(63), _make_video_output(63)]

metrics, actual_output_lens = calculate_metrics(
input_requests=[],
outputs=outputs,
dur_s=10.0,
tokenizer=None,
selected_percentiles=[99.0],
goodput_config_dict={},
task_type=TaskType.GENERATION,
selected_percentile_metrics=_MEDIA_PERCENTILE_METRICS,
max_concurrency=None,
request_rate=float("inf"),
benchmark_duration=10.0,
)

out = capsys.readouterr().out
assert actual_output_lens == [0, 0]
assert metrics.total_output == 0
assert metrics.max_output_tokens_per_s == 0.0
assert " Text Result " not in out
assert "Peak output token throughput" not in out
assert "Time to First Token" not in out
assert " Video Result " in out


def test_image_with_generated_text_still_reports_text_result(capsys):
"""Image edits / AR text alongside images must keep Text Result."""
output = _make_image_output(64)
output.output_tokens = 8
output.generated_text = "revised caption"
output.ttft = 0.05
output.itl = [0.01] * 7

calculate_metrics(
input_requests=[],
outputs=[output],
dur_s=3.0,
tokenizer=_EmptyAwareTokenizer(),
selected_percentiles=[99.0],
goodput_config_dict={},
task_type=TaskType.GENERATION,
selected_percentile_metrics=_MEDIA_PERCENTILE_METRICS,
max_concurrency=None,
request_rate=float("inf"),
benchmark_duration=3.0,
)

out = capsys.readouterr().out
assert " Text Result " in out
assert "Time to First Token" in out
assert " Image Result " in out


if __name__ == "__main__":
pytest.main([__file__, "-v", "-s"])
Loading
Loading