Qwen3.8-27B · EXL3 4.00bpw · exl3xpu

Qwen3.8-27B EXL3 4.00bpw on exl3xpu, 57.0 t/s warm decode, 1.43s TTFT.

57.0
WARM DECODE (T/S)
51.0
COLD DECODE (T/S)
1.43
WARM TTFT (S)
10.00
COLD TTFT (S)
30.7
PEAK VRAM (GB)
1
CONCURRENT REQS
llm-servingexl3exl3xpub70ASRock B70baseline

Based on: https://github.com/SergiioB/intel-arc-pro-b70-inference-cookbook/blob/master/docs/qwen38-27b/EXL3-XPU.md

Status: ready to launch, blockers resolved (b70-vllm stopped, server port fixed to 9932).

Config

Source: https://github.com/SergiioB/intel-arc-pro-b70-inference-cookbook/blob/master/docs/qwen38-27b/EXL3-XPU.md

Field Value
Model Qwen3.8-27B, turboderp/Qwen3.8-27B-exl3 @ rev 113cf7ab
Quant EXL3 trellis 4.00 bpw (~15.7 GiB), MTP3 tensors, fp8 KV
Engine exl3xpu, vLLM XPU fork (ExLlamaV3 trellis + native ESIMD kernels). Image pinned: ghcr.io/0xsero/exl3xpu@sha256:21412bdd7535e9c653eeb3d099dce3bc79a83d440def2cd9556111c79c870fa8 (vLLM 0.26.1.dev0+g568afb3a1 inside)
Context max_model_len=131072 (raised from recipe default 65536, too tight for our 64,346-token corpus)
GPU mem util 0.94, doc's capacity-probe value; safe on a clean card only. If startup OOMs, drop to 0.90
KV dtype fp8, the ONLY option (bf16 KV rejected at capture warmup)
Speculative decoding native MTP3 (num_speculative_tokens=3; method alias qwen3_5_mtp → deprecated mtp)
Batching max_num_batched_tokens=4096 (engine default, vLLM warns it's suboptimal for spec decoding), max_num_seqs=16, chunked prefill on
Prefill mode int8 W8A8 GEMMs (EXL3_INT8_PREFILL=1 default); fp16 = max quality variant
Prefix cache ON by default
Tooling --enable-auto-tool-choice --tool-call-parser qwen3_xml --reasoning-parser qwen3, served name Qwen3.8-27B
GPU card2/renderD129 (ASROCK), same card as the b70-vllm baseline (INTEL card commented out). Good for comparability, but they can't run concurrently
Port / host your-LAN-IP:9932 (published 9932:9932, server --port 9932)
Volumes model dir ./models/qwen3.8-27b-exl3-4.00bpw → /mnt/exl3model; HF/JIT/torch-compile cache shared with b70-vllm's cache volume
Power cap TBD, doc measures +33–42 % decode at 230 W vs 150 W; record the cap used

Launch blockers

Both resolved before launch: b70-vllm stopped (frees card2 + host port 9932), server port changed 9931 → 9932 to match the published mapping.

Alternate variant: SC_5.00bpw_H6 mixed-K (the doc's quality pick)

Test protocol

Standard: frozen corpus (prompt-corpus.txt, ~65k tokens) + "Summarize the main events of this text in exactly 10 bullet points.", temp=0, max_tokens=512. Cold (req #1) then warm (req #2). Client-side streaming timing; server logs + VRAM pasted after.

Stats

Metric Cold (req #1) Warm (APC hit)
Prompt tokens 64,388 64,388
TTFT (s) ~43.2 (derived from /metrics sums; includes ~40 s of one-time Triton JIT spikes, steady-state cold ≈ 10 s) 1.43
Prefill (tok/s) 6,438.6 (server window, steady-state rate) cache-served (hit rate 48.5%)
Decode (tok/s, generated) ~26–51 est. (512 tok over ~10–20 s decode phase; window-diluted 36.1→15.1; mid-decode JIT spikes on rejection/eagle kernels) 57.0 (904 completion tokens / 15.87 s, incl. MTP×3)
Completion tokens 512 (all thinking, max_tokens=512 exhausted before any answer) 904 (thinking + full answer; max_tokens=2048)
E2E (s) 52.8 (client) 17.30
VRAM (GB) 29.4 at start (user) 30.7 peak (user)
MTP acceptance mean length 2.63; per-pos 0.759/0.511/0.358; draft accept 54.3% mean length 2.54; per-pos 0.756/0.503/0.279; draft accept 51.3%

Extra data point: a manual warm test of the same corpus ran ~01:05:54 (TTFT ≈1.5 s, E2E ≈11.1 s, 512 completion tokens, derived from /metrics sums). /metrics totals include it.

Startup (from logs, 2026-10-17 00:51–00:55 UTC)

Findings

Recommendations

Quality variant

  1. SC_5.00bpw_H6 mixed-K weights, doc's quality pick: KL vs BF16 drops 0.42×/0.16× vs plain 4.00bpw at ~0.89× decode cost. A/B against plain is a natural follow-up run.
  2. fp16 prefill (--set env.EXL3_INT8_PREFILL=0 on the serve command. NOT docker run -e, the serve config's env: block overrides it), int8 prefill adds KL ≈ 70 % of the whole weight-quantization error; fp16 costs 1.28–1.63× TTFT (less at longer prompts).

Speed variant

  1. Raise max_num_batched_tokens above 4096, vLLM itself warns the 4096 default "may lead to suboptimal performance" with spec decoding enabled; it also caps prefill chunking.
  2. Power cap at 230 W, most power-limited engine measured in the doc: +33–42 % decode paired medians vs 150 W.
  3. Verify MTP is net-positive / tune depth, per-position acceptance decays to 0.36 at position 3; test without spec and at depth 2.
  4. Prefix cache stays ON, warm long prompts are nearly free (TTFT 1.43 s vs ~10 s steady-state cold).
  5. gmu 0.94 for single-client headless use if context headroom needed (not for concurrent users).

Measurement hygiene

Follow-up / next tests

Traps from source doc

Appendix: docker-compose (verbatim, as provided 2026-10-21)

services:
  exl3xpu:
    image: ghcr.io/0xsero/exl3xpu@sha256:21412bdd7535e9c653eeb3d099dce3bc79a83d440def2cd9556111c79c870fa8
    container_name: b70
    restart: unless-stopped
    ports:
      - "9932:9932"
    devices:
      - /dev/dri
    group_add:
      - "992"
    environment:
      ZE_FLAT_DEVICE_HIERARCHY: COMPOSITE
      HF_HOME: /root/.cache/huggingface
      HUGGINGFACE_HUB_CACHE: /root/.cache/huggingface/hub
    volumes:
      - ./models/qwen3.8-27b-exl3-4.00bpw:/mnt/exl3model:Z
      - ./cache:/root/.cache/huggingface:Z
    command:
      - models/qwen3.8-27b-exl3-4.00bpw
      - --gpu
      - "0"
      - --model-path
      - /mnt/exl3model
      - --set
      - vllm.gpu_memory_utilization=0.94
      - --set
      - vllm.max_model_len=131072
      - --enable-auto-tool-choice
      - --tool-call-parser
      - qwen3_xml
      - --port
      - "9932"
      - --reasoning-parser
      - qwen3
      - --served-model-name 
      - "Qwen3.8-27B"

Note: appendix reflects the config as launched (server port corrected 9931 → 9932).

RELATED
2026-10-16 · exl3xpu · 1x B70 · ctx 131072