Files
club-3090/docs/engines/VLLM.md
T
noonghunnaandClaude Opus 4.7 acd7ffb67c restructure: promote topology to a directory level (single/dual/multi4)
Compose files now live under `<model>/<engine>/compose/<topology>/<file>.yml`,
with topology as a folder rather than a filename prefix. Solves all 7
inconsistencies surfaced in the post-rename audit (single-card composes
without `single-` prefix, unsuffixed `docker-compose.yml` ambiguity,
fine-tunes encoding model name in filename, etc.) by making the directory
hierarchy enforce the convention.

Layout:
  models/<model>/<engine>/compose/<topology>/<feature>.yml

Where:
  - <model>:    qwen3.6-27b, gemma-4-31b
  - <engine>:   vllm, llama-cpp, sglang
  - <topology>: single, dual, multi3, multi4, multi8
  - <feature>:  docker-compose.yml (default) | turbo.yml | dflash.yml | etc.

Each topology subdir has a `docker-compose.yml` for the recommended
starter — bare `cd <topology> && docker compose up` works because
docker compose finds that filename automatically. Variants drop the
`docker-compose.` prefix since they're invoked via `-f` flag.

27 compose file moves total:
- 18 Qwen vLLM composes redistributed across single/dual/multi4
- 2 Qwen llama-cpp composes into single/
- 6 Gemma vLLM composes redistributed across single/dual
- 1 untracked qwopus-bf16mtp moved to dual/

Inside each compose: relative paths to `../patches/` and `../cache/`
bumped to `../../patches/` / `../../cache/`, and `../../../../models-cache`
to `../../../../../models-cache` (one extra `..` for the new depth).

Reference updates across 148 files (BENCHMARKS, all docs, CHANGELOGs,
sibling-table cross-references in compose headers, scripts, patch
READMEs, .github issue templates, tools/residency-instrument).

scripts/switch.sh VARIANTS map updated; tags themselves unchanged
(`vllm/dual` → `dual/docker-compose.yml`, `vllm/dual4` → `multi4/docker-compose.yml`,
`vllm/gemma-mtp` → `gemma-4-31b/.../dual/docker-compose.yml`, etc.).

AGENTS.md "Compose layout" section rewritten to describe the new
hierarchy, with concrete examples and the fine-tune exception
(`dual/carnice-bf16mtp.yml` carries the fine-tune name as a filename
prefix until the fine-tune graduates to its own model directory).

All switch.sh paths verified to resolve to actual files post-move.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-09 12:11:17 +00:00

7.5 KiB
Raw Blame History

vLLM — the validated path (this repo)

This is what the repo's Quick start ships. Everything in the main README is the vLLM recipe — this page is a brief recap + tuning levers + when to deviate from defaults.


TL;DR

  • ✅ Validated, production-grade
  • ✅ Full feature set: vision, tools, streaming, thinking, MTP n=3, TurboQuant 3-bit KV
  • ✅ Full OpenAI API parity
  • 50-53 narr / 66-70 code TPS on a single 3090; 58 narr / 76 code on dual-card (TP=2)
  • 48K default ctx · 75K IDE-agent · 145K with vision · 180K Balanced MTP · 200K Max-context (single 3090) · 262K dual-card. Cliff 2 closed at 60K since 2026-05-02 PM via Genesis v7.69 + vllm#35975 backport — see docs/CLIFFS.md. >60K single-prompt still hits the 24 GB hardware-physical wall on single-card.

What's in the box

  • vLLM nightly (pinned to vllm/vllm-openai:nightly-7a1eb8ac2ec4ea69338c51dc7afd4b15010abfa8 = 0.20.1rc1.dev16+g7a1eb8ac2)
  • Sandermage's Genesis v7.69 dev tip patches (commit 2db18df, mounted into vLLM's site-packages at boot)
  • patch_inputs_embeds_optional.py sidecar — backport of vllm#35975, skips the text-only inputs_embeds GPU buffer (~444 MiB freed at boot on our 180K + MTP K=3 path). Required for the Cliff 2 60K closure recipe.
  • Our patch_tolist_cudagraph.py (CUDA graph capture fix for TurboQuant continuation prefill)
  • 6 compose variants with different KV/ctx/feature trade-offs (see README Status)

Quick recipe

The README's Quick start is the canonical recipe. Reproduced here:

git clone https://github.com/noonghunna/qwen36-27b-single-3090.git
cd qwen36-27b-single-3090
bash scripts/setup.sh
cd compose && docker compose up -d
docker logs -f vllm-qwen36-27b      # wait for "Application startup complete"
curl -sf http://localhost:8020/v1/models

Verify:

bash scripts/verify-full.sh         # 10 functional checks
bash scripts/bench.sh               # 3 warmups + 5 measured (narr + code)

Pros (vs llama.cpp / SGLang)

