Bayesian-optimization autotuner for vLLM serving configs - fork of github.com/SergioMorillas/vllm-bayesian-autotuner with Sub-Net patches (dflash spec-decoding, categorical sampling axes, bare-metal launch, env-key injection hardening)
Find a file
Florian Berthold 43fbffe342 Add dflash spec-decoding, categorical sampling axes, and bare-metal launch support
- config_space: add "dflash" to SpeculativeMethod; account its standalone
  draft model in estimate_memory_breakdown (like "draft_model") so the
  feasibility gate does not treat the draft weights as free
- bayes_opt: per-method pinned num_speculative_tokens, categorical k
  choices, discrete max_num_seqs choices (range fallback unchanged);
  seed_configs snaps to the nearest declared choice on a discrete axis
  instead of enqueueing an out-of-distribution value
- remote_process: render extra_context["env"] as a shlex-quoted shell
  prefix for bare-metal launches and draft prefetch; detect docker-less
  hosts in the container lookup; fast-fail the readiness wait when a
  bare-metal trial process has already exited
- run_autotune_optuna: map max_num_seqs_choices, spec_tokens_choices,
  spec_fixed_num_tokens and env from the JSON config
- security: validate environment-variable names against the POSIX rule
  before interpolating them into shell commands (bare-metal prefix and
  docker -e flags) -- values were already shlex-quoted, keys were not
2026-09-14 09:47:31 +02:00
autotuner Add dflash spec-decoding, categorical sampling axes, and bare-metal launch support 2026-09-14 09:47:31 +02:00
configs feat: initial public release of vLLM Bayesian Autotuner 2026-07-30 16:14:13 +02:00
datos/prompt_banks fix: mcp_tool_use prompt bank now asks for the JSON-RPC format it's scored against 2026-08-21 16:48:14 +02:00
docs docs: publish Qwen3.8-27B / Qwen3.6-27B-FP8 experiment data (anonymized) 2026-08-21 16:46:07 +02:00
pages refactor: introduce the service layer, retire the old monolithic app.py/dashboard.py 2026-08-21 16:27:43 +02:00
results feat: initial public release of vLLM Bayesian Autotuner 2026-07-30 16:14:13 +02:00
scripts Add dflash spec-decoding, categorical sampling axes, and bare-metal launch support 2026-09-14 09:47:31 +02:00
tests refactor: introduce the service layer, retire the old monolithic app.py/dashboard.py 2026-08-21 16:27:43 +02:00
.gitignore feat: initial public release of vLLM Bayesian Autotuner 2026-07-30 16:14:13 +02:00
app.py refactor: introduce the service layer, retire the old monolithic app.py/dashboard.py 2026-08-21 16:27:43 +02:00
LICENSE feat: initial public release of vLLM Bayesian Autotuner 2026-07-30 16:14:13 +02:00
pyproject.toml refactor: introduce the service layer, retire the old monolithic app.py/dashboard.py 2026-08-21 16:27:43 +02:00
README.md feat: initial public release of vLLM Bayesian Autotuner 2026-07-30 16:14:13 +02:00
REPRODUCE.md feat: initial public release of vLLM Bayesian Autotuner 2026-07-30 16:14:13 +02:00
test_model.py feat: initial public release of vLLM Bayesian Autotuner 2026-07-30 16:14:13 +02:00

vLLM Bayesian Autotuner

Automatic configuration tuning for vLLM inference servers via Bayesian Optimization.

This project autotunes vLLM serving configurations by exploring a 24-dimensional parameter space with Tree-structured Parzen Estimator (TPE) optimization. It converges to high-performing settings in 50100 trials, avoiding the costly trial-and-error of manual tuning.

Each trial restarts vllm serve with a new configuration, runs an asynchronous benchmark over representative prompts, measures throughput, latency, and goodput, and feeds the resulting score back into the optimizer. Infeasible configurations are pruned analytically before any GPU time is spent.

At a glance

Feature Status
TPE-based Bayesian Optimization (Optuna)
Random search baseline
Multi-objective optimization (Pareto fronts)
Infeasibility pruning (analytical memory model)
SSH-driven server lifecycle management
Hybrid attention memory model (Mamba + KV)
Speculative decoding integration
Streaming metrics (TTFT, TPOT, ITL, goodput)
Energy monitoring (tokens/joule)
Web UI (Streamlit)
Exhaustive run logging

Verified results

Experiments on NVIDIA H100 NVL (93.58 GiB) with Qwen3.6-35B-A3B (35B MoE, hybrid attention, FP8):

Experiment Strategy Seeds Score (mean ± σ) Throughput (tok/s)
Rich space (20+ dims, speculative decoding) TPE 2 2,594 ± 3 3,704 ± 3
Random 4 2,462 ± 54 3,591 ± 67
Flat space (6 dims) TPE 1 4,630 3,677
Random 1 4,612 3,664

