agentsclimarketplace

Vllm benchmarking

Skill air-gapped/skills/.claude/skills/vllm-benchmarking

Run production vLLM benchmarks — `vllm bench` (serve, throughput, latency, sweep, startup, mm-processor), request-rate vs max-concurrency semantics, TTFT/TPOT/ITL/E2EL percentiles, goodput SLO measurement, prefix-cache workloads, air-gapped operation (HF_ENDPOINT, ModelScope, hf-mirror, offline cache). Methodology split — SLO health checks vs A/B change sweeps — plus pitfalls that produce misleading numbers (no warmup, wrong tokenizer, random-as-prod, `--request-rate inf` alone).From its SKILL.md

Install
npx -y skills add air-gapped/skills --skill vllm-benchmarking

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.1 KB, ~2.6k tokens by cl100k_base, as published. Nobody here has run it

vLLM benchmarking

Target audience: operators producing defensible latency/throughput numbers against production or pre-production vLLM deployments, on datacenter GPUs, often in containerized or air-gapped environments.

This skill measures; it does not tune. Once a number is trusted and the verdict is "too slow", the knobs live elsewhere in the vllm plugin: vllm-performance-tuning (scheduler, MoE kernels, CUDA graphs, parallelism), vllm-caching (KV tiering when the bottleneck is prefill or cache capacity), vllm-nvidia-hardware (the SKU's own ceiling). Measure → change one thing → re-measure with the same methodology; a tuning change compared against a differently-shaped benchmark run is not evidence.

Why this matters

Bad benchmarks are worse than no benchmarks — they drive the wrong decisions with false confidence. The three common failure modes:

  1. Wrong methodology. --request-rate inf answers "saturation throughput," not "TTFT my users see." Mixing those up leads to buying GPUs to solve a latency problem, or shipping a latency regression because total throughput looked fine.
  2. Wrong workload. --dataset-name random has zero prefix structure. Real coding-agent or RAG traffic has heavy prefix reuse. Benchmarking caching wins on random produces numbers that don't survive contact with prod.
  3. No warmup / wrong tokenizer. First N requests hit cold CUDA graphs. Token counts are fiction unless --tokenizer matches the served model exactly.

The cost of getting this right is small; the cost of getting it wrong is buying the wrong hardware.

Decision tree — which subcommand

QuestionCommandWhy
"Saturation throughput of this offline batch"vllm bench throughputSubmits N prompts at once, measures tok/s. No server.
"Single-batch generation latency"vllm bench latencyFixed batch size, repeated N times. Warmup included. Good for kernel-level regression.
"Production serving performance"vllm bench serveHTTP-level, Poisson arrivals, percentile metrics, honors concurrency caps. Use this for serving.
"Find best config under SLO"vllm bench sweepParameter sweep + auto-tune. Finds max throughput subject to P99 < X ms.
"Cold-start / container boot latency"vllm bench startupTime from process launch to first-token-ready.
"Multimodal processor overhead"vllm bench mm-processorImage/video preprocessing cost before decode.

Most production questions route to vllm bench serve. Reach for the others only when the question is specifically kernel-level (latency), offline-batch (throughput), or SLO auto-tuning (sweep).

The two methodologies operators actually need

Methodology A: health check / SLO validation

Question: "Does my running deployment meet the latency SLO under realistic load?"

  • Fixed --max-concurrency matching the production in-flight ceiling (NOT --request-rate).
  • Realistic input/output length distribution — ideally replayed from production logs via --dataset-name custom with a JSONL file.
  • Sustained 10+ minute run to cover warmup + steady state.
  • Report: P50/P95/P99 TTFT, P95/P99 ITL, throughput (tok/s, req/s).
  • Compare against the SLO. Pass/fail.
vllm bench serve \
  --model <served-model> \
  --base-url http://<endpoint> \
  --dataset-name custom \
  --dataset-path /data/captured-prod-prompts.jsonl \
  --max-concurrency 32 \
  --num-prompts 2000 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,95,99 \
  --save-result --output-json health-check.json

Methodology B: change comparison / A/B

Question: "Does config change X make it faster, and at what cost?"

  • Request-rate sweep, not a single rate: e.g. 1, 2, 4, 8, 16, 32, inf req/s.
  • Plot throughput vs P99 latency — the knee of the curve is the usable operating point. A config that shifts the knee right is a win.
  • Same seeds, same --num-prompts (≥500), same dataset on both sides.
  • Run A and B back-to-back on the same hardware in the same session to avoid thermal/neighbor noise.

See scripts/bench-sweep.sh for a parametrized sweep runner that emits one JSON file per rate for plotting.

Critical pitfalls

  1. No warmup. First 30–60 s hit cold CUDA graphs / torch.compile caches. vllm bench serve does not auto-warm (v0.11–v0.25; --num-warmups default re-read as 0 on 2026-07-21) — pre-flight the server with a few requests, or set --num-prompts large enough (≥500) to amortize. latency does warm up via --num-iters-warmup (default 10).
  2. Wrong tokenizer. --tokenizer defaults to --model, but if they differ (e.g., served via a local path while benching with a HF ID), every token count in the output is fiction. Always specify explicitly.
  3. --dataset-name random as a proxy for production traffic. Random has zero prefix structure, overstates prefill work, understates prefix-cache hit rate, makes chunked prefill look worse than reality. For anything involving caching claims, use custom with a real-traffic JSONL, or prefix_repetition for synthetic prefix-heavy tests.
  4. --request-rate inf alone. Measures saturation throughput, not the latency regime users experience. Always include a concurrency sweep for serving comparisons.
  5. --endpoint-type is removed. Deprecated in v0.11.0, now gone. Use --backend. Current full value set (docs.vllm.ai, verified 2026-04-24): openai, openai-chat, openai-audio, openai-embeddings, openai-embeddings-chat, openai-embeddings-clip, openai-embeddings-vlm2vec, vllm, vllm-chat, vllm-pooling, vllm-rerank, infinity-embeddings, infinity-embeddings-clip.
  6. Conflating tok/s with req/s. High total-tokens/sec can coexist with terrible TTFT. Always report both plus P99 ITL.
  7. Noisy neighbor. Shared GPU, unrelated container load, MIG partition changes mid-run — check nvidia-smi dmon for unrelated activity before trusting numbers.
  8. latency subcommand disables prefix caching by default (to keep numbers clean). If benchmarking prefix-cache behavior, use serve with the prefix_repetition dataset.

For the full flag reference for each subcommand, see references/commands.md. For the dataset catalog and when to use each, see references/datasets.md.

Air-gapped environments

Operators who can't reach huggingface.co have three working patterns:

  1. Reroute to a mirror — set HF_ENDPOINT=https://hf-mirror.com (or an internal reverse-proxy URL). huggingface_hub treats it transparently.
  2. ModelScope — set VLLM_USE_MODELSCOPE=True plus trust_remote_code=True. Historical gap: LoRA adapter loading through ModelScope (vLLM issue #32841, closed 2026-01-23). Re-verify on your vLLM version before relying on LoRA-via-ModelScope; issue closure without a linked PR means status is unclear — test first.
  3. Fully offline with pre-seeded cacheHF_HUB_OFFLINE=1 + TRANSFORMERS_OFFLINE=1, HF_HOME pointing at a pre-populated directory (NFS, PVC, or JuiceFS/S3).

For benchmark datasets specifically: sonnet is in-tree at vllm/benchmarks/sonnet.txt — never downloads. random is synthetic — never downloads. sharegpt must be pre-staged: wget the JSON on a connected host, rsync into the enclave, point --dataset-path at it.

For the full air-gapped recipe (HF proxy setup, gated model tokens, MinIO-as-HF-cache, transformer cache warming), see references/air-gapped.md.

Measuring the outcomes that matter

Default metrics (--percentile-metrics ttft,tpot,itl,e2el):

  • TTFT — time-to-first-token. User-facing responsiveness. Dominated by prefill.
  • TPOT — time-per-output-token (averaged across decode). Steady-state perceived speed.
  • ITL — inter-token latency (per-step). Catches stalls that TPOT averages away.
  • E2EL — end-to-end request latency. Only one that matters for pooling/embedding models.

Reporting guideline: always P50 and P99 together. Either in isolation is misleading. Add P95 if ITL has a long tail.

Goodput SLO--goodput KEY:VALUE (milliseconds) tracks requests that completed within an SLO budget. Example: --goodput ttft:500 itl:50. Goodput is what actually matters in production; raw throughput that violates SLO is useless.

For methodology detail (warmup protocols, sweep design, SLO-constrained auto-tune, how to capture real-traffic prompts for replay), see references/methodology.md.

When numbers look wrong or a run crashes

See references/troubleshooting.md for the failure modes: tokenizer mismatch (numbers off 20–40%), cold-cache contamination (suspiciously fast), air-gapped hang (incomplete HF_HUB_OFFLINE setup), goodput=0 (unit error), noisy-neighbor ITL variance, and the full "what to include in a bug report" checklist.

Parsing the output JSON

See references/output-schema.md for the field layout in --output-json — top-level fields (request_throughput, output_throughput, total_token_throughput), the mean_/median_/std_/p<N>_<metric>_ms pattern, speculative decoding fields, and which names are stable across versions vs renamed.

External references

What ships with it: 9 files

50.5 KB alongside SKILL.md, 1 of them executable

scripts/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.