Pro Detail
Deepest Qwen3-Next feature support Vision tower, MTP head, all attention variants supported upstream.
TurboQuant 3-bit KV Lets us reach 198K + vision or 214K text-only on 24 GB single-card (262K on dual-card). No equivalent in llama.cpp; SGLang has it but blocked by other bugs.
MTP speculative decoding Works out of the box on the Lorbus quant; mainline llama.cpp doesn't expose MTP.
Active development Bugs we hit get triaged within days. We've contributed back.
Full OpenAI API parity Tools, streaming, vision-in-message, reasoning-mode, structured output — everything works.

Cons (vs llama.cpp / SGLang)

Con Detail
Heavyweight Docker image is ~9 GB. NVIDIA-only.
Longer cold start ~2 min for compile + cudagraph capture.
Sensitive to upstream API drift We pin to a specific nightly SHA (7a1eb8ac = 0.20.1rc1.dev16) to avoid this. Bumping the pin needs re-validation across all five main variants.
Frontier features can ship with bugs TurboQuant × spec-decode × cudagraph corruption (the whole reason this repo's patches exist).

Tuning levers

--max-model-len and --gpu-memory-utilization

Control context vs activation headroom. See the Activation-memory caveat — this is the most consequential tuning lever.

KV cache type (--kv-cache-dtype)

Type Per-token bytes 24 GB ceiling Notes
BF16 (default) ~55 KB ~8K Don't use on this hardware
fp8_e5m2 ~28 KB ~32K Fast-chat / Tools-text variants
turboquant_4bit_nc ~23 KB ~84K Untested by us — should work
turboquant_3bit_nc ⭐ ~17 KB ~125K Default v7.14 variant

Lower bytes/token = more context, but more dequant scratch + activation pressure. The 3-bit variant is what makes the 198K + vision and 214K text-only tiers reachable on a single 24 GB card.

Spec-decode (--speculative-config)

--speculative-config '{"method":"mtp","num_speculative_tokens":3}'

n=3 is the empirical sweet spot. n=4 nominally hits higher TPS on code but 4th-position acceptance collapses to ~21%. Don't push higher.

Power cap

sudo nvidia-smi -pm 1            # one-time: enable persistence mode
sudo nvidia-smi -pl 290 -i 0     # air-cooled default — peak TPS/W (per 10W-resolution sweep)
sudo nvidia-smi -pl 330 -i 0     # water-cooled default (per @syangsao 3-cap data)
sudo nvidia-smi -pl 230 -i 0     # thermal-constrained / quiet — NOT a sweet spot, see note

Sweet spot: 290W (air) / 330W (water) — peak TPS/W efficiency on 3090s and only ~5-7% TPS loss vs unrestricted stock. Past the sweet spot: diminishing returns (SM clocks saturate near 1.9 GHz). Stock TDP is less efficient than the sweet-spot cap on Qwen3.6's GDN-attention kernels.

Note: 230W is NOT the sweet spot — 290W is. The "230W sweet spot" lore traces back to coarse 3-cap-resolution data; the dense 10W sweep on this rig shows 230W costs ~16% efficiency vs 290W (air-cooled). 230W is a low-power / quiet cap, not a perf-per-watt one. On llama.cpp + Qwen3.6 it costs ~34% TPS (cross-rig data from @syangsao) because the GDN forward kernel is genuinely compute-bound. On vLLM the penalty is smaller (~10-15%) because the kernel mix is GEMM-dominated, but 290W is still the better default. See docs/HARDWARE.md#power for the full cross-rig table.

Genesis env-opt-in patches

The default compose enables P64/P65/P66 by default. Optional patches (env-gated):

- GENESIS_ENABLE_P68_AUTO_FORCE_TOOL=1            # long-ctx tool adherence
- GENESIS_ENABLE_P69_LONG_CTX_TOOL_REMINDER=1

If you're running long-ctx tool flows (50K+ tokens with multiple tools active), these help with format compliance. They're already enabled in the default v7.14 compose.


When to deviate from defaults

Workload Compose Why
IDE agents (Cline / Cursor / Copilot) + long prompts single/tools-text.yml fp8 + 75K + no vision; PN8 closes Cliff 1
No spec-decode (debugging) single/minimal.yml 32K + fp8 + no MTP — simplest stack
Minimal (no Genesis, no spec-decode) single/minimal.yml 32K + fp8 — escape hatch if Genesis clone fails

See SINGLE_CARD.md and DUAL_CARD.md for full per-workload guidance.


When to switch engines

You don't need to switch unless:

  • You need lighter cold start / smaller footprint → llama.cpp
  • You need to run on AMD / Intel / Apple Silicon → llama.cpp
  • You're building a high-throughput multi-tenant service and want SGLang's RadixAttention scheduling → SGLang (currently blocked, see watch list)
  • You want max context (262K) on a single 3090 → llama.cpp + Q4_K_M + q4_0 KV. Honest trade-off: AutoRound (~19 GB) leaves only ~3-4 GB for KV at full ctx; a smaller GGUF leaves ~8-10 GB. You lose MTP spec-decode (~30% TPS) but gain ~5× the usable context.

For most local-LLM use cases, vLLM is the right pick on this model class. Other engines exist for legitimate reasons; this stack is just optimized for the vLLM path.