← Back to posts

Qwen3.8-27B FP8 on Two Intel Arc Pro B70s: TP2, Graphs, and Context

A structured n=3 record of the official Qwen3.8-27B FP8 route on dual Arc Pro B70s, including TP2 speed, PP2 validation, XPU Graph results, context scaling, RAM findings, and failed concurrency tests.

Case Snapshot

Situation

The official Qwen3.8-27B FP8 artifact is about 28.75 GiB, so a useful serving route needs both 32 GiB Arc Pro B70 cards.

Issue:

The dual-GPU process exposed a Level Zero host-memory tax, MTP4 full graph capture deadlocked, and concurrent prefill plus speculative decode still crashes the GDN causal-convolution path.

Solution:

Use TP2 with one Level Zero mask per worker, Docker SYS_PTRACE for oneCCL IPC, MTP4 with XPU Graph disabled, and keep PP2 as a separate load and coherence validation route.

Used In:

Portfolio case study and the Qwen3.8-27B serving record in the public B70 research repository.

Impact:

Fresh TP2 C1 output throughput measured 44.71 tok/s at p512, 35.59 at p2048, and 16.46 at p8192. A valid p240K probe completed at 0.179 tok/s; Cn throughput remains excluded.

Evidence status: self-reported n=3 confirmation. The fresh speed cells below use three measured repetitions. Cn produced no valid aggregate throughput result. The page keeps those limits on the record.

The official Qwen/Qwen3.8-27B-FP8 artifact now loads and serves across two Intel Arc Pro B70s on a 32 GiB host. The speed route is TP2/PP1, MTP4, FP8 KV cache, and compile-only execution with XPU Graph disabled.

The first result to remember is 44.71 tok/s end-to-end at p512/g128. This is request output throughput, so it includes time to first token. The approximate post-first rate is about 60.5 tok/s. At p8192, end-to-end output throughput falls to 16.46 tok/s because prompt processing dominates the request; the post-first diagnostic stays near 60.6 tok/s.

This page uses the same numeric source as the companion figures and the Qwen3.8 cookbook section. The raw campaign roots are in SergioB/B70-DOCS.

Result at a glance

ItemRecorded value
ModelQwen/Qwen3.8-27B-FP8
Artifact30,890,081,596 bytes · about 28.75 GiB
Hardware2 × Intel Arc Pro B70 · 32,656 MiB visible VRAM each
Host32 GiB RAM · xe driver
Speed topologyTP2/PP1 · two cards in one server
Main routeMTP4 · compile-only · XPU Graph off
KV cacheFP8
Research cap230 W configured; draw was not measured in these cells
Declared full context262,144 tokens
Reported full-context KV capacity603,082 tokens · 2.30× at 262,144-token requests
Concurrency resultExcluded; no valid Cn aggregate rate

The official FP8 artifact does not leave a useful single-card serving envelope. There is no FP8 TP1 number in this record.

TP2 speed route

TP2 means tensor parallelism: the model’s layers are split across both cards and each request uses both cards. PP1 means no pipeline split. This is the route used for the fresh FP8 speed cells.

The pinned image was:

vllm/vllm-openai-xpu@sha256:f01e24f6c7ff01f1e0662234255a1372297d1dbd89d003cf13c8fad3eab1ba4f

Core settings:

--tensor-parallel-size 2 --pipeline-parallel-size 1
--quantization fp8 --dtype bfloat16 --kv-cache-dtype fp8
--speculative-config '{"method":"mtp","num_speculative_tokens":4}'
--max-model-len 9216 --max-num-seqs 8 --max-num-batched-tokens 4096
--gpu-memory-utilization 0.90 --no-enable-prefix-caching --language-model-only
VLLM_XPU_ENABLE_XPU_GRAPH=0

The launch also needs two local settings:

  1. A spawn-time patch assigns one ZE_AFFINITY_MASK to each worker before Level Zero initializes.
  2. Docker gets --cap-add SYS_PTRACE so oneCCL can use its pidfd IPC path under the default seccomp profile.

These are local launch changes. They are not upstream fixes.

Fresh TP2 C1 measurements

Each cell used exact endpoint input lengths, one same-shape warmup, unique prompt entropy, prefix caching off, and three measured requests. Output throughput is successful generated tokens divided by the request interval. Approx. post-first is 1000 / mean TPOT; it is a decode diagnostic and must not be mixed with the older custom lane statistic.

Input / outputOutput throughputMean TTFTMean TPOTApprox. post-firstMean ITLMean E2ELMTP acceptanceResult
p512 / g12844.71 tok/s762.38 ms16.539 ms60.46 tok/s75.920 ms2,862.83 ms82.14% · 4.29 tokens3/3
p2048 / g12835.59 tok/s1,489.11 ms16.595 ms60.26 tok/s77.106 ms3,596.68 ms93.29% · 4.73 tokens3/3
p8192 / g12816.46 tok/s5,681.05 ms16.489 ms60.65 tok/s78.530 ms7,775.19 ms96.56% · 4.86 tokens3/3

Qwen3.8-27B FP8 TP2 prompt-length screen