Key findings:

  • TPE outperforms Random by +5.4% in objective score and +3.1% in peak throughput on complex search spaces.
  • TPE shows ~18× lower variance across seeds (σ = 3 vs 54).
  • On simpler spaces, BO ties with Random (+0.4%), suggesting that the advantage of Bayesian optimization grows with configuration-space complexity.
  • BO also discovers domain-relevant patterns automatically, such as mtp > none > ngram for speculative decoding and when TP=2 provides no benefit.

Full experimental details: docs/09-plan-evaluacion-experimental.md

Quick start

The examples below assume that a reachable vLLM server is already running.

Basic usage

# Install
python -m venv .venv && source .venv/bin/activate
pip install -e ".[test]"

# Single benchmark (no optimization)
python scripts/run_benchmark_once.py \
    --vllm-url http://localhost:8000 \
    --model your/model \
    --concurrency 50 \
    --num-requests 256

# Full Bayesian autotuning
python scripts/run_autotune_optuna.py \
    --vllm-url http://localhost:8000 \
    --model your/model \
    --trials 50 \
    --concurrency 16 \
    --num-requests 100

Advanced usage

# Random baseline
python scripts/run_autotune_optuna.py \
    --config configs/your-config.json \
    --sampler random \
    --seed 42 \
    --ssh-host gpu.example.com \
    --ssh-user ubuntu \
    --ssh-key-path ~/.ssh/id_ed25519 \
    --auto-tunnel \
    --run-label my-random-baseline

# Dry run (validate search space without GPU time)
python scripts/run_autotune_optuna.py \
    --config configs/your-config.json \
    --dry-run

# Analyze results
python scripts/analyze_experiment.py

# Web UI
streamlit run app.py

# Tests
pytest -q

Key options

Flag Purpose
--sampler tpe|random TPE (Bayesian, default) or Random (baseline)
--config <file> JSON config file (search space, model, workload)
--trials N Optimization budget (default: 50)
--concurrency N Maximum concurrent requests per trial
--num-requests N Prompts per trial (default: 100)
--ssh-host / --ssh-user SSH connection to the GPU host
--auto-tunnel Automatic SSH port-forward
--dry-run Validate the search space without launching vLLM

Configuration space

The search space consists of up to 24 parameters organized in five blocks. Not all dimensions are explored simultaneously; the effective space is configured per study.

Full space definition: docs/05-espacio-de-configuracion.md

Block Key parameters
Parallelism tensor_parallel_size, pipeline_parallel_size, data_parallel_size, enable_expert_parallel
Memory & cache max_num_seqs (16512), gpu_memory_utilization (0.700.97), kv_cache_dtype (auto/fp8), max_model_len (4K262K), enforce_eager
Scheduler max_num_batched_tokens, enable_chunked_prefill, enable_prefix_caching, block_size, scheduling_policy
Speculative decoding method (none/ngram/mtp/eagle/draft_model/medusa), num_speculative_tokens
Mamba cache mamba_cache_dtype, mamba_ssm_cache_dtype, mamba_block_size, mamba_cache_mode

Feasibility pruning: before consuming GPU time, configurations are filtered through six checks, including structural constraints, head/layer divisibility, and an additive memory model (weights + KV + Mamba state + overhead vs. VRAM).

Objective function

Single-objective (default):

score = throughput_tok/s × cap_factor × floor_factor  α × latency_p95  β × error_rate × 1000
  • cap_factor: discounts low concurrency or low-context configurations.
  • floor_factor: quadratic penalty below a usable throughput floor (default: 20 tok/s).
  • α (default 0.01): tail-latency penalty.
  • β (default 1.0): error-rate penalty.

Multi-objective: optimizes 27 objectives simultaneously via NSGA-II, returning a Pareto front. Available axes: throughput, context, latency, goodput, TTFT, TPOT, tokens/joule.

Full objective specification: docs/07-metricas-y-funcion-objetivo.md

Prompt battery

The benchmark battery contains 794 prompts across 7 categories in datos/prompt_banks/*.jsonl, with one file per category:

Category Count Input range Output target
simple_qa 111 616 tokens 32
complex_reasoning 118 1579 tokens 384
code 118 1152 tokens 256
rag_context 117 2398 tokens 64
long_context 112 ~24K35K tokens 192
mcp_tool_use 112 244713 tokens 512
data_processing 106 594.3K tokens 384

Banks are generated once (seed = 42) and reused identically across trials. Six additional workload profiles are available beyond generalist.

Battery design: docs/06-diseno-bateria-prompts.md

Reproducibility

Every experiment produces a self-contained run directory:

results/runs/<run_id>/
├── requests.jsonl          # One record per HTTP request
├── trial_<n>_manifest.json # Per-trial config + metrics
├── run_manifest.json       # Study summary + all trials
└── run.log                 # Structured log

Convergence plots and Pareto fronts are saved alongside. The analysis script (scripts/analyze_experiment.py) generates best-so-far curves, reproducibility plots, and post-hoc early-stopping estimates from these manifests.

Documentation

Note

Most in-depth design notes are currently available in Spanish.

The docs/ directory contains the technical background and design notes behind the project, including system architecture, configuration-space design, prompt-battery construction, metrics, optimization strategy, experimental methodology, logging, SSH-based orchestration, and research extensions.

Useful entry points:

References

License

MIT