Views
No views yet
mtp.* tensors), so speculative decoding runsmodel.visual.* tensors), so it stays multimodalqwen3_coder tool
calls all working. It runs under vLLM too, and there's a short section on that
below.nvidia-smi --query-gpu=compute_cap --format=csv| module group | dtypes | size |
|---|---|---|
| MLP | 192 INT8 + 192 BF16 | 15.94 GiB |
| linear_attn (48 layers) | 432 BF16 + 144 INT8 | 5.21 GiB |
| norms / misc | 130 BF16 | 2.37 GiB |
lm_head | BF16 | 2.37 GiB |
| full_attn (16 layers) | 96 BF16 + 64 INT8 | 1.56 GiB |
| vision tower | 333 BF16 | 0.86 GiB |
| MTP | 15 BF16 | 0.79 GiB |
linear_attn layers are only
partly quantized, since in_proj_a, in_proj_b and norm are excluded. That's
where most of that 5.21 GiB sits. lm_head and MTP are excluded too.48 value heads x 128 key dim x 128 value dim x 4 bytes (fp32) = 3 MiB per layer
3 MiB x 48 linear-attention layers = 144 MiB per sequence
at TP=2 = ~72 MiB per GPU per sequencemamba_full_memory_ratio (default 0.9), which
handed roughly half the cache arena to DeltaNet state on my box. Setting
--max-mamba-cache-size 8 freed most of it.--max-num-seqs defaults to 128, which is several GB per card gone
before a single KV token is allocated, on top of a ~15 GiB weight share. Set it
to 1 or 2.--gpu-memory-utilization.1python -m sglang.launch_server \
2 --model-path RukaRat/Qwen3.8-27B-INT8-W8A8-imatrix-MTP \
3 --tp 2 \
4 --speculative-algorithm NEXTN \
5 --speculative-num-steps 3 \
6 --speculative-eagle-topk 1 \
7 --speculative-num-draft-tokens 4 \
8 --context-length 245760 \
9 --mem-fraction-static 0.95 \
10 --max-running-requests 2 \
11 --max-mamba-cache-size 8 \
12 --mamba-ssm-dtype bfloat16 \
13 --kv-cache-dtype fp8_e4m3 \
14 --cuda-graph-max-bs 2 \
15 --tool-call-parser qwen3_coder \
16 --reasoning-parser qwen3 \
17 --default-chat-template-kwargs '{"preserve_thinking": true, "reasoning_effort": "low"}'--max-mamba-cache-size 8 with --kv-cache-dtype fp8_e4m3 — by far the
biggest win, and it's the per-sequence state described above. Speculative
decoding consumes roughly 4 state slots per request, so 8 is the floor. Fewer
slots also shrink the mamba radix prefix cache, which is what gives you warm
starts on multi-turn sessions, so don't go below it.--mem-fraction-static 0.95 — I stress-tested vision at this setting (1024px
image at deep context) and it held, with well under a gigabyte free. Don't go
higher.--mamba-ssm-dtype bfloat16 — worth a useful chunk of context. Needle recall
stayed perfect at 105k, 215k and 240k tokens, and generation stayed coherent.
Those are light probes rather than a parity proof, so if you see long-session
weirdness, revert this one first.--speculative-num-draft-tokens 3 gained nothing,
and YaRN is moot here, since the pool sits below the model's native 262,144
window. The cap is memory-bound, not rope-bound.1vllm serve RukaRat/Qwen3.8-27B-INT8-W8A8-imatrix-MTP \
2 --tensor-parallel-size 2 \
3 --trust-remote-code \
4 --max-num-seqs 1 \
5 --disable-custom-all-reduce \
6 --gpu-memory-utilization 0.92 \
7 --hf-overrides '{"text_config":{"rope_parameters":{"rope_type":"yarn","factor":1.5,"original_max_position_embeddings":262144,"mrope_interleaved":true,"mrope_section":[11,11,10],"partial_rotary_factor":0.25,"rope_theta":10000000}}}' \
8 --max-model-len 311296 \
9 --kv-cache-dtype fp8_e4m3 \
10 --enable-prefix-caching \
11 --enable-chunked-prefill \
12 --max-num-batched-tokens 4096 \
13 --speculative-config '{"method":"mtp","num_speculative_tokens":3}' \
14 --limit-mm-per-prompt '{"image":4,"video":0}' \
15 --mm-processor-kwargs '{"max_pixels":2000000,"min_pixels":65536}' \
16 --enable-auto-tool-choice \
17 --tool-call-parser qwen3_coder \
18 --reasoning-parser qwen3 \
19 --default-chat-template-kwargs '{"preserve_thinking": true, "reasoning_effort": "low"}'--max-num-seqs 1. See the per-sequence state section above. This is the
flag that decides whether the server starts at all on 2x24 GB.--max-model-len exceeds max_position_embeddings. Keep
mrope_section, partial_rotary_factor and rope_theta in there or the model
won't load. Extending this way also seemed to help deep-context behaviour
rather than hurt it: at 1.5x, position 250k sits where roughly 167k would land
natively, comfortably inside the trained range.--disable-custom-all-reduce on PCIe. vLLM's custom all-reduce kernels
assume NVLink.--max-model-len to 311,296 to leave room, since MTP spends cache on the drafter
too. Read the figure off your own startup output rather than trusting mine, and
set --max-model-len just under it. --max-num-batched-tokens 4096 beat 8192 and
16384 for me, both of which cost KV cache without buying prefill back — vLLM's own
startup warning telling you to raise it is wrong on this hardware, at least when
serving one request at a time.Mismatch in `image` token count between text and `input_ids`--mm-processor-kwargs '{"max_pixels":2000000,"min_pixels":65536}' caps
it, and setting it also gives you back a chunk of KV cache, since vLLM otherwise
reserves room for a worst-case image.xhigh, the maximum. I default to low instead and
raise it per request when a task calls for it. On a self-grading build task —
the model writes a unittest suite and the harness runs it — low came out close
to xhigh on tests passed while taking a fraction of the time and a small
fraction of the reasoning tokens.medium is not a middle setting. The template injects no instruction for it,
whereas low explicitly says "move directly to the conclusion". So low gets a
clear directive and follows it, while medium drifts, and it scored worst of
the three for me.xhigh can talk itself out of the right answer. On that build task it rejected
the stdlib zoneinfo module every time and hand-rolled a datetime.tzinfo
subclass that then crashed; low and medium imported zoneinfo every time.
The xhigh instruction says to "consider plausible alternatives", which on a
problem the stdlib already solves is a liability. I also tried it as an agentic
default and reverted the same day — the think-to-output ratio made sessions
unworkable.max_tokens. This model thinks at length, and if it
runs out mid-thought you get reasoning back with empty content.preserve_thinking: true keeps reasoning always, which makes prompts append-only
and measurably improved cache hit rate, re-prefill and wall-clock time on a
multi-turn tool-calling workload. It costs context, since retained reasoning is
carried forward, so if you hit the ceiling on long sessions, drop this first.chat_template.jinja, so you shouldn't need to
pass a template at all. Don't substitute the custom Qwen3.6 one if you have it
lying around.temperature 0.6 · top_k 20 · top_p 0.86 · repetition_penalty 1.07
presence_penalty 0.0 · frequency_penalty 0.0presence_penalty 1.5) was the worst setting I tested — it repeated more, and it
suppressed natural stopping so replies ran to the token cap.ntok += 1 per chunk undercounts
speculative decoding by roughly the acceptance factor, and will tell you MTP made
things slower when it didn't. Every "MTP is slower on my machine" result I had
turned out to be this. Count tokens server-side.format compressed-tensors, W8A8
weights 8-bit int · symmetric · per-channel · static · observer: imatrix-mse
activations 8-bit int · symmetric · per-token · dynamic
targets Linear
ignore all model.visual.* blocks · linear_attn in_proj_a / in_proj_b / norm
· lm_head · re:.*mtp.*imatrix-mse weights the quantization error by how much each activation actually
matters, rather than minimizing average weight error uniformly. It needs
calibration data and pipeline="sequential". If either is missing, llmcompressor
falls back to a data-free pipeline, logs imatrix_mse: no importance data available. Falling back to uniform MSE, and finishes normally while producing
something different from what you asked for. Worth checking the log for that line.imatrix-mse and minmax came out about even at the top, W8A16 and
SmoothQuant behind them, and plain mse last — it clips the activation outliers
the drafter depends on. So: don't "fix" this build to plain mse.AutoModelForImageTextToText. With AutoModelForCausalLM you get
the language model on its own and no vision tower, and there's no error to
tell you.mtp.* tensors back from
the base checkpoint afterwards. They stay BF16 with no scales, which is
expected.re:.*mtp.* to quantization_config.ignore. Without it vLLM reads those
BF16 tensors as INT8 and speculative decoding sits at 0% acceptance without
complaining, or throws KeyError: 'weight_scale'. After fixing it, clear
~/.cache/vllm/torch_compile_cache, otherwise vLLM reuses the graph it
compiled under the broken config and the fix looks like it did nothing. That
one cost me an hour.| SGLang | vLLM | |
|---|---|---|
| engine | 0.5.18, sglang-kernel 0.4.6.post1 | 0.22.0 |
| torch | 2.13.0+cu130 | 2.11.0+cu130 |
| flashinfer | 0.6.17 | 0.6.11.post2 |
| transformers | 5.12.1 | 5.9.0 |
systemd-oomd to kill the load. Running the server in its own scope with
ManagedOOMPreference=omit fixed that for me. The kernel OOM killer still
applies, which is the point.lm_head, MTP and a good chunk of
linear_attn are still BF16.