Files
vLLM/tests/benchmarks/test_forward_cli.py
T
2026-10-14 10:52:26 -07:00

128 lines
4.5 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import json
import subprocess
from dataclasses import replace
from types import SimpleNamespace
import numpy as np
import pytest
from vllm.benchmarks.forward.batch import build_batch
from vllm.benchmarks.forward.shapes import jitter_kv_lens, parse_case
from vllm.platforms import current_platform
MODEL_NAME = "meta-llama/Llama-3.2-1B-Instruct"
def _group(block_size: int, replay: int = 0, dcp_sharded: bool = False):
return SimpleNamespace(
kv_cache_spec=SimpleNamespace(
block_size=block_size,
prefix_replay_tokens=replay,
dcp_sharded=dcp_sharded,
)
)
def test_build_batch_layout():
"""The runner sees each request kind as the scheduler would schedule it."""
case = parse_case("c=2*1@101,4@64:3,32,8@48c,16@316")
groups = [_group(16), _group(32, replay=128)]
batch = build_batch(case, groups, 1000, 1000, np.random.default_rng(0))
# (scheduled, computed, is_decode) per request: decode, verify, prefill,
# chunked prefill, then a prefix hit replaying the last 128 hit tokens.
assert list(batch.expected.values()) == [
(1, 100, True),
(1, 100, True),
(4, 60, True),
(32, 0, False),
(8, 40, False),
(16 + 128, 300 - 128, False),
]
output = batch.scheduler_output
assert output.total_num_scheduled_tokens == 1 + 1 + 4 + 32 + 8 + 144
assert len(output.scheduled_spec_decode_tokens["bench-2"]) == 3
assert output.scheduled_new_reqs[-1].replay_start == 300 - 128
for req_id, (scheduled, _, _) in batch.expected.items():
assert len(batch.query_token_ids[req_id]) == scheduled
# Groups share one block pool: block IDs must not overlap.
ranges = sorted(r for group in batch.group_block_ranges for r in group)
assert ranges[0][0] == 1
assert all(a[1] == b[0] for a, b in zip(ranges, ranges[1:]))
with pytest.raises(ValueError, match="KV blocks"):
build_batch(case, groups, 10, 1000, np.random.default_rng(0))
# DCP spreads each block's tokens over the DCP ranks, like the scheduler.
groups = [_group(16, dcp_sharded=True), _group(16)]
batch = build_batch(
parse_case("c=1@640"), groups, 1000, 1000, np.random.default_rng(0), 4
)
assert batch.group_block_ranges == [[(1, 11)], [(11, 51)]]
def test_jitter_kv_lens():
"""Prefixes vary, capped at the max length; queries and fresh prefills don't.
A case's draw depends only on the seed and its name, so adding or reordering
other cases doesn't change it."""
case = parse_case("c=1000*1@1001,4@64:3,32")
jittered = jitter_kv_lens(case, 0.5, 1400, 0)
assert jitter_kv_lens(case, 0.5, 1400, 0) == jittered
other = jitter_kv_lens(replace(case, name="d"), 0.5, 1400, 0)
assert other.requests != jittered.requests
prefixes = np.array([r.prefix_len for r in jittered.requests[:1000]])
assert prefixes.min() >= 500 and max(r.kv_len for r in jittered.requests) == 1400
assert prefixes.std() > 200
assert [r.query_len for r in jittered.requests] == [
r.query_len for r in case.requests
]
assert jittered.requests[-1] == case.requests[-1]
assert jitter_kv_lens(case, 0, 2048, 0) == case
with pytest.raises(ValueError, match="KV length"):
jitter_kv_lens(case, 0, 1000, 0)
@pytest.mark.benchmark
@pytest.mark.skipif(
current_platform.is_rocm(), reason="Hangs on ROCm; the bench is CUDA-only."
)
def test_bench_forward(tmp_path):
output = tmp_path / "forward.json"
command = [
"vllm",
"bench",
"forward",
"--model",
MODEL_NAME,
"--max-model-len",
"2048",
"--gpu-memory-utilization",
"0.5",
"--case",
"decode=8*1@512",
"--case",
"prefill=256",
"--case",
"mix=128@256,4*1@512",
"--iters",
"3",
"--samples",
"2",
"--output-json",
str(output),
]
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)
print(result.stderr)
assert result.returncode == 0, f"Benchmark failed: {result.stderr}"
results = {row["case"]: row for row in json.loads(output.read_text())}
assert list(results) == ["decode", "prefill", "mix"]
for row in results.values():
rank0 = row["ranks"][0]
assert len(rank0["batch_order"]) == len(row["requests"])
assert rank0["finite"] and rank0["graph_deterministic"]
assert row["median_us"] > 0