Open the prompt-length SVG. The blue bars include first-token delay. The teal bars show the approximate post-first diagnostic.

The decoder is nearly flat at about 16.5 ms per generated token in the FP8 C1 cells. The visible rate drop comes from the prompt side: mean TTFT grows from 0.76 s at p512 to 5.68 s at p8192.

Full-context check

The model declares a 262,144-token context. A full-context server reported 603,082 KV tokens and 2.30× maximum concurrency for requests at the declared maximum length. That is a capacity report, not a completed multi-user throughput test.

The valid near-maximum request was p240000/g128, n=1:

CellOutput throughputMean TTFTMean TPOTAcceptanceResult
p240000 / g1280.179 tok/s711.06 s25.681 ms80.42% · 4.22 tokens1/1

Qwen3.8-27B FP8 TP2 context scaling

Open the context-scaling SVG. The x and y axes are logarithmic so the p240K point remains visible beside the shorter C1 cells.

The exact p262016 attempt is excluded. The client tokenizer produced 282,403 tokens, above the 262,144-token server limit. The p240K result is therefore the valid near-maximum probe in this record.

Why the speed route keeps XPU Graph off

“Compile-only” here means vLLM compilation remains enabled while XPU Graph capture is disabled with VLLM_XPU_ENABLE_XPU_GRAPH=0.

MTP1 and MTP2 full graph capture work on this TP2 setup. The fresh MTP2 endpoint cells and the historical MTP4 custom-lane cells use different statistics, so they are not a clean same-metric speed A/B. MTP4 full graph capture failed in earlier runs. cudagraph_mode=PIECEWISE made one MTP4 request work, but it was only a functional probe.

The statistics are kept separate because the fresh MTP2 values are n=3 endpoint output rates, while the MTP4 rows are historical custom post-first lane values.

ModeGraph settingRecorded cellRateEvidence level
MTP2Full graphp512/g128, n=334.39 tok/s e2e · 39.41 approx. post-firstFresh confirmation
MTP2Full graphp8192/g128, n=311.30 tok/s e2e · 32.06 approx. post-firstFresh confirmation
MTP4Compile-onlyp512/g128, n=546.97 tok/sHistorical custom lane
MTP4PIECEWISEp512/g128, n=144.98 tok/sFunctional one-request probe
MTP4Full graphTP2 attemptNo rateDeadlock; excluded
MTP1Full graphp512/g128, n=528.36 tok/sHistorical custom lane

Qwen3.8-27B FP8 XPU Graph screen

Open the graph-screen SVG. The current recommendation keeps graph capture off for MTP4 because full graph failed and PIECEWISE was only a one-request functional probe. It does not claim that graphs never help on XPU.

The practical decision is straightforward: MTP4 compile-only is the current speed route; MTP2 full graph is a working fallback; MTP4 PIECEWISE is a functional test mode; MTP4 full graph is not usable on this build.

TP2 and PP2 are separate topologies

Both routes use two B70s, but they split work differently and should not share one result table.

TP2: speed route

TP2 shards the model across both cards during each forward pass. It is the only official FP8 route in this record with fresh C1 speed data. It still does not give a 2× C1 result: each token pays cross-card synchronization and the request has one shared latency path.

PP2: load and validation route

PP2 means pipeline parallelism: --tensor-parallel-size 1 --pipeline-parallel-size 2. It proved that the official FP8 artifact can load and produce output across both cards, but it is much slower in the historical validation lane. These rows use n=5 custom-lane measurements and are not comparable to the fresh TP2 table.

PP2 modeTP / PPC1 decodeCold inputStatus
Eager1 / 28.05 tok/s1,077 tok/sValidation only
Compile1 / 210.9 tok/s639 tok/sValidation only; graph capture had no speed effect

PP2 is useful when the question is “can the artifact be staged across both cards?” It is not the selected FP8 speed path. The PP2 results used the same per-worker affinity and SYS_PTRACE launch settings, with a serial materialization overlay used only for the validation load path.

INT4 comparison on the same model family

The current INT4 route is a separate artifact and a separate kernel path. It is included to explain why the smaller representation can be faster even when both tests use two cards.

Current TP2 comparison

These are matched n=3 C1 endpoint cells at a configured 230 W cap. FP8 uses the official artifact, MTP4, compile-only, and FP8 KV. INT4 uses the current no-g_idx artifact, MTP4, full graph, and FP8 KV.

InputFP8 TP2 e2e outputINT4 TP2 e2e outputCurrent INT4 result
p512 / g12844.71 tok/s84.00 tok/s3/3
p2048 / g12835.59 tok/s38.35 tok/s3/3
p8192 / g12816.46 tok/s24.52 tok/s3/3

The current TP2 data shows INT4 ahead in all three end-to-end cells. The likely contributors are lower weight traffic and a more mature INT4 kernel path on this build. That explanation is an inference from the measurements; no profiler breakdown was captured here.

Current single-card INT4 control

The current INT4 control used one card at a configured 150 W cap. It is directionally useful, not an apples-to-apples scaling measurement against the 230 W TP2 rows.

InputINT4 TP1 e2e outputApprox. post-firstResult
p512 / g12849.08 tok/s60.54 tok/s3/3
p8192 / g12812.51 tok/s65.33 tok/s3/3

The older 230 W INT4 figures, 83.7 tok/s at p512 and 77.1 tok/s at p8192, used an older g_idx artifact and the historical custom post-first statistic. They remain orientation data only and are not blended into the current table.

Host RAM was a separate dual-GPU blocker

The model fit the combined VRAM budget, but the host had only 32 GiB of RAM. A Level Zero process that could see both GPUs consumed host memory at nearly the same scale as its device allocation.

Process viewDevice allocation in probeHost overhead in probeReading
One process sees both GPUs11.0 GiB10.97 GiBProblem case
One process sees one GPU5.6 GiB0.77 GiBSingle-device control
Two workers, one GPU mask each10.5 GiB0.82 GiBWorking dual-worker shape

Dual-B70 host-memory probe and workaround

Open the host-memory SVG. These are probe-level resident-memory deltas, not total system-memory readings.

The findings match two open upstream reports:

The working local launch shape is one device mask per spawned worker plus Docker SYS_PTRACE. CCL_ZE_IPC_EXCHANGE=none was ignored in the tested environment. No upstream fix was verified in this run.

INT4 virtual-address failure and rejected patch

Several INT4 attempts on this boot reached a separate failure in mamba_utils: the GDN state pool landed at device addresses at or above 2^63, while this vLLM build stored pointers in signed int64 tensors.

The two’s-complement wrap patch stopped the Python overflow exception, but the kernels then read wrapped addresses and produced garbage tokens. That patch is rejected. The real fix is to make the allocation land below 2^63, not to reinterpret an invalid pointer.

The current no-g_idx INT4 TP2 n=3 rows above completed the throughput cells. The rejected wrap remains recorded because it explains why a seemingly harmless overflow fix cannot be used as a serving recipe.

Concurrency result: capacity was measured, throughput was not

At max_model_len=9216, the speed server reported 132,096 KV tokens and 14.33× maximum concurrency at the full request length. A C14 wave was sized from that report.

AttemptResultReason
C14 MTP4Excludedcausal_conv1d rejected speculative-decode and non-spec tokens in the same invocation
C19 MTP4Excluded1 success and 56 failures with the same mixed prefill/decode failure
C14 no-spec compileExcluded before healthHost MemAvailable reached 1,029 MiB; the watchdog floor was 3,072 MiB
C14 no-spec eagerExcluded before healthHost MemAvailable reached 1,008 MiB; the watchdog floor was 3,072 MiB

No Cn aggregate throughput number is published for this build. C1 output throughput and Cn aggregate throughput are different measurements. The next valid concurrency run needs both a fixed GDN mixed prefill/decode path and a host-memory load that stays above the watchdog floor.

Patch and configuration ledger

ItemStateScopeReason
Per-worker Level Zero affinity patchRequired local workaroundTP2 and PP2Sets one mask per worker before device initialization
Docker SYS_PTRACERequired launch settingTP2 and PP2Keeps oneCCL pidfd IPC on its working path under seccomp
cudagraph_mode=PIECEWISEFunctional graph workaroundMTP4 graph testAvoids the first-generation full-graph failure; slower than compile-only
VLLM_XPU_ENABLE_XPU_GRAPH=0Current speed settingTP2 MTP4Fastest functional MTP4 route measured here
Two’s-complement pointer wrapRejectedINT4Prevented the exception but produced invalid output

The local worker patch and launch wrapper are kept in the B70 research repository’s scripts/tmp/ area until the exact source generation is promoted. The cookbook records their state as local-only workarounds.

Reproduction and evidence

The numeric source for this page is src/data/qwen38-b70-fp8.json. It was compiled from the per-cell JSON files and manifests in the following B70-DOCS evidence roots:

EvidencePurpose
results/qw38-fp8-tp2-mtp4-230w-compile-confirm-20260828T130000Z/Fresh FP8 TP2 C1 n=3
results/qw38-fp8-tp2-mtp4-230w-fullctx-20260828T130957Z/Full-context server and valid p240K probe
results/qw38-fp8-tp2-mtp2-graph-confirm-20260828T114155Z/Fresh MTP2 full-graph confirmation
results/qw38-int4-tp2-mtp4-230w-graph-confirm-20260828T120423Z/Current INT4 TP2 comparator
results/qw38-int4-sc-mtp4-champ-20260828T112032Z/Current INT4 single-card control
research/qw38-fp8-singlecard-unblock-and-pp2-rootcause-20260827.mdRAM and affinity root cause

Protocol choices used for the fresh cells:

  • one same-shape warmup was discarded;
  • actual endpoint input tokens were retained;
  • prompt entropy was placed at the start of each cold request;
  • prefix caching was disabled;
  • output throughput used the full request interval;
  • post-first rate stayed in a separate diagnostic column;
  • failed and excluded attempts stayed out of throughput tables.

The figures are generated from the same JSON source:

These results are published as self-reported n=3 confirmations. The tables keep n=1, historical, and excluded evidence labels visible; they do not claim Cn throughput or performance beyond the listed cells.