Date: 2026-06-16
Get nameistoken/Qwen3.6-35B-A3B-Quark-W8A8-INT8 on 4x Intel Arc Pro B70
well above 100 tok/s single-request decode with no quality loss.
Quality loss is not acceptable. Any promoted change must be token/canary/quality validated against the current safe fast identity before being treated as a win.
Do not compare runs unless the full run identity matches.
Critical identity fields:
COMPILATION_CONFIGGPU_MEMORY_UTILIZATIONXPU_GRAPH, VLLM_XPU_ENABLE_XPU_GRAPHVLLM_XPU_FORCE_GRAPH_WITH_COMM, VLLM_XPU_GRAPH_NOOP_COMM_CAPTUREVLLM_XPU_GDN_NATIVE_FALLBACKVLLM_EXTRA_ARGSPrevious mistake to avoid: a run missing
COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}' defaulted to graph-none
and measured around 15 tok/s, which was incorrectly compared against the fast
PIECEWISE forced-comm lane.
Known fast-but-unsafe historical baseline: PIECEWISE forced-comm graph lane at
about 93.45 tok/s.
Current validated safe research base:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-mixed-workspace-async-deep-gate-summary-20260615a13deep2.json93.55054235558917 tok/s10.689885 ms/token187.3366 ms128/128, color 256/256, quality suite,
baseline match, long-context pass.Model:
/mnt/fast-ai/llm-cache/hf/models--nameistoken--Qwen3.6-35B-A3B-Quark-W8A8-INT8/snapshots/cced56592e8c8935f8220836b4baa04dfd389118
Core settings:
COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}'
XPU_GRAPH=1
VLLM_XPU_ENABLE_XPU_GRAPH=1
VLLM_XPU_FORCE_GRAPH_WITH_COMM=1
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=1
VLLM_XPU_GDN_NATIVE_FALLBACK=prefill
VLLM_XPU_GDN_PREFILL_RECURRENT_FALLBACK=1
VLLM_XPU_DISABLE_PREFILL_CUDAGRAPH_REPLAY=1
VLLM_XPU_GREEDY_SAMPLE_TOPK_FALLBACK=1
VLLM_XPU_INT8_MOE_MIXED_WORKSPACE=1
GPU_MEMORY_UTILIZATION=0.90
VLLM_EXTRA_ARGS='--uvicorn-log-level warning'
2026-06-19 update: the latest decisive no-async TP2 PIECEWISE trace changed the immediate priority. MoE is still the largest model-forward family, but the accepted no-async decode path is also paying a large sampled-token materialization cost after sampling.
Diagnostic identity:
TP_SIZE=2
ONEAPI_DEVICE_SELECTOR=level_zero:0,1
COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}'
XPU_GRAPH=1
VLLM_XPU_ENABLE_XPU_GRAPH=1
VLLM_XPU_FORCE_GRAPH_WITH_COMM=1
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=1
VLLM_XPU_GDN_NATIVE_FALLBACK=prefill
VLLM_EXTRA_ARGS='--no-async-scheduling --uvicorn-log-level warning'
New diagnostic artifacts:
/home/steve/llm-optimizations/data/qwen36-stage-boundary-noasync-tp2-20260619stage1.log/home/steve/llm-optimizations/data/qwen36-stage-boundary-noasync-tp2-p512o128-20260619stage1.json/home/steve/llm-optimizations/data/qwen36-stage-boundary-noasync-tp2-timing-summary-20260619stage1.json/home/steve/llm-optimizations/data/qwen36-sync-to-list-noasync-tp2-p512o128-20260619tolist1.json/home/steve/llm-optimizations/data/qwen36-sync-to-list-nosync-noasync-tp2-p512o128-20260619tolist0.jsonKey finding:
83.80 tok/s.forward_end around 5.25 ms.bookkeeping_sync is around 4.67 ms.gpu_model_runner.bookkeeping_to_list, around
4.62 ms, where sampled token IDs are copied/materialized to CPU._to_list variants did not remove the wall:
VLLM_XPU_SYNC_TO_LIST=1, pre-copy sync on: 83.58 tok/s.VLLM_XPU_SYNC_TO_LIST=1, pre-copy sync off: 84.55 tok/s.Implication:
The next high-upside exact path is output materialization overlap/removal,
especially repairing async scheduling correctness or building a narrower
deferred sampled-token output path. Exact MoE/shared-expert work remains
important, but it cannot by itself reach the target while _to_list costs
about 4.6 ms/token in the safe no-async path.
Async isolation results:
async-fast-outputids-tp2, PIECEWISE, async, fast output-id repair:
JSON failed at repeat 21, color passed.async-refresh-prev-sampled-tp2, PIECEWISE, async, refreshed
prev_sampled_token_ids tensor reference each step:
JSON failed at repeat 21, color passed.async-gdn-decode-prefill-tp2, PIECEWISE, async, native GDN for decode and
prefill:
JSON failed at repeat 31, color passed.async-sync-after-zero-tp2, PIECEWISE, async, sync after fresh block zero:
JSON failed at repeat 21, color passed.async-sync-output-copy-tp2, PIECEWISE, async, synchronous output copy:
JSON failed at repeat 21, color passed.async-sync-before-return-tp2, PIECEWISE, async, full XPU sync before
returning async output:
JSON failed at repeat 21, color passed.async-graphnone-tp2, graph-none, async:
JSON passed 40/40, color passed 8/8.Follow-up async/PIECEWISE replay isolation:
async-clear-on-prefill-tp2, async, PIECEWISE, clear captured graphs on
every new prefill:
rejected. It crashed before a valid canary response with
beginAllocateToPool: already recording to mempool_id.async-initial4-eager-tp2, async, PIECEWISE, first four decode forwards
eager for each fresh request:
rejected. JSON failed at repeat 60 with
{"answer":"42 widgets","unit":"widgets"}; color passed 16/16.async-clone-sampled-ids-tp2, async, PIECEWISE, clone sampled token IDs
before async reuse:
rejected. JSON failed at repeat 22; color passed.async-sync-replay-tp2, async, PIECEWISE, synchronize around every graph
replay:
rejected. JSON failed at repeat 22; color passed.async-real-comm-capture-tp2, async, PIECEWISE, forced comm graph with
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=0:
rejected. Workers died during startup before readiness.async-eager-every8req-tp2, async, PIECEWISE, all decode replay eager for
every eighth fresh request:
accepted as a correctness workaround. JSON passed 96/96; color passed
16/16.async-eager-every8req-tp2-metrics, same identity as the passing N=8
canary:
corrected decode 83.35363772782296 tok/s, decode
12.00305433894755 ms/token.async-eager-every16req-tp2, async, PIECEWISE, all decode replay eager for
every sixteenth fresh request:
rejected. JSON passed 96/96, but color failed at repeat 12.Interpretation:
{"answer, so output serialization and simple
sampled-token CPU repair are not the root cause.N=8 is the only passing
async/PIECEWISE workaround found so far, but it does not improve the
single-request decode lane versus the safe no-async baseline. It is useful
as a correctness control and reliability fallback, not as the >150 tok/s
path.N=8 available only if a later endpoint patch needs a
quality-safe async control.Older context: before this trace, the work was focused on the fused W8A8 MoE prologue path, because earlier family timing showed MoE/shared-expert work dominating the single-request decode budget.
Important timing artifact:
/home/steve/llm-optimizations/data/qwen36-shared-expert-internals-timing-timing-decision-20260615a20.jsonTiming conclusion from that artifact:
forward_total roughly 5.53 msmodel_forward roughly 5.48 msmoe.quant_method_total roughly 4.75 msmoe.shared_experts.apply_no_overlap roughly 2.74 msmoe.apply roughly 1.60 msxpu_moe.fused_moe_call roughly 1.02 msSo the current high-upside branch is still MoE/shared-expert structural work, not TP topology or sampler-only work.
I added a layer skip/include gate for fused prologue capture so we can bisect which layers poison PIECEWISE graph capture.
Code changed:
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/fused_moe_interface.py/home/steve/src/vllm/vllm/model_executor/layers/quantization/quark/quark_moe.pyNew env vars:
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_SKIP_LAYERS
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_LAYER_REGEX
Syntax checks passed before the latest endpoint tests:
/home/steve/.venvs/vllm-xpu/bin/python -m py_compile /home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/fused_moe_interface.py
/home/steve/.venvs/vllm-xpu/bin/python -m py_compile /home/steve/src/vllm/vllm/model_executor/layers/quantization/quark/quark_moe.py
Latest endpoint readiness tests:
skip0
prefill-safe-int8-prologue-skip0-c1-ready/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-prologue-skip0-c1-ready-20260616prolskip0ready1.logUR_RESULT_ERROR_DEVICE_LOST/home/steve/src/vllm/vllm/v1/worker/gpu_model_runner.py:9456
copying logit_indices_device = torch.from_numpy(logit_indices).to(...)
during c1 PIECEWISE graph capture.skip0-3
prefill-safe-int8-prologue-skip0-3-c1-ready/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-prologue-skip0-3-c1-ready-20260616prolskip03ready1.logUR_RESULT_ERROR_DEVICE_LOST
at the same dummy logit_indices copy during c1 graph capture.xpu-smi ps showed no vLLM workers left.skip0-39
prefill-safe-int8-prologue-skipall-c1-ready/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-prologue-skipall-c1-ready-summary-20260616prolskipallready1.json/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-prologue-skipall-c1-ready-20260616prolskipallready1.logskip0-19
prefill-safe-int8-prologue-skip0-19-c1-ready/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-prologue-skip0-19-c1-ready-20260616prolskip019ready1.logUR_RESULT_ERROR_DEVICE_LOST, then the API server
timed out waiting for engine core startup.xpu-smi ps after failure showed only xpu-smi, no vLLM workers.Interpretation:
Main reproducer:
/home/steve/llm-optimizations/scripts/repro-qwen36-prologue-tp4-capture.pyRecently added features:
--endpoint-context--compile-wrapper--workspace-manager--custom-op-wrapperWorkspaceManagerdirect_register_custom_opPassing reduced artifacts:
/home/steve/llm-optimizations/data/qwen36-prologue-endpoint-context-workspace-r1-20260616b.json/home/steve/llm-optimizations/data/qwen36-prologue-endpoint-context-workspace-tp4-l1-20260616a.json/home/steve/llm-optimizations/data/qwen36-prologue-endpoint-context-workspace-tp4-l40-prewarm8192-20260616a.json/home/steve/llm-optimizations/data/qwen36-prologue-endpoint-context-workspace-compile-tp4-l1-20260616a.json/home/steve/llm-optimizations/data/qwen36-prologue-endpoint-context-workspace-compile-tp4-l40-prewarm8192-20260616a.json/home/steve/llm-optimizations/data/qwen36-prologue-customop-workspace-r1-20260616b.json/home/steve/llm-optimizations/data/qwen36-prologue-customop-workspace-tp4-l1-20260616a.json/home/steve/llm-optimizations/data/qwen36-prologue-customop-workspace-tp4-l40-prewarm8192-20260616a.json/home/steve/llm-optimizations/data/qwen36-prologue-customop-workspace-compile-tp4-l40-prewarm8192-20260616a.jsonMeaning:
torch.compile.Optimization notes and plans:
/home/steve/llm-optimizations/notes/2026-06-14-qwen36-recovery-implementation.md/home/steve/suggestions.md/home/steve/AGENTS.md/home/steve/llm-optimizations/AGENTS.mdRunners and launchers:
/home/steve/llm-optimizations/scripts/run-qwen36-ablation-candidate.sh/home/steve/llm-optimizations/scripts/launch-qwen36-quark-int8-accepted.sh/home/steve/llm-optimizations/scripts/run-qwen36-decisive-timing.sh/home/steve/llm-optimizations/scripts/qwen36-timing-family-decision.py/home/steve/llm-optimizations/scripts/qwen36-ablation-report.py/home/steve/llm-optimizations/scripts/run-qwen36-oracle-parity-gate.shCurrent reproducer:
/home/steve/llm-optimizations/scripts/repro-qwen36-prologue-tp4-capture.pyvLLM Python code:
/home/steve/src/vllm/vllm/model_executor/layers/quantization/quark/quark_moe.py/home/steve/src/vllm/vllm/model_executor/layers/fused_moe/experts/xpu_moe.py/home/steve/src/vllm/vllm/v1/worker/gpu_model_runner.py/home/steve/src/vllm/vllm/compilation/cuda_graph.pyXPU kernel bridge and native code:
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/fused_moe_interface.py/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/ops.h/home/steve/src/vllm-xpu-kernels/csrc/xpu/torch_bindings.cpp/home/steve/src/vllm-xpu-kernels/CMakeLists.txtImportant patch directory:
/home/steve/llm-optimizations/patches/Immediate next steps:
skip0-19 failed and skip0-39 passed, next likely tests are
ranges that keep progressively fewer high layers enabled:
skip0-29, skip0-34, skip0-37, then single-layer enables if needed.VLLM_XPU_MOE_LIVE_ABI_FILE=/home/steve/llm-optimizations/data/...jsonl
and VLLM_XPU_MOE_LIVE_ABI_MAX_LINES=2000.The current credible path to >150 tok/s single-session decode without quality
loss is still trained EAGLE speculation. Other measured routes either failed
quality gates or lost too much speed. The verifier/spec-state work has moved:
VLLM_XPU_SPEC_DECODE_DRAFT_ONLY=1, serial-packed GDN, scheduler tail
fallback, and accepted running conv/SSM state promotion./home/steve/llm-optimizations/data/qwen36-oracle-k8-draftonly-serialgdnpacked-promoteconv-tailfallback-currentbaseline-eager-tp2-20260618cb-candidate.json.0.6-3 tok/s and only the first group accepted 5/8 draft tokens.
Do not promote that route.New EAGLE data export support was added in
/home/steve/src/vllm/vllm/v1/worker/gpu_model_runner.py behind opt-in envs:
VLLM_XPU_EAGLE_DATA_DUMP_DIR=<dir>
VLLM_XPU_EAGLE_DATA_DUMP_RANK=0
VLLM_XPU_EAGLE_DATA_DUMP_MAX_STEPS=<n>
VLLM_XPU_EAGLE_DATA_DUMP_DTYPE=bfloat16
VLLM_XPU_EAGLE_DATA_DUMP_SINGLE_TOKEN_ONLY=1
Normal inference is unchanged when these envs are unset. Dump shards are stitched by:
/home/steve/llm-optimizations/scripts/build-qwen36-eagle-dataset-from-dump.py
Smoke results:
/home/steve/llm-optimizations/data/qwen36-eagle-hidden-dump-smoke-20260618d.log.0,1 succeeded. Dump dir:
/home/steve/llm-optimizations/data/qwen36-eagle-hidden-dump-smoke-tp2-20260618e.cced56592e8c8935f8220836b4baa04dfd389118, TP2, 32k max model length,
COMPILATION_CONFIG='{"cudagraph_mode":"NONE"}', no async, MAX_NUM_SEQS=2,
MAX_NUM_BATCHED_TOKENS=1024, GPU_MEMORY_UTILIZATION=0.82./home/steve/llm-optimizations/data/qwen36-eagle-hidden-dataset-smoke-tp2-20260618e-summary.json.
It produced 64 usable rows, 63 continuity matches, 0 continuity breaks,
and one stitched .pt sample./home/steve/llm-optimizations/data/qwen36-eagle-hidden-dataset-smoke-tp2-20260618e/sample-000000-cmpl-911c7a5dc7a3503b-0-907523cf.pt
with hidden state shape (64, 2048) in BF16.Current implementation direction:
method:"eagle" path first, not EAGLE3. It wraps a
plain Llama-style draft as EagleLlamaForCausalLM.2048, vocab size 248320, and a
tiny one-layer Llama decoder. vLLM’s simple EAGLE model consumes
[token_embedding, target_hidden_state] through model.fc.Created a reusable smoke-checkpoint generator:
/home/steve/llm-optimizations/scripts/create-qwen36-eagle1-smoke-checkpoint.py
Generated draft:
/home/steve/llm-optimizations/data/qwen36-eagle1-smoke-draft-20260618a
Checkpoint properties:
hidden_size=2048, intermediate_size=4096;num_attention_heads=16, num_key_value_heads=2, head_dim=128;vocab_size=248320;43M BF16 parameters;embed_tokens and lm_head so vLLM shares the target
modules.Loader/runtime smoke:
/home/steve/llm-optimizations/data/qwen36-eagle1-smoke-load-tp2-20260618a.log
failed before model/draft loading with a oneCCL/SYCL
PersistentDeviceCodeCache::getItemFromDisc segfault during early TP2
allreduce.SYCL_CACHE_PERSISTENT=0:
/home/steve/llm-optimizations/data/qwen36-eagle1-smoke-load-tp2-20260618b.log
reached readiness.Detected EAGLE model without its own embed_tokens... and
Detected EAGLE model without its own lm_head....Drafted: 1 tokens,
Accepted: 0 tokens, acceptance 0.0%.Conclusion:
The local EAGLE-1 checkpoint format is loadable and executable on XPU. The next required step is a real trainer that writes the same checkpoint format from the Quark INT8 hidden-state dataset, then a TP2 acceptance smoke, then larger data export/training before any TP4 PIECEWISE performance gate.
cuda_graph.py/home/steve/suggestions.md and
/home/steve/llm-optimizations/notes/2026-06-14-qwen36-recovery-implementation.md
after each meaningful result.Promotion rules:
93.55 tok/s base and pass canaries and
quality gates before it matters.After the latest failed skip0-19 run, I checked xpu-smi ps.
Observed state:
xpu-smi appeared while checkingSo the machine was clean at the time this file was written.
Readiness-only test skip0-37 was run with the full safe fast identity
plus fused-prologue capture enabled and SKIP_LAYERS=0-37 (only layers
38-39 allowed to use fused-prologue capture).
prefill-safe-int8-prologue-skip0-37-c1-ready/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-prologue-skip0-37-c1-ready-20260617011726.logRuntimeError: level_zero backend failed with error: 20
(UR_RESULT_ERROR_DEVICE_LOST) at
/home/steve/src/vllm/vllm/v1/worker/gpu_model_runner.py:9456,
logit_indices_device = torch.from_numpy(logit_indices).to(...).This is the SAME failure site as skip0, skip0-3, and skip0-19,
even though only 1-2 layers (38-39) used fused-prologue capture.
Conclusion: the failure is SYSTEMIC to “fused-prologue capture enabled
at all,” not per-layer MoE math. Enabling captured fused prologue on any
layer perturbs the c1 PIECEWISE capture such that the dummy sampler
torch.from_numpy(...).to(device) copy device-losts. Every layer-enabled
fused-prologue capture path will fail at this same site.
Implication: the layer bisection is futile. Stop spending time on captured fused prologue. The failure site is in the sampler/logits dummy run and the piecewise capture boundary machinery, not in MoE kernel math, so the remaining options are the handoff step-3 pivots.
Post-run cleanup: the 4 VLLM::Worker_TP* processes orphaned after the
device-lost were SIGKILLed; xpu-smi ps confirmed all GPUs idle and no
vLLM processes remain.
Note: the ablation runner now records
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_SKIP_LAYERS and
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_LAYER_REGEX in both the log
echo and the summary JSON, so future fused-prologue runs carry full
identity (this was previously a gap).
After concluding fused-prologue capture is systemically dead, pivoted to
shared-experts on the working safe PIECEWISE lane (per the
moe.shared_experts.apply_no_overlap ~2.74 ms/step budget). Two
implemented levers were tested against the safe identity (only the one
env var changed, so identity matches the 93.55 base).
Lever A — VLLM_XPU_SHARED_EXPERTS_STREAM=1 (aux-stream overlap):
prefill-safe-int8-sharedexp-stream-c1RuntimeError: wait method cannot be used for an event associated
with a command graph at torch.xpu.empty_cache() inside
torch.cuda.graph() capture (cuda_graph.py:1634). Multi-stream
shared-expert events are incompatible with XPU PIECEWISE command-graph
capture. Preconditions were met (disable_inplace=True via
layer.py:521, token threshold 256 OK for decode). So this lever is
blocked by an XPU runtime limitation, same wall as fused-prologue.Lever B — VLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT=1 (fused
silu+mul+quant + raw int8_gemm_w8a8 for the shared down_proj):
prefill-safe-int8-sharedexp-fusedactquant-c1/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-fusedactquant-c1-summary-20260617014121.json91.85 tok/s corrected
(decode 10.89 ms/tok) — SLOWER than 93.55 base.silu_and_mul_quant_int8_xpu
kernel.down_proj for a slower raw int8_gemm_w8a8,
and the fused silu+quant kernel diverges numerically.Shared-expert per-step timing breakdown (from
qwen36-shared-expert-internals-timing-timing-decision-20260615a20.json,
summed across layers): gate_up_proj 0.725 ms, silu_and_mul 0.725 ms,
down_proj 0.549 ms, expert_gate 0.373 ms, gate_mul 0.146 ms. Most
of this is real memory-bound int8 GEMM work for single-token decode, so
the only realistic reductions are overlap (blocked) or kernel fusion
(currently buggy and slower).
Conclusion: on the captured PIECEWISE lane, the two main shared-expert
speed levers are not viable as-is. Remaining untested lower-ceiling
levers: VLLM_XPU_SHARED_EXPERT_ACT_WORKSPACE (buffer reuse, tiny) and
VLLM_XPU_MOE_SHARED_ADD_ALLREDUCE_CUSTOM_OP.
User directed fixing the fused-act-quant kernel. Deep investigation (standalone op-level comparison on XPU) shows the fused activation kernel is NOT the bug:
torch.ops._xpu_C.silu_and_mul_quant_int8_xpu(gate_up) produces
BIT-EXACT identical (int8, scale) to the unfused path
silu_and_mul -> torch.ops._xpu_C.per_token_quant_int8_xpu(out)
(0/24576 mismatches, scale max diff 0.0, bf16 inputs, d=3072).int8_gemm_w8a8 op and the SAME
processed weight/scale as the normal XPUInt8ScaledMM.apply_weights
path (scaled_mm/xpu.py:198), with identical args (out_dtype=bf16,
bias=None). So the shared-expert math is identical fused vs unfused.So the color-canary failure is NOT a math/correctness bug in the fused
kernel. The mismatch record shows it occurred at repeat index 6
(repeats 0-5 were consistent) with a divergent <think> token sequence
— i.e. NON-DETERMINISM under PIECEWISE capture replay, not a systematic
error. Most likely cause: the fused path calls the non-_out variant
silu_and_mul_quant_int8_xpu, which allocates its output tensors fresh
each call; under cudagraph capture those pool allocations can alias
across replays, breaking determinism. The _out variant
(silu_and_mul_quant_int8_xpu_out) with persistent buffers likely fixes
determinism.
The speed deficit (91.85 vs 93.55, +0.20 ms) is because
launch_silu_and_mul_quant_int8 (int8_quant.cpp:85) is a non-vectorized
two-pass scalar kernel, slower than the vectorized silu_and_mul +
per_token_quant_int8_xpu it replaces. Even with determinism fixed AND
the kernel vectorized, the theoretical ceiling is small (it only merges
the silu read-pass with the quant read-pass; the GEMM is unchanged) —
well under the ~7% needed for >100 tok/s.
Net: fused-act-quant is architecturally low-ceiling on this lane. The recurring blocker across fused-prologue, stream-overlap, and now fused-act-quant is the XPU PIECEWISE capture constraint itself.
Pursued the capture constraint as the root blocker for shared-expert stream overlap. Root-caused the failure:
torch.cuda.graph.__enter__ (torch/xpu/graphs.py) calls
torch.xpu.empty_cache() directly. vLLM already no-ops
torch.accelerator.empty_cache for PIECEWISE pieces with
gc_disable (cuda_graph.py:1617), but that is a DIFFERENT function
object from torch.xpu.empty_cache (verified distinct module/identity),
so the actual call in __enter__ was never intercepted. With aux-stream
events tied to a command graph, empty_cache’s internal wait raised
RuntimeError: wait method cannot be used for an event associated with
a command graph.Fix added (vllm cuda_graph.py, env-gated VLLM_XPU_GRAPH_NO_EMPTY_CACHE):
no-op torch.xpu.empty_cache during capture for XPU. This is a real bug
fix (vLLM was patching the wrong function on XPU) and unblocks aux-stream
graph capture generally; gated so the default lane is unchanged.
Result with VLLM_XPU_SHARED_EXPERTS_STREAM=1 +
VLLM_XPU_GRAPH_NO_EMPTY_CACHE=1 (full safe identity, both envs changed):
prefill-safe-int8-sharedexp-stream-noemptycache-c1/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-stream-noemptycache-c1-summary-20260617030450.json73.77 tok/s corrected (decode 13.56 ms/tok) — MUCH SLOWER than
the 93.55 base (-21%).Conclusion: the capture constraint was NOT the real blocker for stream overlap’s value. Even capture-unblocked, shared-experts aux-stream overlap is slower on 4x Arc Pro B70, because the separate stream does not provide real compute concurrency on this hardware — the fork/join/sync overhead is paid with nothing hidden. Stream overlap is closed as a speed lever.
Overall MoE/shared-expert lane outcome (all on the captured PIECEWISE lane): fused-prologue (capture-dead), stream-overlap (capture-fixable but no concurrency -> slower), fused-act-quant (kernel correct but slower + replay-nondeterministic). No per-pass MoE lever reaches the +7% needed for
100 tok/s. The capture constraint plus the lack of XPU multi-stream concurrency together cap the captured-lane approach.
Pivoted to speculative decoding (native MTP is dead: the Quark INT8
checkpoint has 0 mtp.* weights, confirmed in
notes/2026-06-10-qwen36-local-argmax-and-mtp-rejected.md). Tested
n-gram spec decode (method:ngram, k=5, prompt_lookup_min=2/max=5,
{"cudagraph_mode":"PIECEWISE","max_cudagraph_capture_size":128}) through
the standard canary+quality pipeline (runner made launcher-configurable via
SERVER_LAUNCHER; identity otherwise matches the safe lane). The prior
2026-06-10 note reported 114.86 tok/s, but that was on a HIGH-REUSE prompt.
Result (label prefill-safe-int8-ngram5-cg128-c1):
natural-chat metrics prompt: 48.30 tok/s
corrected (decode 26.79 ms/tok) — SLOWER than the 93.55 base.SpecDecodingMetrics during the metrics phase showed many windows
at Mean acceptance length: 1.00, Accepted: 0, 0.0%; only the
repetitive canary/quality prompts showed high acceptance (88%, length
5.40). So spec drafts are all rejected on general prompts -> pure
overhead. n-gram only helps copy/repetition workloads.Conclusion: n-gram spec decode is NOT a reliable path to >100 tok/s with no quality loss on the standard benchmark. It regresses speed on general prompts (low acceptance) and is nondeterministic (json canary).
Four speed levers exhausted with clear mechanisms:
VLLM_XPU_GRAPH_NO_EMPTY_CACHE) but no XPU stream concurrency -> -21%.Concrete artifacts produced (all uncommitted, ready to commit):
SERVER_LAUNCHER runner
option + handoff findings.VLLM_XPU_GRAPH_NO_EMPTY_CACHE capture bug-fix (real fix: vLLM
patched the wrong empty_cache function on XPU)./tmp/opencode/.The 93.55 tok/s base appears near the practical ceiling for this model/hardware on the captured PIECEWISE lane. Reaching a reliable >100 tok/s with no quality loss likely requires either a real draft-model spec decoder with high diverse-prompt acceptance (EAGLE; MTP weights are absent from this checkpoint) or custom MoE int8 GEMMs faster than oneDNN.
Reframed goal: >150 tok/s single-session decode (prefer >200), no quality
loss. Only a learned-draft spec decoder can give that multiplicative win.
Native MTP was thought dead (Quark INT8 checkpoint has 0 mtp.* weights),
but the official FP8 snapshot IS present locally
(models--Qwen--Qwen3.6-35B-A3B-FP8/snapshots/95a72...), so a hybrid
checkpoint (Quark INT8 target + official FP8 MTP head) was built via
scripts/create-qwen36-quark-fp8-mtp-hybrid.py (output
/mnt/fast-ai/qwen36-quark-int8-fp8-mtp-hybrid).
GOTCHA found and fixed: the ablation runner exports MODEL_PATH (Quark
default) which the hybrid-mtp launcher’s ${MODEL_PATH:-hybrid} kept, so
the first run served the QUARK path (random/uninitialized MTP head) -> 0%
acceptance. Re-running with MODEL_PATH explicitly set to the hybrid made
MTP load the real weights.
MTP acceptance (hybrid, real weights): GOOD. Per-position acceptance
62%-100% (avg ~62% on natural-chat, ~88% on repetitive), acceptance length
~1.6-1.9 at k=1. So a learned draft matched to this target IS achievable;
the hybrid head generalizes acceptably across the INT8/FP8 hidden-state
gap. (Label prefill-safe-int8-hybrid-mtp-k1-real-c1.)
BUT two blockers remain:
SPEED (the MTP draft is too heavy). The MTP head is a FULL MoE layer
(256 experts, mtp.layers.0.mlp.experts.*), nearly as expensive as a
target forward. Measured at k=1 PIECEWISE cg128: 69.46 tok/s (decode
14.42 ms/tok) -> SLOWER than the 93.55 base. The extra draft token
costs ~12.8 ms (~1x a target forward). A draft that costs ~1x the
target cannot yield big speedups. (A graph-none run measured 19 tok/s -
the accepted-launcher default is cudagraph_mode:NONE; always set
PIECEWISE+cg128 for valid spec-decode speed.)
CORRECTNESS (parity bug persists). At k=1 PIECEWISE both json- and
color-canary FAILED (the verifier bonus-row GDN recurrent/conv state
divergence from recovery-implementation.md is not fixed).
Conclusion: MTP cannot reach >150 here because its draft is a full MoE
layer (too expensive). n-gram is free but 0% diverse acceptance. The
architecture that delivers >150 on diverse prompts is a LIGHTWEIGHT
learned draft (EAGLE: 1-2 thin layers, no MoE). vLLM has
extract_hidden_states.py (EAGLE data prep) and the eagle proposer, but
the EAGLE training repo is NOT present locally and no trained EAGLE draft
for this model exists on disk. Reaching the >150 goal therefore requires
training an EAGLE draft on the Quark INT8 target (extract hidden states,
clone SafeAILab/EAGLE, train) - a multi-hour task with XPU-compat risk.
Important re-anchor:
VLLM_XPU_SPEC_DECODE_DRAFT_ONLY=1,
VLLM_XPU_GDN_SERIAL_SPEC_DECODE=1,
VLLM_XPU_GDN_SERIAL_SPEC_PACKED_DECODE=1,
VLLM_XPU_GDN_SERIAL_SPEC_CONV=1,
VLLM_XPU_GDN_SPEC_PROMOTE_RUNNING_AFTER_SPEC=1, and
VLLM_XPU_GDN_SPEC_PROMOTE_CONV_STATE=1.qwen36-oracle-k8-draftonly-serial-restoredidentity-currenttree-eager-tp2-20260618b
has baseline_match_all=true, first diff null, 24/24 accepted, 100%
full-accept rows. The parity gate still reports replay-accounting
mismatches, but token output is exact.qwen36-oracle-k8-draftonly-serialgdn-nopostprocess-control-currenttree-eager-tp2-20260618b)
failed at token 18 with 16/32 accepted. Do not use this as evidence against
the serial oracle path; it was the wrong identity.Failed native-prefill bridge attempts:
qwen36-oracle-k8-draftonly-nativeprefill-allornothing-rollback-currenttree-eager-tp2-20260618b:
all-or-nothing reduced the third partial row to 0 accepted and preempted,
but output still diverged at token 17. Scheduler rollback alone cannot make
native-prefill safe.qwen36-oracle-k8-draftonly-nativeprefill-allornothing-finalpromote-currenttree-eager-tp2-20260618b:
enabling final promotion plus conv-state promotion did not help; same first
diff at token 17.qwen36-oracle-k8-draftonly-nativeprefill-replaycols-allornothing-finalpromote-currenttree-eager-tp2-20260618b:
replayed output state columns worsened acceptance (8/16) and still diverged
at token 17.Conclusion:
Postprocess experiment status:
VLLM_XPU_GDN_NONSPEC_POSTPROCESS_ACCEPTED_STATE=1 helper in
vllm/v1/worker/mamba_utils.py and runner hook in
vllm/v1/worker/gpu_model_runner.py.Serial correctness anchor:
qwen36-oracle-k8-draftonly-serial-restoredidentity-currenttree-eager-tp2-20260618e
emitted the accepted no-spec token stream exactly (first_diff=null, 32/32
output tokens match) with spec activity (24/24 accepted, 100%, 3 rows).--allow-replay-accounting-mismatch and wrapper env
ALLOW_REPLAY_ACCOUNTING_MISMATCH=1. Default behavior remains strict.data/qwen36-oracle-k8-draftonly-serial-restoredidentity-currenttree-eager-tp2-20260618e-allowacct-gate-summary.json
passed exact mode. Use this option only for known accounting-only trace noise,
not for token or suppressed-follow-up failures.Native verifier probes after the restored serial anchor:
qwen36-oracle-k8-draftonly-nativeprefill-exactstatereplay-offset1-manualconv-nofinalpromote-allornothing-postprocess-currenttree-eager-tp2-20260618e:
failed at token 17. All-or-nothing is not sufficient.qwen36-oracle-k8-draftonly-nativeprefill-exactstatereplay-offset1-manualconv-nofinalpromote-nopreemptreject-postprocess-currenttree-eager-tp2-20260618e:
failed at token 24, immediately after accepting ... 271, 248068.qwen36-oracle-k8-draftonly-nativeprefill-exactstatereplay-offset1-manualconv-writeoutputs-nofinalpromote-postprocess-currenttree-eager-tp2-20260618e:
failed at token 17. Python/FLA replay outputs are not equivalent to the native
safe output lane.qwen36-oracle-k8-draftonly-nativeprefill-exactstatereplay-offset1-nativedecode-writeoutputs-nofinalpromote-postprocess-currenttree-eager-tp2-20260618e:
failed at token 17. Native per-position replay output write also worsened.qwen36-oracle-k7-draftonly-nativeprefill-exactstatereplay-offset1-manualconv-nofinalpromote-postprocess-currenttree-eager-tp2-20260618e:
failed at token 24. The issue is not simply k=8 depth; it is a native
verifier/state/logit boundary around the 248068 special token region.Current interpretation:
VLLM_XPU_GDN_NATIVE_SPEC_PREFILL_EXACT_REPLAY_WRITE_OUTPUTS=1 and
VLLM_XPU_GDN_NATIVE_SPEC_PREFILL_EXACT_REPLAY_NATIVE_DECODE=1 both failed.Serial verifier speed check:
qwen36-ablation-serialgdn-oracle-k8-piecewise-tp2-metrics-20260618f
measured the serial-correct verifier under PIECEWISE graph on TP2.data/qwen36-ablation-serialgdn-oracle-k8-piecewise-tp2-metrics-20260618f-summary-20260618192000.json.14.03 corrected tok/s, 72.10 ms/token. This is not a viable
speed lane. Keep serial GDN as a token-parity oracle only.EAGLE speed ceiling:
qwen36-ablation-eagle2-tokenheavy-synthaccept4-piecewise-tp2-k3-ceiling-20260618h
measured 140.14 corrected tok/s, 7.20 ms/token. This is metrics-only,
with canaries and quality skipped, so it is not promotable. It shows perfect
k3 is still below the >150 tok/s target.qwen36-ablation-eagle2-tokenheavy-synthaccept6-piecewise-tp2-k5-ceiling-20260618h
measured 181.91 corrected tok/s, 5.56 ms/token. This is also metrics-only
and not promotable. It proves the serial EAGLE k5 path can structurally clear
>150 tok/s if the draft acceptance is high enough.Real EAGLE draft results:
qwen36-ablation-eagle2-tokenheavy-fusedbatch-nosafety-fastpath-piecewise-tp2-k5-smoke-20260618h
measured 52.52 corrected tok/s, 18.95 ms/token, with mean acceptance
length 2.02, 107/525 accepted drafts, and per-position acceptance
0.543, 0.314, 0.124, 0.038, 0.000.scripts/train-qwen36-eagle1-draft.py with --rollout-steps; default
one-step behavior is unchanged.data/qwen36-eagle2-corpus2-tokenheavy-rollout5-trained-20260618h.
Training used the token-heavy checkpoint as init, three epochs, lr 5e-6,
rollout steps 5, and the same corpus/corpus2/pos datasets.qwen36-ablation-eagle2-rollout5-piecewise-tp2-k5-smoke-20260618h
measured 55.70 corrected tok/s, 17.95 ms/token. Acceptance was worse
than useful: mean acceptance length 1.90, 80/445 accepted drafts, and
per-position acceptance 0.494, 0.303, 0.101, 0.000, 0.000.Decision:
181.91 tok/s synthetic ceiling.Cached PIECEWISE traceability fix:
/home/steve/src/vllm/vllm/compilation/caching.py so the cached
PiecewiseBackend construction now passes submod_name=submod_name.VLLM_XPU_CUDAGRAPH_DISABLE_SUBMOD_REGEX and direct-sync isolation
hard to audit.py_compile passed for vllm/compilation/caching.py.Launcher health preflight:
/home/steve/llm-optimizations/scripts/launch-qwen36-quark-int8-accepted.sh
with QWEN36_XPU_PREFLIGHT.QWEN36_XPU_PREFLIGHT=auto, which runs the XPU/XCCL
preflight for TP_SIZE>=4 before starting vLLM. Set
QWEN36_XPU_PREFLIGHT=0 only for explicitly labeled diagnostics.${LOG_PATH%.*}-xpu-health.log unless QWEN36_XPU_PREFLIGHT_LOG is set.bash -n passed for the launcher and health script. A blocked
launch smoke test refused to start TP4 and wrote
/home/steve/llm-optimizations/data/qwen36-preflight-blocked-launch-smoke-20260618-xpu-health.log.Fused-prologue isolation attempts:
qwen36-ablation-prologue-disable-submod2-c1capture-20260618proliso1.log
attempted to isolate piecewise:2/ with
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET=1,
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_ALLOW_CAPTURE=1,
VLLM_XPU_CUDAGRAPH_DISABLE_SUBMOD_REGEX='piecewise:2/', and direct sync on
the same submodule.qwen36-ablation-prologue-disable-submod2-c1capture-gmem086-20260618proliso2.log
repeated the same probe at GPU_MEMORY_UTILIZATION=0.86.Hardware/runtime health finding:
/home/steve/llm-optimizations/scripts/check-qwen36-xpu-xccl-health.sh./home/steve/llm-optimizations/data/qwen36-xpu-health-device3-wedged-20260618.log.
Physical devices 0, 1, and 2 pass single-device XPU allocation; physical
device 3 fails a small tensor allocation with
UR_RESULT_ERROR_OUT_OF_RESOURCES. A 4-rank XCCL barrier/all-reduce over
0,1,2,3 then fails on rank 3 with the same error./home/steve/llm-optimizations/data/qwen36-xpu-health-devices012-pass-20260618.log.
Physical devices 0,1,2 pass single-device allocation and 3-rank XCCL
barrier/all-reduce.xpu-smi discovery: failed XPU id 3 is PCI
0000:47:00.0, DRM /dev/dri/card2./dev/dri/card2 or the render nodes. xpu-smi ps
showed only xpu-smi itself during inspection.xe 0000:47:00.0 GT0 job timeouts, VM job
timeouts, and driver reset attempts before the health check. This is a
low-level card/driver recovery state, not an ordinary vLLM OOM.xpu-smi config -d 3 --reset reported Fail to reset device. Prior
noninteractive sysfs reset also required sudo. A root-level PCI/SBR reset or
host reboot is required before valid TP4 endpoint benchmarking can resume.How to re-check health:
PHYSICAL_DEVICES=0,1,2,3 XCCL_DEVICES=0,1,2,3 \
/home/steve/llm-optimizations/scripts/check-qwen36-xpu-xccl-health.sh
Healthy-subset check:
PHYSICAL_DEVICES=0,1,2 XCCL_DEVICES=0,1,2 \
/home/steve/llm-optimizations/scripts/check-qwen36-xpu-xccl-health.sh
Current decision:
piecewise:2/ isolation
probe with full benchmark identity before interpreting speed or quality.Because physical XPU 3 is still unhealthy, the following runs are offline
single-device replay diagnostics only. They are not endpoint speed results and
are not comparable to TP4 serving throughput. They use ZE_AFFINITY_MASK=0 and
ONEAPI_DEVICE_SELECTOR=level_zero:0 to avoid the wedged card.
Middle-layerlet correctness recheck:
/home/steve/llm-optimizations/scripts/check-qwen36-w8a8-middle-layerlet.py --graph-replay --require-graph/home/steve/llm-optimizations/data/qwen36-w8a8-middle-layerlet-check-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-w8a8-middle-layerlet-check-device0-20260618.mdqwen36_decode_one_token_tp4_shape.Full route-exact MoE replay, current binary:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-current-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-current-device0-20260618.mdpreallocated_staged at
191.159 us best-row mean, 1.527x versus current xpu_fused_moe.xpu_fused_moe 308.363 us, scratch 256.104 us,
preallocated staged 200.261 us, fused-prologue staged 270.232 us.160 us/layerlet target.Offset/active-offset GEMM replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-offset-active-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-offset-active-device0-20260618.mdfused_prologue_offset_gemm at 186.619 us, 1.581x versus current
xpu_fused_moe.xpu_fused_moe 316.883 us, scratch 262.541 us,
preallocated staged 203.542 us, fused-prologue offset-GEMM 200.269 us,
active-offset-GEMM 201.639 us.160 us/layerlet target.Hot-packed expert replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-hotpack-offset-active-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-hotpack-offset-active-device0-20260618.mdfused_prologue_offset_gemm at 186.212 us, with
worse tail rows (worst_best_exact_nonreference_us_mean=241.526 us).Fused SiLU+quant replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-fusedsilu-offset-active-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-fusedsilu-offset-active-device0-20260618.mdfused_prologue_offset_gemm at
189.774 us.Graph replay timing diagnostic:
/home/steve/llm-optimizations/scripts/bench-qwen36-int8-moe-kernels.py
now supports --graph-replay-timing, --graph-warmup, and
--graph-iterations./home/steve/.venvs/vllm-xpu/bin/python -m py_compile scripts/bench-qwen36-int8-moe-kernels.py
passed./home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-graph-offset-active-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-graph-offset-active-device0-20260618.mdxpu_fused_moe_with_scratch mean 235.608 us,
preallocated_staged mean 238.654 us,
fused_prologue_offset_gemm mean 238.264 us, and
fused_prologue_active_offset_gemm mean 236.136 us, with a wide
~145-331 us range.56 was rerun
sequentially to avoid device contention:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-route-replay-graph-route56-sequential-device0-20260618.json.
It showed eager fused_prologue_offset_gemm at 185.661 us, but graph
replay around 331-340 us, so the under-160 graph samples from the
multi-route sweep are not trusted as promotion evidence.graph-route0-device0-20260618 and
graph-route56-device0-20260618 were accidentally launched in parallel on
the same physical XPU. Do not use those two artifacts for performance
conclusions.Updated target decision:
160 us on this route replay before it
deserves endpoint promotion work. For the >150 tok/s goal, the endpoint
target will probably need this MoE layerlet plus restored healthy TP4 graph
execution; current replay-only candidates are still not enough.2025.3 binary staging:
libsycl.so.9 ABI
and caused native crashes against the current PyTorch/XPU runtime./home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/
were backed up with suffix backup-20260618-full-layerlet-2025-precopy
before staging.torch.ops._xpu_C.qwen36_moe_w8a8_middle_layerlet and
torch.ops._xpu_C.qwen36_moe_w8a8_full_layerlet.Full layerlet implementation:
/home/steve/src/vllm-xpu-kernels:
csrc/xpu/moe_layerlet.cpp, csrc/xpu/ops.h,
csrc/xpu/torch_bindings.cpp, and CMakeLists.txt.VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1 in
vllm_xpu_kernels/fused_moe_interface.py.VLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1, so production/accepted runs cannot
accidentally use the experimental path.VLLM_XPU_MOE_W8A8_FULL_LAYERLET.Exact replay result:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-sweep-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-sweep-device0-20260618.mdfull_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0.155.263 us, mean 161.743 us,
max 171.005 us, median 161.083 us.167.315 us.xpu_fused_moe reference timing on the same sweep:
mean 304.637 us.160 us, but the
sweep is single-device offline replay, not an endpoint or graph-quality
result.Live Python branch smoke:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-livepath-smoke-device0-20260618.json
was exact with max_abs_diff = 0.0 and about 1.49x over the local base
call on that route./home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-livepath-prealloc-smoke-device0-20260618.json
was exact with max_abs_diff = 0.0, but only about 1.20x in that noisy
wrapper test.Next exact MoE work:
Fused quant1 candidates:
csrc/xpu/moe_layerlet.cpp, controlled by
VLLM_XPU_MOE_W8A8_FUSED_Q1=1.VLLM_XPU_MOE_W8A8_FUSED_Q1 unless
VLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1.run-qwen36-ablation-candidate.sh records
VLLM_XPU_MOE_W8A8_FUSED_Q1 in the run-summary identity.Results:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-q1fused-sweep-device0-20260618.json
0.0.158.761 / 167.807 / 163.251 / 182.766 us.1/16 rows under 160 us./home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-q1parallel-sweep-device0-20260618.json
0.0.160.385 / 178.733 / 176.648 / 202.214 us.0/16 rows under 160 us.Restored guarded default:
VLLM_XPU_MOE_W8A8_FUSED_Q1 unset:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-q1guard-default-sweep-device0-20260618.json
0.0.154.587 / 160.819 / 159.294 / 178.277 us.11/16 rows under 160 us.Next exact MoE target:
Fast gather:
VLLM_XPU_MOE_W8A8_FAST_GATHER=1./home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-fastgather-sweep-device0-20260618.json
0.0.155.116 / 161.622 / 161.662 / 170.463 us.5/16 rows under 160 us./home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-q1guard-default-sweep-device0-20260618.json
had 154.587 / 160.819 / 159.294 / 178.277 us and 11/16 rows under
160 us.Unchecked-middle wrapper:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-uncheckedmiddle-sweep-device0-20260618.json
0.0.163.327 / 177.125 / 173.131 / 210.363 us.0/16 rows under 160 us./home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-uncheckedmiddle-control-device0-20260618.json
0.0.157.361 / 171.858 / 170.957 / 191.336 us.2/16 rows under 160 us.backup-20260618-uncheckedmiddle-precopy; the importable
package is back to the previous fast-gather/full-layerlet staged binary.Current best exact offline MoE artifact:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-q1guard-default-sweep-device0-20260618.json.Harness fixes:
/home/steve/llm-optimizations/scripts/bench-qwen36-int8-moe-kernels.py.
The oracle compares candidates against a forced rows-per-expert W8A8 path
with offset/layerlet/prologue env paths disabled.Correctness bug found:
csrc/moe/fused_moe_prologue.hpp wrote
unpermuted_row_to_permuted_row in column-major expanded-token order
(k * rows + row) while moe_gather expects row-major
(row * topk + k).MergeExpertPrefixSumKernel to keep
permuted_row_to_unpermuted_row column-major for internal remap, but write
unpermuted_row_to_permuted_row[source_row * topk + source_k_rank].Build lesson:
_xpu_C.abi3.so was insufficient. The Python benchmark path
calls torch.ops._moe_C.fused_moe_prologue; that binary was still from
2026-06-12 and kept the old map layout._xpu_C.abi3.so: /home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_xpu_C.abi3.so_moe_C.abi3.so: /home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_moe_C.abi3.so_xpu_C.abi3.so.backup-20260618174647-pre-rowmajor-prologue-fix_moe_C.abi3.so.backup-20260618175726-pre-rowmajor-prologue-fixCorrected artifacts:
_moe_C rebuild failing/contaminated graph diagnostic:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-rowmajorfix-graph-hot-skew-device0-20260618.json_moe_C rebuild:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-immediatediff-after-moeC-rebuild-hot-skew-rows2-device0-20260618.json_moe_C rebuild:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-after-moeC-rebuild-hot-skew-sweep-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-after-moeC-rebuild-graph-hot-skew-rows2-device0-20260618.jsonPatch snapshots:
/home/steve/llm-optimizations/patches/vllm-xpu-kernels-qwen36-w8a8-layerlet-rowmajor-moeC-20260618.patch/home/steve/llm-optimizations/patches/llm-optimizations-qwen36-real-routing-oracle-immediatediff-20260618.patchCorrected hot-skew sweep summary:
| rows | xpu fused us | prealloc us | offset us | middle layerlet us | full layerlet us | max diff |
|---|---|---|---|---|---|---|
| 1 | 392.893 | 255.456 | 243.029 | 211.361 | 228.176 | 0.0 |
| 2 | 319.131 | 199.030 | 200.844 | 182.169 | 166.017 | 0.0 |
| 4 | 342.726 | 224.809 | 218.783 | 188.799 | 181.233 | 0.0 |
| 8 | 322.517 | 200.850 | 200.811 | 177.496 | 169.169 | 0.0 |
| 16 | 317.837 | 204.380 | 194.935 | 173.069 | 166.498 | 0.0 |
Current interpretation:
max_abs_diff = 0.0 on hot-skew
multi-row replay after both extensions are rebuilt.160 us gate on this
sweep. Rows 2/8/16 are close (166-169 us for full layerlet), but rows 1 is
still too slow (211 us middle, 228 us full).320-330 us, slower
than eager replay.Next exact/no-quality-loss performance targets:
_moe_C binary. It was not
included in the corrected sweep and may help hot-skew paths with many zero
experts.Corrected active-offset sweep:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-after-moeC-rebuild-activeoffset-hot-skew-sweep-device0-20260618.jsonmax_abs_diff = 0.0.| rows | active offset us | plain offset us | middle layerlet us | full layerlet us |
|---|---|---|---|---|
| 1 | 200.102 | 198.386 | 168.792 | 187.122 |
| 2 | 200.772 | 199.823 | 167.921 | 163.722 |
| 4 | 195.026 | 197.106 | 167.212 | 164.606 |
| 8 | 200.824 | 199.621 | 171.210 | 163.241 |
| 16 | 201.019 | 197.892 | 167.576 | 160.979 |
Decision: keep active-offset as a diagnostic only. It is exact, but not faster enough to promote.
Corrected q1/fast-gather retests:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-after-moeC-q1only-hot-skew-rows1-2-device0-20260618.json
0.0.190.957 us, full 201.240 us.173.546 us, full 176.384 us./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-after-moeC-fastgatheronly-hot-skew-rows1-2-device0-20260618.json
0.0.162.288 us, full 172.371 us.168.064 us, full 158.136 us./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-after-moeC-q1-fastgather-hot-skew-rows1-2-device0-20260618.json
0.0.163.146 us, full 170.599 us.161.330 us, full 158.470 us.Current next target:
162-169 us, while full C++ layerlet is still
slower on rows=1. A useful next patch should reduce full-layerlet rows=1
overhead without changing math, or add a genuinely faster single-token
full-layerlet path.Stable real-route baseline:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-default-firstdecode-routes-row1-stable-device0-20260618.jsonsetvars.sh:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-default-setvars-firstdecode-routes-row1-stable-device0-20260618.json/home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-firstdecode-route-fixture-routes-20260612ct.jsonl,
route starts 0:120:5, 30 measured iterations, 5 warmup iterations,
real-routing oracle enabled, q1 and fast-gather envs unset.max_abs_diff = 0.0.Stable real-route rows=1 summary:
| path | min us | mean us | median us | p90 us | max us | <=160 us rows |
|---|---|---|---|---|---|---|
| xpu scratch | 248.525 | 262.639 | 257.973 | 275.888 | 292.490 | 0/24 |
| plain offset | 189.216 | 203.241 | 200.009 | 212.507 | 233.532 | 0/24 |
| active offset | 189.769 | 204.650 | 202.847 | 213.597 | 226.699 | 0/24 |
| middle layerlet | 161.001 | 173.428 | 171.181 | 180.944 | 193.764 | 0/24 |
| full layerlet | 157.215 | 169.210 | 166.545 | 177.622 | 188.859 | 2/24 |
Decision: the corrected full layerlet is the best current exact MoE replay
path, but it is not enough for endpoint promotion. It clears the 160 us gate
on only 2 of 24 real routes and still has a 188.859 us tail.
Same-env confirmation after runtime identity patch:
158.326 / 169.172 / 164.374 / 182.699 / 205.267 us, with 5 of 24 rows
under 160 us.156.054 / 170.198 / 167.456 / 175.876 / 207.490 us, with 3 of 24 rows
under 160 us.Rejected real-route q1 and gather variants:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-fastgather-firstdecode-routes-row1-stable-device0-20260618.json
0.0.157.106 / 171.433 / 169.913 / 182.596 / 200.772 us./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-q1opt-firstdecode-routes-row1-stable-device0-20260618.json
0.0.159.356 / 171.950 / 167.073 / 190.830 / 199.455 us.Hot-expert physical packing simulation:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routepack-firstdecode-routes-row1-stable-device0-20260618.json0.0 within the remapped replay.156.054 / 170.198 / 167.456 / 175.876 / 207.490 us.25 us and 21 us, but hurt routes 80
and 5 by about 39 us and 31 us.Decision: physical expert packing is not a safe global speed lever from this fixture. Keep it as an idea for per-layer/per-rank adaptive layouts only if a future trace shows a stable route distribution with a smaller tail.
Runtime environment lesson:
/opt/intel/oneapi/compiler/2025.3/env/vars.sh made xpu-smi see all B70s
but made PyTorch report torch.xpu.device_count() == 0./opt/intel/oneapi/setvars.sh --force restored PyTorch XPU
visibility (device_count == 4), and with ONEAPI_DEVICE_SELECTOR=level_zero:0
the replay saw one selected device.runtime_identity, including device selector
env, q1/fast-gather envs, torch version, selected XPU device names, Python
path, and loaded vllm_xpu_kernels extension paths. It also records the
selected expert rows directly in each result.Immediate command-list real-route check:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-immediate-firstdecode-routes-row1-stable-device0-20260618.jsonSYCL_PI_LEVEL_ZERO_USE_IMMEDIATE_COMMANDLISTS=1 and
UR_L0_USE_IMMEDIATE_COMMANDLISTS=1.0.0.157.265 / 169.618 / 169.510 / 178.733 / 188.032 us, with 2 of 24 rows
under 160 us.158.326 / 169.172 / 164.374 / 182.699 / 205.267 us), immediate lists
smooth the worst tail but worsen median and do not improve mean.Decision: keep immediate command lists as a possible endpoint stability/tail lever to retest only after TP4 health is restored. It is quality-neutral in offline replay, but it is not a standalone rows=1 MoE win and should not be combined into accepted endpoint manifests without full quality and reliability gates.
W8A8 grouped GEMM m8 policy checks:
w8a16_policy_m_8 through
VLLM_XPU_W8A8_GROUPED_GEMM_POLICY=m8, plus diagnostic shape-specific
overrides:
VLLM_XPU_W8A8_GROUPED_GEMM_N_LT_K_POLICY and
VLLM_XPU_W8A8_GROUPED_GEMM_N_GT_K_POLICY.runtime_identity.max_abs_diff = 0.0).Artifacts and results:
| policy | artifact | full layerlet min/mean/median/p90/max us | <=160 rows | decision |
|---|---|---|---|---|
| rebuilt default | /home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-default-rebuilt-firstdecode-routes-row1-device0-20260618.json |
154.045 / 163.868 / 159.998 / 175.859 / 203.502 |
12/24 | current rebuilt control |
| generic m8 run 1 | /home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-m8policy-firstdecode-routes-row1-stable-device0-20260618.json |
155.153 / 163.253 / 160.339 / 172.203 / 179.175 |
9/24 | promising tail, not repeatable |
| generic m8 run 2 | /home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-m8policy-repeat-firstdecode-routes-row1-device0-20260618.json |
154.458 / 170.817 / 170.447 / 178.564 / 195.076 |
2/24 | rejected for repeatability |
| generic m8 + immediate | /home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-m8-immediate-firstdecode-routes-row1-device0-20260618.json |
154.731 / 167.267 / 164.970 / 174.000 / 212.484 |
6/24 | rejected |
m8 only when N<K |
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-m8-nltk-firstdecode-routes-row1-device0-20260618.json |
155.290 / 175.271 / 173.277 / 192.108 / 202.766 |
3/24 | rejected |
m8 only when N>K |
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-m8-ngtk-firstdecode-routes-row1-device0-20260618.log |
crashed before JSON flush | n/a | rejected, unsafe |
Decision: do not promote m8 policy. Generic m8 can reduce a single-run tail, but the improvement is not stable across repeats. GEMM1-only m8 is slower. GEMM2-only m8 segfaulted, although device 0 survived a post-crash XPU smoke test. Keep the m8 code path as a diagnostic only; do not set any m8 policy env in accepted endpoint launchers.
Next exact/no-quality-loss work:
Purpose:
>150 tok/s target (181.91 tok/s) if draft
acceptance is high enough, but current real draft acceptance is too weak.New script:
/home/steve/llm-optimizations/scripts/evaluate-qwen36-eagle-draft-offline.pyembed_tokens/lm_head, reads hidden-state dataset .pt files, rolls out
greedy draft proposals, compares token IDs against the recorded target greedy
continuation, and reports:
Offline acceptance results:
| checkpoint | eval set | artifact | mean accepted | decision |
|---|---|---|---|---|
| token-heavy EAGLE2 | capped corpus2+pos | data/qwen36-eagle2-tokenheavy-offline-accept-capped-20260618.json |
1.640625 |
baseline |
| rollout5 EAGLE2 | capped corpus2+pos | data/qwen36-eagle2-rollout5-offline-accept-capped-20260618.json |
1.9296875 |
best offline signal |
| token-focused rollout5 | capped corpus2+pos | data/qwen36-eagle2-tokenfocused-rollout5-offline-accept-capped-20260618i.json |
1.828125 |
rejected |
| token-heavy EAGLE2 | smoke held-out | data/qwen36-eagle2-tokenheavy-offline-accept-smoke-20260618.json |
1.5689655 |
baseline |
| rollout5 EAGLE2 | smoke held-out | data/qwen36-eagle2-rollout5-offline-accept-smoke-20260618.json |
1.7413793 |
best offline signal |
| token-focused rollout5 | smoke held-out | data/qwen36-eagle2-tokenfocused-rollout5-offline-accept-smoke-20260618i.json |
1.6724138 |
rejected |
Rollout5 details:
/home/steve/llm-optimizations/data/qwen36-eagle2-corpus2-tokenheavy-rollout5-trained-20260618h0.90625, 0.6121, 0.4225, 0.5667, 0.7647.Rejected token-focused fine-tune:
/home/steve/llm-optimizations/data/qwen36-eagle2-tokenfocused-rollout5-trained-20260618i--rollout-steps 5 --feature-loss-weight 0.25 --token-loss-weight 1.0
for 4 epochs.top1 = 0.3156, top3 = 0.4508,
token_loss = 2.0299), and offline acceptance landed below rollout5 on both
capped and smoke gates.EAGLE data coverage status:
data/qwen36-eagle-hidden-dataset-corpus-tp2-20260618a (48 samples)data/qwen36-eagle-hidden-dataset-corpus2-tp2-20260618a (254 samples)data/qwen36-eagle-hidden-dataset-pos-tp2-20260618a (54 samples)data/qwen36-eagle-hidden-dataset-smoke-tp2-20260618e (1 sample)/home/steve/llm-optimizations/scripts/collect-qwen36-eagle-hidden-corpus.py:
debug-log, python-patch, sql-analysis, shell-runbook,
api-contract, longform-summary, test-plan, algorithm,
comparison, and user-support.Next EAGLE work:
The shape-specific diagnostic VLLM_XPU_W8A8_GROUPED_GEMM_N_GT_K_POLICY=m8
previously segfaulted before JSON flush. The grouped-GEMM policy override now
ignores m8 when gemm_n > gemm_k, both for generic and N>K overrides:
/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2_interface.hppThis guard is not a performance win and does not change the accepted endpoint
identity. It exists to keep future diagnostic sweeps from selecting the unsafe
GEMM2 shape. Rebuild/stage _xpu_C after this edit before running any policy
sweeps that import vllm_xpu_kernels._xpu_C.
Expanded TP2 hidden-state export:
data/qwen36-eagle-hidden-dump-expanded-tp2-20260618j.log failed during
sampler profiling because Triton loaded
/opt/intel/oneapi/compiler/2026.0/lib/libsycl.so.9, which mismatched the
active UR loader:
undefined symbol: urDeviceWaitExp, version LIBUR_LOADER_0.12.LD_LIBRARY_PATH=/opt/intel/oneapi/compiler/2025.3/lib:$LD_LIBRARY_PATH
and PATH=/opt/intel/oneapi/compiler/2025.3/bin:$PATH. A direct Triton
target smoke passed with that ordering and still saw torch.xpu.device_count()
== 4.data/qwen36-eagle-hidden-dump-expanded-tp2-20260618k.log
on TP2 cards 0,1, graph-none, no async, Quark W8A8 INT8 target, 32k max
model length, GPU_MEMORY_UTILIZATION=0.82,
MAX_NUM_BATCHED_TOKENS=1024, MAX_NUM_SEQS=8, and hidden dump envs set.data/qwen36-eagle-hidden-expanded-tp2-20260618k-requests.json
with 64 prompts over the expanded prompt families.data/qwen36-eagle-hidden-dataset-expanded-tp2-20260618k-summary.json
produced 6144 rows, 64 saved samples, 6080 continuity matches,
0 continuity breaks, 0 position breaks, and 0 bad files.Expanded-data offline acceptance controls:
| checkpoint | expanded mean accepted | decision |
|---|---|---|
| token-heavy control | 1.046875 |
weak |
| rollout5 control | 1.132812 |
current control |
The new expanded set is substantially harder than the old capped corpus2+pos
gate where rollout5 gets 1.929688. This explains why endpoint acceptance is
still far below the synthetic k5 ceiling.
Fine-tune attempts against expanded data:
| checkpoint | capped corpus2+pos | smoke | expanded | decision |
|---|---|---|---|---|
| rollout5 control | 1.929688 |
1.741379 |
1.132812 |
keep as best current control |
| expanded low-LR mixed | 1.914062 |
1.741379 |
1.125000 |
reject |
| expanded-only | 1.875000 |
not run | 1.226562 |
reject; improves expanded but hurts established gate |
| expanded oversample4 | 1.882812 |
1.793103 |
1.156250 |
reject; improves smoke/expanded slightly but hurts capped gate |
Artifacts:
data/qwen36-eagle2-rollout5-expanded-lowlr-trained-20260618k.data/qwen36-eagle2-rollout5-expanded-only-trained-20260618l.data/qwen36-eagle2-rollout5-expanded-oversample4-trained-20260618m.data/qwen36-eagle2-rollout5-expanded-*-offline-accept-*.json.Decision:
Offline eval identity correction:
start_stride is part of the offline EAGLE acceptance identity.start_stride=4; the historical smoke
control used start_stride=1; the expanded control used start_stride=4.start_stride=1 capped rerun of the same 2-layer rollout5 control
measured only 1.273438 mean accepted, while the historical stride-4 capped
identity measures 1.929688. Do not compare offline acceptance artifacts
without matching dataset_dir, max_steps, max_starts, start_stride,
dtype, draft checkpoint, target checkpoint, and evaluator script revision.Trainer changes:
scripts/train-qwen36-eagle1-draft.py:
--repeat-last-init-layer: copy the last checkpoint layer into added
layers. This was tested and rejected.--zero-extra-init-layer: zero added layer matrices. This preserves output
but is mostly dead for training because zeroing every matrix removes useful
gradients through the added layer.--residual-extra-init-layer: copy the last checkpoint layer into added
layers, then zero only self_attn.o_proj.weight and
mlp.down_proj.weight. This starts as a residual no-op while preserving
trainable q/k/v and gate/up features.--freeze-init-base-layers: freeze fc and checkpoint-loaded layers so
only added residual layers train.1.929688) and froze the two loaded base layers, so this
is the correct initializer for future deeper-draft work.Results:
| checkpoint | capped stride-4 | smoke stride-1 | expanded stride-4 | decision |
|---|---|---|---|---|
| 2-layer rollout5 control | 1.929688 |
1.741379 |
1.132812 |
current control |
| 3-layer repeat-init expanded2 | 1.921875 |
1.793103 |
1.132812 |
reject; capped regression |
| 3-layer repeat-init old-only | 1.898438 |
1.793103 |
1.117188 |
reject; capped regression |
| 3-layer residual-init expanded2, base frozen | 1.890625 |
1.758621 |
1.125000 |
reject; capped and expanded regression |
| 3-layer residual-init old-only, base frozen | 1.929688 |
1.741379 |
1.109375 |
reject; neutral on accepted gates, worse expanded |
Decision:
start_stride.Checkpoint-selection follow-up:
--checkpoint-every to scripts/train-qwen36-eagle1-draft.py so
intermediate draft checkpoints can be selected by offline acceptance instead
of final batch metrics.data/qwen36-eagle3-rollout5-residualextra-oldonly-ckpt-trained-20260618s.data/qwen36-eagle3-residualextra-oldonly-checkpoint-capped-sweep-20260618s.jsonl.step-000050, step-000100, step-000500, and
final, all tied the control at 1.929688. No checkpoint exceeded the
accepted 2-layer rollout5 control.User asked to pursue exact MoE optimization first, with GEMM2+gather epilogue fusion before escalating to a persistent one-dispatch layerlet.
Implementation:
/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cpp.VLLM_XPU_MOE_W8A8_DIRECT_GEMM2_GATHER=1./home/steve/llm-optimizations/scripts/bench-qwen36-int8-moe-kernels.py./home/steve/src/vllm-xpu-kernels/build/xpu-c-only-2025/_xpu_C.abi3.so
copied to
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_xpu_C.abi3.so.Smoke replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-directgemm2gather-route0-smoke-device0-20260619.json.ONEAPI_DEVICE_SELECTOR=level_zero:0, ZE_AFFINITY_MASK=0,
route fixture
qwen36-quark-int8-tp4-firstdecode-route-fixture-routes-20260612ct.jsonl,
rows=1, route start 0, direct flag enabled.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0 and
full_layerlet_vs_rows_oracle_max_abs_diff = 0.0.200.595 us; same-run middle layerlet 169.842 us.Multi-route replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-directgemm2gather-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-directgemm2gather-routes7-device0-20260619.md.0,40,80,85,95,100,115.max_abs_diff = 0.0 versus both xpu_fused_moe and rows-oracle.168.969 / 190.527 / 200.715 us, 0/7 rows under
160 us.171.844 / 198.937 / 228.868 us, 0/7 rows under
160 us.Decision:
Next exact MoE work:
max_abs_diff = 0.0,
repeated timing, target below the current 160-180 us layerlet band before
any endpoint work.Follow-up to the direct GEMM2+gather diagnostic above. The direct scalar kernel was exact but slow because it abandoned the optimized DPAS grouped-GEMM path. I prototyped a narrower DPAS-backed top-k8 GEMM2+gather path to test the best plausible version of “GEMM2 + gather epilogue first” before escalating to the persistent layerlet.
Implementation:
VLLM_XPU_MOE_W8A8_DPAS_GEMM2_GATHER=1.VLLM_XPU_MOE_W8A8_DPAS_GEMM2_GATHER_NTILE=32 and
VLLM_XPU_MOE_W8A8_DPAS_GEMM2_GATHER_NTILE=16. Unset/default remains the
original N64 policy./home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.hpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2_interface.hpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/gemm_xe2_policy.hpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.cpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.h/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/grouped_gemm_interface.cpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/grouped_gemm_interface.h/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cpp/home/steve/llm-optimizations/scripts/bench-qwen36-int8-moe-kernels.pyBuild/import:
cmake --build /home/steve/src/vllm-xpu-kernels/build/xpu-c-only-2025 -j 8./home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_xpu_C.abi3.so and
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_moe_C.abi3.so.torch.ops._xpu_C.qwen36_moe_w8a8_full_layerlet is
available.Smoke replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-route0-smoke-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-route0-smoke-device0-20260619.md.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0.176.817 us, above the 160 us promotion gate.Multi-route replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-routes7-device0-20260619.md.ZE_AFFINITY_MASK=0, VLLM_XPU_MOE_W8A8_DPAS_GEMM2_GATHER=1,
route fixture
/home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-routecapture6-routes-rank0-20260611.jsonl,
layer regex layers[.]9[.], rows=1, route starts
0,40,80,85,95,100,115, warmup=20, iterations=100.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0.161.554 / 172.244 / 178.651 us.0/7 rows under 160 us;
status exact_nonreference_candidates_exist_but_gate_not_met.N-tile replay follow-up:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-ntile32-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-ntile32-routes7-device0-20260619.md.VLLM_XPU_MOE_W8A8_DPAS_GEMM2_GATHER_NTILE=32.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0.170.550 / 176.388 / 173.695 / 191.394 / 189.810 us; 0/7 rows under
160 us./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-ntile16-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-dpasgemm2gather-ntile16-routes7-device0-20260619.md.VLLM_XPU_MOE_W8A8_DPAS_GEMM2_GATHER_NTILE=16.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0.164.743 / 182.092 / 177.334 / 214.518 / 211.275 us; 0/7 rows under
160 us.Decision:
Updated next move:
max_abs_diff = 0.0 before endpoint or quality-gate work.After rejecting GEMM2+gather epilogue variants, I tested one smaller
fixed-overhead cut before escalating to a true persistent layerlet: remove the
tiny per-GEMM atomic_buffer = at::empty({1}, int32) allocation inside the
grouped GEMM offsets launcher when called from the full layerlet.
Implementation:
VLLM_XPU_MOE_W8A8_WORKSPACE_ATOMIC=1.int* atomic buffer into the XE2
W8A8 INT8 offsets grouped GEMM path:
/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/grouped_gemm_interface.h/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/grouped_gemm_interface.cpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.h/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.cpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2_interface.hpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cppint slot of the existing uint8 prologue
workspace after prologue/quant1 completes. This keeps the current optimized
DPAS GEMM kernels and output order unchanged.Build/import:
cmake --build /home/steve/src/vllm-xpu-kernels/build/xpu-c-only-2025 --target _xpu_C -j 4./home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_xpu_C.abi3.so and
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_moe_C.abi3.so.torch.ops._xpu_C.qwen36_moe_w8a8_full_layerlet is
available.Route replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-workspaceatomic-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-workspaceatomic-routes7-device0-20260619.md./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-workspaceatomic-control-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-workspaceatomic-control-routes7-device0-20260619.md.ZE_AFFINITY_MASK=0, route fixture
/home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-routecapture6-routes-rank0-20260611.jsonl,
layer regex layers[.]9[.], rows=1, route starts
0,40,80,85,95,100,115, warmup=20, iterations=100.VLLM_XPU_MOE_W8A8_WORKSPACE_ATOMIC=1.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0 on all 7 rows.168.110 / 174.518 / 173.856 / 183.004 / 182.163 us; 0/7 rows under
160 us.161.697 / 166.446 / 166.044 / 177.249 / 175.779 us; 0/7 rows under
160 us.Decision:
After the GEMM2+gather and workspace-atomic branches missed the promotion gate, I tested the symmetric GEMM1-side question: whether the c1/top-k8 route-known shape can bypass the generic grouped-GEMM scheduler for GEMM1 and directly launch one DPAS workgroup family per routed expert row.
Implementation:
VLLM_XPU_MOE_W8A8_ROUTE_GEMM1=1.VLLM_XPU_MOE_W8A8_ROUTE_GEMM1_MTILE=8|32; unset/default uses M16./home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.hpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2_interface.hpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.cpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.h/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/grouped_gemm_interface.cpp/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/grouped_gemm_interface.h/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cpp/home/steve/llm-optimizations/scripts/bench-qwen36-int8-moe-kernels.pyBuild/import:
cmake --build /home/steve/src/vllm-xpu-kernels/build/xpu-c-only-2025 --target _xpu_C -j 4./home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_xpu_C.abi3.so.torch.ops._xpu_C.qwen36_moe_w8a8_full_layerlet is
available.Route replay:
ZE_AFFINITY_MASK=0, route fixture
/home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-routecapture6-routes-rank0-20260611.jsonl,
layer regex layers[.]9[.], rows=1, route starts
0,40,80,85,95,100,115, warmup=20, iterations=100,
target 160 us./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-control-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-control-routes7-device0-20260619.md./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-m16-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-m16-routes7-device0-20260619.md./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-m8-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-m8-routes7-device0-20260619.md./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-m32-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-routegemm1-m32-routes7-device0-20260619.md.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0 on all 7 rows.Timing:
0:162.311, 40:166.755, 80:170.076, 85:166.611,
95:167.100, 100:165.555, 115:161.335.161.335 / 165.677 / 166.611 / 170.671 / 170.076 us; 0/7 rows under
160 us.0:173.424, 40:167.312, 80:162.484, 85:163.872,
95:163.861, 100:164.664, 115:161.763.161.763 / 165.340 / 163.872 / 174.647 / 173.424 us; 0/7 rows under
160 us.167.183 / 176.882 / 177.742 / 190.124 / 188.642 us; 0/7 rows under
160 us.162.336 / 175.938 / 171.121 / 200.497 / 198.761 us; 0/7 rows under
160 us.Decision:
160 us target.After closing route-known GEMM1, I refreshed the two decision gates that decide whether to keep working TP4 endpoint timing or route-class-specific kernels.
TP4 XPU/XCCL health:
/home/steve/llm-optimizations/data/qwen36-xpu-health-tp4-20260619-routegemm1-followup.log.RuntimeError: level_zero backend failed with error: 40 (UR_RESULT_ERROR_OUT_OF_RESOURCES).dist.barrier(...) with the same
Level Zero out-of-resources error.xpu-smi ps showed no long-running vLLM/Python model workers holding VRAM;
only transient xpu-smi processes appeared.xpu-smi diag -d 3 -l 1 -j aborts with
terminate called without an active exception.xpu-smi config -d 3 --reset is available but failed as the current user.
Noninteractive sudo is not available, and /sys/bus/pci/devices/0000:47:00.0/reset
is root-only./home/steve/llm-optimizations/data/qwen36-xpu-health-devices012-20260619-routegemm1-followup.log.Decision:
QWEN36_XPU_PREFLIGHT=auto runs
/home/steve/llm-optimizations/scripts/check-qwen36-xpu-xccl-health.sh for
TP_SIZE >= 4 and refuses to start if the health gate fails.Route-class AOT/cache refresh:
/home/steve/llm-optimizations/data/qwen36-routeclass-aot-routecapture6-20260619.json./home/steve/llm-optimizations/data/qwen36-routeclass-aot-routecapture6-20260619.md./home/steve/llm-optimizations/data/qwen36-route-signature-cache-routecapture6-20260619.json./home/steve/llm-optimizations/data/qwen36-route-signature-cache-routecapture6-20260619.md.needs_more_route_windows_before_aot_commit.0.337 of the layer records, and every
top-k class count is 1 in this fixture.Decision:
I tested whether graph replay of the current exact full MoE layerlet removes enough launch/dispatch overhead to make the existing boundary worth promoting.
Artifacts:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-graphreplay-control-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-graphreplay-control-routes7-device0-20260619.md.Run identity:
ZE_AFFINITY_MASK=0.rows=1, warmup=20, iterations=80./home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-routecapture6-routes-rank0-20260611.jsonl.layers[.]9[.].0,40,80,85,95,100,115.Normal full layerlet timing:
0:163.709, 40:170.123, 80:164.201, 85:161.504,
95:167.763, 100:167.834, 115:167.326.161.504 / 166.066 / 167.326 / 170.123 us.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0.Graph replay timing:
full_layerlet graph replay route/us:
0:181.851, 40:195.768, 80:179.414, 85:179.286,
95:186.839, 100:181.281, 115:177.510.177.510 / 183.136 / 181.281 / 195.768 us.xpu_fused_moe_with_scratch mean 313.858 us,
preallocated_staged mean 253.763 us, and
fused_prologue_staged mean 229.347 us.Decision:
~161-170 us route band,
while graph replay makes this fixture slower rather than faster.Before starting a larger rewrite, I checked whether already-rejected exact
subpaths stack into a stable win. The only interesting combo was
VLLM_XPU_MOE_W8A8_FUSED_Q1=1 plus
VLLM_XPU_MOE_W8A8_ROUTE_GEMM1=1.
Initial combo sweep:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-combo-q1-routegemm1-routes7-device0-20260619.json.0:158.337, 40:158.479, 80:171.398, 85:157.696,
95:157.323, 100:157.864, 115:157.953.157.323 / 159.864 / 157.953 / 171.398 us.full_layerlet_vs_xpu_fused_moe_max_abs_diff = 0.0.Repeat gate:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-control-repeat100-a-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-q1-routegemm1-repeat100-a-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-q1-routegemm1-repeat100-b-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-q1-routegemm1-repeat100-m8-routes7-device0-20260619.json./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-q1-routegemm1-repeat100-m32-routes7-device0-20260619.json.Repeat timing:
162.568 / 169.427 / 167.591 / 177.143 us; 0/7 rows under 160 us.158.042 / 168.391 / 164.461 / 187.160 us; 3/7 rows under 160 us.158.862 / 170.901 / 167.939 / 186.982 us; 1/7 rows under 160 us.165.230 / 177.547 / 169.780 / 205.600 us; 0/7 rows under 160 us.165.580 / 174.773 / 177.906 / 185.706 us; 0/7 rows under 160 us.Decision:
I tested the public DFlash draft model as a possible no-quality-loss route to verified multi-token decode, but only as a quality/parity smoke. This was not a speed promotion.
Draft model:
z-lab/Qwen3.6-35B-A3B-DFlash.42d3b34d588423cdae7ba8f53a8cf7789346a719./mnt/fast-ai/llm-cache/hf/models--z-lab--Qwen3.6-35B-A3B-DFlash/snapshots/42d3b34d588423cdae7ba8f53a8cf7789346a719.905M.architectures=["DFlashDraftModel"], num_hidden_layers=8,
block_size=16, dflash_config.mask_token_id=248070,
dflash_config.target_layer_ids=[1,10,19,28,37].Kernel/runtime prerequisite fixed:
vllm_xpu_kernels._xpu_C extension could not import:
libgrouped_gemm_xe_2.so was stale and missing the new route-GEMM1 symbols._xpu_C and copied the rebuilt grouped GEMM/GDN
shared libraries into vllm_xpu_kernels/._xpu_C import ok, current_platform.device_type == "xpu".Parity runner/tooling:
/home/steve/llm-optimizations/scripts/run-qwen36-spec-parity-candidate.sh./home/steve/llm-optimizations/scripts/replay-qwen36-spec-trace.py.scheduled_spec_token_ids=[-1,...], and the replay
report should preserve those placeholders instead of crashing during
tokenizer decode.Smoke run:
qwen36-dflash-k15-eager-tp2-smoke-20260619b.0,1, eager, XPU graph off, GPU memory utilization 0.82,
prompt 512, output 32,
speculative config
{"method":"dflash","model":"...Qwen3.6-35B-A3B-DFlash/...42d3b34d...","num_speculative_tokens":15}./home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-smoke-20260619b-20260619dflashsmoke2.log./home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-smoke-20260619b-20260619dflashsmoke2-candidate.json./home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-smoke-20260619b-20260619dflashsmoke2-spec-trace.jsonl.Result:
draft_tokens=90, accepted=35, rejected=55,
accept rate 38.888888888888886%./home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-smoke-20260619b-20260619dflashsmoke2-replay-rerun.json.accounting_mismatch_count=0, generated mismatches=0,
schedule mismatches=0, accept mismatches=0./home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-smoke-20260619b-20260619dflashsmoke2-gate-summary-rerun.json.pass=false, mismatch_count=1.First mismatch:
/home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-smoke-20260619b-20260619dflashsmoke2-fixture-rerun.json.24.271 (\n\n).198 (\n).[11,321,874,4131,4557,13,271,248068,271,248069,271,16,13,2972,10886,38563].[11,321,874,4131,4557,13,271,248068,198,90700,8340,25,271,16,13,220].4, emission role replacement_after_reject,
num_accepted=0, num_rejected=15,
scheduled_spec_token_ids=[-1 x 15], generated_token_ids=[198].Decision:
248068 region seen in prior native verifier probes.Follow-up DFlash recovery flag tests:
Full preempt replacement recovery:
qwen36-dflash-k15-eager-tp2-recover-replacement-20260619a-20260619dflashrecover1.
Flags:
VLLM_XPU_SPEC_DECODE_SUPPRESS_REPLACEMENT=1,
VLLM_XPU_SPEC_DECODE_RECOVER_SUPPRESSED_REPLACEMENT=1,
VLLM_XPU_SPEC_DECODE_RESUME_AFTER_RECOVERY_PREEMPT=1,
VLLM_XPU_SPEC_DECODE_RECOVERY_FORCE_SINGLE_STEPS=16.
Result:
pass=false, accept rate 11.666666666666666%.
First mismatch moved earlier to output index 2; the candidate duplicated
token 440 (with) where baseline expected token 27044 ( dense).
Fixture:
/home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-recover-replacement-20260619a-20260619dflashrecover1-fixture.json.
No-preempt replacement suppression:
qwen36-dflash-k15-eager-tp2-no-preempt-replacement-20260619a-20260619dflashnopreempt1.
Flags:
VLLM_XPU_SPEC_DECODE_SUPPRESS_REPLACEMENT=1,
VLLM_XPU_SPEC_DECODE_RECOVER_SUPPRESSED_REPLACEMENT=1,
VLLM_XPU_SPEC_DECODE_NO_PREEMPT_SUPPRESSED_REPLACEMENT=1.
Result:
pass=false, accept rate 32.38095238095238%.
First mismatch at output index 4; the candidate repeated the accepted
prefix [440,27044,47193] before continuing.
Fixture:
/home/steve/llm-optimizations/data/qwen36-dflash-k15-eager-tp2-no-preempt-replacement-20260619a-20260619dflashnopreempt1-fixture.json.
Decision:
Additional DFlash follow-up on 2026-06-19:
scheduled_spec_decode_tokens in
update_draft_token_ids_in_output.
Run:
qwen36-dflash-k15-eager-tp2-no-preempt-replacement-20260619b-20260619dflashnopreempt2.
Result:
still pass=false, accept rate 32.38095238095238%, first mismatch at
output index 4, same repeated accepted prefix
[440,27044,47193]. I reverted the guard because it was ineffective.VLLM_XPU_SPEC_DECODE_REPLAY_SUPPRESSED_REPLACEMENT_ACCEPTED=1,
VLLM_XPU_SPEC_DECODE_EAGER_REPLACEMENT_RECOVERY=1,
VLLM_XPU_SPEC_DECODE_EAGER_ALL_RECOVERY_STEPS=1.
Run:
qwen36-dflash-k15-eager-tp2-replay-accepted-replacement-20260619a-20260619dflashreplayaccepted1.
Result:
endpoint failed before producing a candidate with
UR_RESULT_ERROR_DEVICE_LOST in rotary embedding after a scheduler output
still contained num_scheduled_tokens=16 and placeholder
scheduled_spec_decode_tokens=[-1 x 15] during a forced recovery phase.
Reject this flag stack as unstable./home/steve/llm-optimizations/data/qwen36-xpu-health-devices01-after-dflash-replayaccepted-scoped-20260619.log.3 unhealthy:
/home/steve/llm-optimizations/data/qwen36-xpu-health-devices01-after-dflash-replayaccepted-20260619.log.Updated decision:
Additional DFlash follow-up on 2026-06-19, later:
VLLM_XPU_SPEC_DECODE_REPLAY_ROW_STATEFUL_RECOVERY=1 for
VLLM_XPU_SPEC_DECODE_REPLAY_ROW_AFTER_TOKEN_IDS. Instead of calling
_preempt_request() and resetting num_computed_tokens to zero, it keeps
the request running, clears speculative IDs/placeholders, sets the replay
boundary to visible_tokens - 1, and forces single-token recovery steps.
Syntax gate passed:
/home/steve/.venvs/vllm-xpu/bin/python -m py_compile ....qwen36-dflash-k15-eager-tp2-stateful-replay-think-20260619b-20260619dflashstateful2
crashed with AsyncScheduler._update_request_with_output placeholder
underflow after the stateful row. The spec trace confirms the row normalized
to num_computed_tokens=521, num_output_placeholders=0, spec_len=0,
and did not preempt. This means async still needs a separate stale in-flight
scheduler-output transaction if this path is ever revived.qwen36-dflash-k15-eager-tp2-stateful-replay-think-sync-20260619a-20260619dflashstatefulsync1
completed but failed parity. First diff remained at output index 24
after <think>: baseline token 271 vs candidate token 198. Avoiding
the long prefill replay is not enough; the native packed verifier rows
before the recovery point already leave recurrent state non-identical.qwen36-dflash-k15-eager-tp2-serial-gdn-sync-20260619a-20260619dflashserial1
also failed parity, though it moved the first diff to output index 25.
Gate role was replacement_after_reject, accept rate was about 25.71%.
This shows DFlash is not a near-term promotion lane even with serial GDN;
it needs a larger verifier/replacement transaction fix, not another
scheduler-only replay knob.I ran a same-identity TP2 endpoint smoke to check whether the exact C++
qwen36_moe_w8a8_full_layerlet route-replay gain survives real endpoint
execution. This used devices 0,1 because physical device 3 is still not
TP4-safe.
Shared identity:
/mnt/fast-ai/llm-cache/hf/models--nameistoken--Qwen3.6-35B-A3B-Quark-W8A8-INT8/snapshots/cced56592e8c8935f8220836b4baa04dfd389118TP_SIZE=2, ONEAPI_DEVICE_SELECTOR=level_zero:0,1,
ZE_AFFINITY_MASK=0,1, QWEN36_XPU_PREFLIGHT=1.COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}'.XPU_GRAPH=1, VLLM_XPU_ENABLE_XPU_GRAPH=1,
VLLM_XPU_FORCE_GRAPH_WITH_COMM=1,
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=1.VLLM_XPU_GDN_NATIVE_FALLBACK=prefill,
VLLM_XPU_GDN_PREFILL_RECURRENT_FALLBACK=1,
VLLM_XPU_DISABLE_PREFILL_CUDAGRAPH_REPLAY=1,
VLLM_XPU_GREEDY_SAMPLE_TOPK_FALLBACK=1,
VLLM_XPU_INT8_MOE_MIXED_WORKSPACE=1.VLLM_EXTRA_ARGS='--uvicorn-log-level warning'.512/256 metrics run plus 8-repeat JSON/color
canaries; quality suite skipped.Control artifact:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-tp2-control-smoke-20260619a-summary-20260619tp2control1.json83.68628063307641 tok/s.11.948150581702066 ms/token.8/8 each.Full-layerlet artifact:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-full-layerlet-tp2-smoke-20260619a-summary-20260619fulltp2smoke1.jsonVLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1,
VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1.85.45754293668266 tok/s.11.702753504323482 ms/token.8/8 each.Decision:
+1.77 tok/s, +2.1%, or 0.245 ms/token.>150 tok/s goal, and it does not
justify a production promotion by itself.I ran decisive live decode timing on the same TP2 PIECEWISE identity used by the full-layerlet smoke above.
Control timing artifacts:
/home/steve/llm-optimizations/data/qwen36-prefill-safe-int8-tp2-control-decisive-20260619a-p512o256-20260619tp2timingctl1.json/home/steve/llm-optimizations/data/qwen36-prefill-safe-int8-tp2-control-decisive-20260619a-timing-decision-20260619tp2timingctl1.json84.30754286685192 tok/s.11.861692133606994 ms/token.Full-layerlet timing artifacts:
/home/steve/llm-optimizations/data/qwen36-prefill-safe-int8-full-layerlet-tp2-decisive-20260619a-p512o256-20260619tp2timingfull1.json/home/steve/llm-optimizations/data/qwen36-prefill-safe-int8-full-layerlet-tp2-decisive-20260619a-timing-decision-20260619tp2timingfull1.jsonVLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1,
VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1.84.4112340056149 tok/s.11.845327250739501 ms/token.Key timing result:
moe, but the dominant
live label is moe_forward_shared.custom_op, not the routed W8A8 layerlet.moe_forward_shared.custom_op max 8.841024 ms,
moe.shared_experts.apply_no_overlap max 5.772847 ms,
qwen2_moe.shared.silu_and_mul max 3.394437 ms,
xpu_moe.fused_moe_call max only 1.318158 ms.moe_forward_shared.custom_op max 8.720514 ms,
moe.shared_experts.apply_no_overlap max 5.635415 ms,
qwen2_moe.shared.silu_and_mul max 3.311749 ms,
xpu_moe.fused_moe_call max 1.342872 ms.Decision:
>150 tok/s.moe_forward_shared, so another routed expert micro-kernel is likely
to produce only sub-percent endpoint gains.VLLM_XPU_SHARED_EXPERTS_STREAM=1 + graph no-empty-cache was quality-clean
but slowed to 73.7749 tok/s; VLLM_XPU_SHARED_EXPERT_ACT_WORKSPACE=1
failed canaries under PIECEWISE; VLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT=1
was slower and/or replay-nondeterministic; VLLM_XPU_MOE_SHARED_ADD_ALLREDUCE_CUSTOM_OP=1
was quality-clean but slower.gate_up_proj, silu_and_mul, down_proj, expert_gate, and gate_mul
under the current INT8 path and tests exact fused/persistent variants
outside the endpoint before any canary run._out Is Not Graph-SafeI tested a narrower shared-expert optimization that reuses preallocated
activation-quantization outputs instead of letting
silu_and_mul_quant_int8_xpu allocate fresh tensors. The endpoint flag stack
was:
VLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT=1VLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT_OUT=1Microbench result:
_xpu_C.silu_and_mul_quant_int8_xpu_out is mathematically exact against
separate silu_and_mul plus INT8 quantization for the tested shared-expert
shapes.6-7 us versus the separate two-op path for
rows 1..32, d=512.Endpoint shallow smoke, TP2 PIECEWISE identity:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-fusedactquant-out-tp2-smoke-20260619a-summary-20260619175320.json85.47207810078443 tok/s.11.700355452376243 ms/token.8/8 each.Deep canary under normal PIECEWISE decode replay:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-fusedactquant-out-tp2-deep-canary-20260619a-summary-20260619175659.json31.{"answer":"42","unit":"widgets"}, got
{"answer":"12","unit":"widgets"}.96/96.Diagnostic replay bypass:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-fusedactquant-out-tp2-eager-everyreq-canary-20260619a-summary-20260619180050.jsonVLLM_XPU_DECODE_CUDAGRAPH_REPLAY_EAGER_EVERY_N_REQUESTS=1.96/96.Strong-output replay mitigation:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-fusedactquant-out-tp2-strong-output-canary-20260619a-summary-20260619180502.jsonVLLM_XPU_CUDAGRAPH_STRONG_OUTPUT=1.31 with the same 42 -> 12 mismatch.96/96.Decision:
VLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT_OUT=1 for endpoint use.The fastest exact path is still shared-expert structural work, but promotion requires deep canaries under normal PIECEWISE replay, not only eager replay.
I retested the current TP2 PIECEWISE forced/noop-comm graph lane after separating async scheduling from GDN/shared-expert graph replay issues.
Clean TP2/no-async identity:
TP_SIZE=2, devices 0,1.COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}'.XPU_GRAPH=1, VLLM_XPU_ENABLE_XPU_GRAPH=1,
VLLM_XPU_FORCE_GRAPH_WITH_COMM=1,
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=1.VLLM_XPU_GDN_NATIVE_FALLBACK=prefill.VLLM_XPU_GDN_PREFILL_RECURRENT_FALLBACK=1.VLLM_XPU_GREEDY_SAMPLE_TOPK_FALLBACK=1.VLLM_XPU_DISABLE_PREFILL_CUDAGRAPH_REPLAY=1.VLLM_XPU_INT8_MOE_MIXED_WORKSPACE=1.VLLM_EXTRA_ARGS='--no-async-scheduling --uvicorn-log-level warning'.Control results:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-tp2-control-noasync-canary-20260619a-summary-20260619prefillnoasynca.json/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-tp2-control-noasync-metrics-20260619a-summary-20260619prefillnoasyncmetricsa.json64/64.16/16.82.06149961234053 tok/s.12.19165415864154 ms/token.Async separation:
21 or 31 with the characteristic 42 -> 12
corruption.0, layers 0..2, and even full
decode,prefill GDN fallback did not fix the async failure.decode,prefill GDN fallback plus --no-async-scheduling passed
JSON 64/64 and color 16/16.Shared-expert fused _out retest under the clean no-async identity:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-fusedactquant-out-noasync-tp2-canary-20260619a-summary-20260619sharedoutnoasync1.json/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedexp-fusedactquant-out-noasync-tp2-metrics-20260619a-summary-20260619sharedoutnoasyncmetrics1.jsonVLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT=1,
VLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT_OUT=1.64/64.16/16.83.61311830066423 tok/s.11.960592332457054 ms/token.+1.5516186883237 tok/s, about +1.9% versus the clean baseline), but it
is far too small to explain a path to >150 tok/s by itself and still needs
full quality/reliability gates before production use.Routed full-layerlet retest under the clean no-async identity:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-full-layerlet-noasync-tp2-candidate-20260619a-summary-20260619fulllayerletnoasync1.jsonVLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1,
VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1.64/64.16/16.82.18196711720888 tok/s.12.17378463775276 ms/token.Updated immediate plan:
_out candidate only after any larger
candidate passes; it is not enough to chase alone.80 tok/s range
is too far from the >150 tok/s target without either healthy four-card
execution or a no-quality-loss multi-token path.Device/topology health:
3 still fails a trivial single-device allocation:
/home/steve/llm-optimizations/data/qwen36-xpu-health-device3-single-20260619195006.logRuntimeError: level_zero backend failed with error: 40 (UR_RESULT_ERROR_OUT_OF_RESOURCES).xpu-smi stats -d 3 reports only 34 MiB GPU memory used and
0% memory utilization, and xpu-smi ps -d 3 shows no model process./sys/bus/pci/devices/0000:47:00.0/reset, with reset methods flr bus,
but it is root-only and this shell does not have non-interactive sudo.0,1,2 pass fresh single-device and XCCL all-reduce health:
/home/steve/llm-optimizations/data/qwen36-xpu-health-devices012-fresh-20260619195121.loghidden_size=2048,
num_attention_heads=16, num_key_value_heads=2, num_experts=256,
num_experts_per_tok=8, and vocab_size=248320 are not divisible by 3.Conclusion:
3 health.0,1.Stacked routed+shared candidate:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedout-full-layerlet-noasync-tp2-candidate-20260619a-summary-20260619sharedoutfulllayerletnoasync1.jsonVLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT=1,
VLLM_XPU_SHARED_EXPERT_FUSED_ACT_QUANT_OUT=1,
VLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1,
VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1.64/64.16/16.83.64920085436258 tok/s.11.955750166180223 ms/token.Decision:
_out candidate (+0.036 tok/s over shared _out alone).gate_up_proj, activation/quantization, down_proj, expert_gate, and
final multiply/add. The endpoint timing says this is the live TP2 wall.Implemented a larger exact shared-expert W8A8 boundary:
/home/steve/src/vllm-xpu-kernels:
int8_gemm_w8a8_out.qwen36_shared_expert_w8a8_out.[hidden, 1] and live
ReplicatedLinear layout [1, hidden] without Python-side contiguous
transpose./home/steve/src/vllm/vllm/model_executor/models/qwen2_moe.py behind:
VLLM_XPU_SHARED_EXPERT_BOUNDARY_OUT=1./home/steve/llm-optimizations/scripts/bench-qwen36-shared-expert-replay.py.Build lesson:
pip install -e . --no-build-isolation for every
_xpu_C kernel iteration; it rebuilds broad attention targets first and
took too long.cmake --build /home/steve/src/vllm-xpu-kernels/build/temp -j=8 --target=_xpu_C,
then copy _xpu_C.abi3.so and sibling .so files into
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/.Offline replay:
/home/steve/llm-optimizations/data/qwen36-shared-expert-replay-cppbound-smoke-20260619.md/home/steve/llm-optimizations/data/qwen36-shared-expert-replay-cppbound-tp2-20260619.mdmax_abs_diff = 0 for rows 1,2,4,8,16,32.1: 216.180 us vs baseline 264.540 us.2: 206.868 us vs baseline 250.995 us.4: 260.857 us vs baseline 247.807 us (slower).8: 273.691 us vs baseline 248.311 us (slower).16: 208.777 us vs baseline 283.551 us.32: 215.368 us vs baseline 311.497 us.Endpoint canary, same clean TP2/no-async identity:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedboundary-out-noasync-tp2-canary-20260619a-summary-20260619sharedboundarycanary1.jsonVLLM_XPU_SHARED_EXPERT_BOUNDARY_OUT=1.64/64.16/16.Endpoint metrics, same clean TP2/no-async identity:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedboundary-out-noasync-tp2-metrics-20260619a-summary-20260619sharedboundarymetrics1.json83.6714304189521 tok/s.11.952609020454474 ms/token.Decision:
+1.61 tok/s over the clean TP2/no-async baseline and
essentially tied with the previous shared _out candidate.>150 tok/s target. The next larger work
must attack routed MoE fixed costs, collectives/topology, or exact
speculation parity.Decisive timing after the shared-expert C++ boundary:
/home/steve/llm-optimizations/data/qwen36-sharedboundary-noasync-tp2-decisive-20260619a-run-summary-20260619sharedboundarytiming1.json/home/steve/llm-optimizations/data/qwen36-sharedboundary-noasync-tp2-decisive-20260619a-timing-decision-20260619sharedboundarytiming1.mdVLLM_XPU_SHARED_EXPERT_BOUNDARY_OUT=1.84.10955623702061 tok/s.gpu_model_runner.model_forward: mean 5.363407 ms.gpu_model_runner.bookkeeping_sync: mean 4.679359 ms.gdn_attention_core_xpu.native: mean 1.506388 ms.qwen2_moe.shared.boundary_int8_cpp: mean 1.548210 ms.Interpretation:
160 us layerlet across the route set.Runner hygiene:
scripts/run-qwen36-ablation-candidate.sh and
scripts/run-qwen36-decisive-timing.sh so future summaries record the newer
W8A8 route flags, DPAS gather flags, workspace-atomic flag, route-GEMM1
tile flag, shared-expert boundary flag, VLLM_EXTRA_ARGS, and
GPU_MEMORY_UTILIZATION.bash -n passed for both scripts.Next active plan:
~82-84 tok/s.>150 tok/s.
k=1 must be token-identical to no-spec on the same
no-async/PIECEWISE identity.3 XPU/XCCL health is repaired externally.
TP3 is incompatible with this model, and TP2 is the only healthy exact
topology available in this shell.Implemented an opt-in C++/SYCL epilogue inside the existing shared-expert W8A8 boundary:
/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cppVLLM_XPU_SHARED_EXPERT_FUSED_GATE_MUL=1VLLM_XPU_SHARED_EXPERT_BOUNDARY_OUT=1/home/steve/llm-optimizations/scripts/run-qwen36-ablation-candidate.shImplementation details:
matmul(hidden_states, expert_gate_w) -> sigmoid -> multiply down_out.down_out.Build/install:
JOBS=4 /home/steve/llm-optimizations/scripts/build-vllm-xpu-kernels-xpu-c-only.sh
cp /tmp/vllm-xpu-xpu-c-only-2025/vllm_xpu_kernels/_xpu_C.abi3.so \
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/_xpu_C.abi3.so
Import sanity:
PYTHONPATH=/home/steve/src/vllm:/home/steve/src/vllm-xpu-kernels \
LD_LIBRARY_PATH=/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels:/home/steve/.venvs/vllm-xpu/lib:/home/steve/.venvs/vllm-xpu/lib/python3.12/site-packages/torch/lib \
/home/steve/.venvs/vllm-xpu/bin/python -c 'import torch, vllm_xpu_kernels._xpu_C; print(hasattr(torch.ops._xpu_C, "qwen36_shared_expert_w8a8_out"))'
Validation identity:
TP_SIZE=2
ONEAPI_DEVICE_SELECTOR=level_zero:0,1
ZE_AFFINITY_MASK=0,1
QWEN36_XPU_PREFLIGHT=0
COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}'
XPU_GRAPH=1
VLLM_XPU_ENABLE_XPU_GRAPH=1
VLLM_XPU_FORCE_GRAPH_WITH_COMM=1
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=1
VLLM_XPU_GDN_NATIVE_FALLBACK=prefill
VLLM_EXTRA_ARGS='--no-async-scheduling --uvicorn-log-level warning'
VLLM_XPU_SHARED_EXPERT_BOUNDARY_OUT=1
VLLM_XPU_SHARED_EXPERT_FUSED_GATE_MUL=1
GPU_MEMORY_UTILIZATION=0.90
Quality/canary artifacts:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedgate-mul-noasync-tp2-canary-summary-20260619sharedgatemulcanary1.json64/64.16/16./home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedgate-mul-noasync-tp2-quality-summary-20260619sharedgatemulquality1.jsonpass_all=true, baseline_match_all=true,
exact arithmetic/copy/json cases passed, repeat passed, long-context passed.Metrics artifact:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-sharedgate-mul-noasync-tp2-metrics-summary-20260619sharedgatemulmetrics1.json84.9100565680358 tok/s.11.777681041849064 ms/token.269.54865898005664 ms.Comparable TP2/no-async controls:
82.06149961234053 tok/s, 12.19165415864154 ms/token.83.6714304189521 tok/s, 11.952609020454474 ms/token.84.9100565680358 tok/s, 11.777681041849064 ms/token.Decision:
+2.85 tok/s over the plain TP2/no-async control and
+1.24 tok/s over the shared-boundary-only endpoint.>150 tok/s goal.Next best branches after this increment:
>150 tok/s.N=8 eager workaround is correct but not faster. Treat this as a
correctness branch, not a speed branch, unless a new replay ownership fix
appears.3/XCCL remains a blocker. Do not interpret TP4 endpoint speed
until the preflight passes cleanly.Important correction:
VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1 alone.VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET=1.VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_ALLOW_CAPTURE=1.VLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1.Corrected endpoint identity tested:
TP_SIZE=2
ONEAPI_DEVICE_SELECTOR=level_zero:0,1
ZE_AFFINITY_MASK=0,1
QWEN36_XPU_PREFLIGHT=0
COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}'
XPU_GRAPH=1
VLLM_XPU_ENABLE_XPU_GRAPH=1
VLLM_XPU_FORCE_GRAPH_WITH_COMM=1
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=1
VLLM_XPU_GDN_NATIVE_FALLBACK=prefill
VLLM_EXTRA_ARGS='--no-async-scheduling --uvicorn-log-level warning'
GPU_MEMORY_UTILIZATION=0.90
VLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET=1
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_ALLOW_CAPTURE=1
VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1
VLLM_XPU_MOE_W8A8_FUSED_Q1=1
VLLM_XPU_MOE_W8A8_ROUTE_GEMM1=1
VLLM_XPU_SHARED_EXPERT_BOUNDARY_OUT=1
VLLM_XPU_SHARED_EXPERT_FUSED_GATE_MUL=1
Operational lesson:
VLLM::EngineCore and VLLM::Worker_TP*
processes alive, holding XPU memory.Corrected run artifact:
/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-q1-routefull-sharedgate-real-noasync-tp2-summary-20260619q1routefullshared3.json/home/steve/llm-optimizations/data/qwen36-ablation-prefill-safe-int8-q1-routefull-sharedgate-real-noasync-tp2-20260619q1routefullshared3.log24/24.24/24.82.4576360185309 tok/s.12.135906462845014 ms/token.268.7872141832486 ms.Decision:
84.9100565680358 tok/s) and only about equal to the plain
TP2/no-async control.Current installed XPU extension state after the latest rebuild:
_xpu_C.abi3.so SHA256:
6201eb3b6088344f69faf215815bf34172d1f0a3b2454f3a2a764cfc4dff9b75128 local-size direct GEMM2/gather binary was reverted.qwen36_topk_ids_to_active_expert_ids_int32_out,
cutlass_grouped_gemm_w8a8_int8_active_offsets_interface,
qwen36_moe_w8a8_full_layerlet, and
qwen36_shared_expert_w8a8_out.Sorted active-helper replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-active-offset-runtime-sortedactive-routes7-device0-20260619activepath5.json/home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-routecapture6-routes-rank0-20260611.jsonl,
route starts 0,40,80,85,95,100,115, device 0,
VLLM_XPU_INT8_MOE_ACTIVE_OFFSET_GEMM=1,
VLLM_XPU_MOE_W8A8_FUSED_Q1=1,
VLLM_XPU_MOE_W8A8_ROUTE_GEMM1=1.0.0 max diff against
the XPU fused-MoE oracle on all checked routes.rows_ready_for_endpoint_gate=0/7,
worst_best_exact_nonreference_us_mean=222.65256,
target 160 us.147.62-175.06 us, but
full prologue-inclusive host means remained around 202.87-223.79 us.Q1 + direct GEMM2/gather full-layerlet:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-q1direct-routes7-device0-20260619.json0,
VLLM_XPU_MOE_W8A8_FUSED_Q1=1,
VLLM_XPU_MOE_W8A8_DIRECT_GEMM2_GATHER=1.full_layerlet_vs_xpu_fused_moe_max_abs_diff=0.0 and
full_layerlet_vs_rows_oracle_max_abs_diff=0.0 on all seven routes.193.65164, 169.30628, 181.80136, 162.99816, 188.18748, 175.80628,
173.56040 us.rows_ready_for_endpoint_gate=0/7,
worst_best_exact_nonreference_us_mean=193.65164,
target 160 us.Direct GEMM2/gather local-size 128 diagnostic:
/home/steve/llm-optimizations/data/qwen36-int8-moe-full-layerlet-q1direct128-routes7-device0-20260619.json0.0 max diff on all seven routes.rows_ready_for_endpoint_gate=0/7,
worst_best_exact_nonreference_us_mean=193.2268,
target 160 us.174.18024, 189.54988, 184.99260, 182.28184, 184.12160, 187.88224,
193.22680 us.6201eb... SHA above.Reviewed /home/steve/suggestions.md again:
3 health is restored and the full
benchmark identity matches.Current conclusion:
~160-180 us layerlet band with exact parity.Harness update:
scripts/bench-qwen36-int8-moe-kernels.py now has an explicit
--enable-onednn-sidecar diagnostic path.qwen36_moe_onednn_sidecar_probe, and then runs the normal weighted gather.remapped_hidden_states, but the
C++ diagnostic validates it as the int8 GEMM1 input buffer; the Python wrapper
now passes gemm1_a there and leaves the regular BF16 remap path unchanged.Artifacts:
23 smoke:
/home/steve/llm-optimizations/data/qwen36-int8-moe-onednn-sidecar-smoke-device0-20260619.json33 smoke:
/home/steve/llm-optimizations/data/qwen36-int8-moe-onednn-sidecar-mode33-smoke-device0-20260619.json33 replay:
/home/steve/llm-optimizations/data/qwen36-int8-moe-onednn-sidecar-mode33-routes7-device0-20260619.json/home/steve/llm-optimizations/data/qwen36-int8-moe-onednn-sidecar-mode33-routes7-device0-20260619.mdSeven-route identity:
/home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-routecapture6-routes-rank0-20260611.jsonl0,40,80,85,95,100,115ONEAPI_DEVICE_SELECTOR=level_zero:0, ZE_AFFINITY_MASK=0480/2033--real-routing-oracle enabledResults:
0.0 max abs diff versus xpu_fused_moe
and 0.0 max abs diff versus the rows-per-expert oracle on all seven routes.rows_ready_for_endpoint_gate=0/7,
status=exact_nonreference_candidates_exist_but_gate_not_met,
target 160 us.full_layerlet.
Best route: 162.21985 us; worst route: 174.2182 us.299.754, 314.4245, 303.52465, 305.75675, 306.969, 321.3886,
319.8897 us.45.575, 47.725, 45.0625, 45.45, 45.975, 48.5125, 48.9625 us.Decision:
~45-49 us internal middle-wall number as a diagnostic hint only.
It says oneDNN’s cached GEMM middle can be fast in isolation, but the current
boundary does not remove enough fixed route/prologue/quant/gather overhead.Next best branch:
Diagnostic implementation:
VLLM_XPU_MOE_W8A8_UNCHECKED_FULL_LAYERLET=1 in
/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cpp.qwen36_moe_w8a8_full_layerlet._xpu_C.abi3.so with SHA:
c585bbb150720ff5d210205004f56f8e200f9d1f164192a042e329a8c5b22d06.Artifacts:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-unchecked-q1-routegemm1-smoke-route80-device0-20260620.json/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-checked-q1-routegemm1-smoke-route80-device0-20260620.jsonIdentity:
ONEAPI_DEVICE_SELECTOR=level_zero:0, ZE_AFFINITY_MASK=0/home/steve/llm-optimizations/data/qwen36-quark-int8-tp4-routecapture6-routes-rank0-20260611.jsonl804VLLM_XPU_MOE_W8A8_FUSED_Q1=1VLLM_XPU_MOE_W8A8_ROUTE_GEMM1=1Results:
full_layerlet_vs_xpu_fused_moe_max_abs_diff=0.0,
full_layerlet_vs_rows_oracle_max_abs_diff=0.0.full_layerlet_vs_xpu_fused_moe_max_abs_diff=0.0,
full_layerlet_vs_rows_oracle_max_abs_diff=0.0.204.1338 us.167.8508 us.160 us prologue-inclusive promotion target.Decision:
Next branch:
Root cause fixed:
/home/steve/src/vllm-xpu-kernels/csrc/xpu/grouped_gemm/xe_2/grouped_gemm_xe2.hpp
used the wrong B tensor layout.MoEGEMMLauncherOffsets<'R','R',...>, but internally flips B to
column-major (actual_layout_of_B='C').'C' made routed GEMM1 exact.Direct GEMM1 proof:
/home/steve/llm-optimizations/data/qwen36-route-gemm1-direct-compare-route80-20260620.json
had overall_max_abs_diff=16.75./home/steve/llm-optimizations/data/qwen36-route-gemm1-direct-compare-route80-blayoutfix-20260620.json
had overall_max_abs_diff=0.0./home/steve/llm-optimizations/data/qwen36-route-gemm1-direct-compare-route80-blayoutfix-handbody-20260620.json
also had overall_max_abs_diff=0.0.Replay results:
/home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-blayoutfix-handbody-q1-routegemm1-smoke-route80-device0-20260620.json
exact, full_layerlet_total_us_mean=138.69128./home/steve/llm-optimizations/data/qwen36-int8-moe-layerlet-blayoutfix-handbody-q1-routegemm1-routes7-device0-20260620.json
all exact. Route means:
0=139.78, 40=141.45, 80=139.00, 85=136.67,
95=138.02, 100=160.80, 115=148.58 us.143.38 us, but the seven
route set still had a 165.67 us worst row. Do not promote MTILE=8 yet.Endpoint A/B results:
/home/steve/llm-optimizations/data/qwen36-ablation-tp2-control-blayoutfix-summary-20260620tp2ctlblayout1.json
passed JSON/color canaries, 13.95497 tok/s./home/steve/llm-optimizations/data/qwen36-ablation-tp2-fulllayerlet-q1-routegemm1-blayoutfix-summary-20260620tp2fulllayerlet1.json
passed JSON/color canaries, 13.89789 tok/s./home/steve/llm-optimizations/data/qwen36-ablation-tp2-piecewise-control-blayoutfix-summary-20260620tp2piecewisectl1.json
passed JSON/color canaries, 83.56112 tok/s./home/steve/llm-optimizations/data/qwen36-ablation-tp2-piecewise-fulllayerlet-q1-routegemm1-blayoutfix-summary-20260620tp2piecewisefulllayerlet1.json
passed JSON/color canaries, 82.32790 tok/s.Important correction:
/home/steve/src/vllm-xpu-kernels/csrc/xpu/moe_layerlet.cpp:
VLLM_XPU_MOE_W8A8_FULL_LAYERLET_TRACE_FILE./home/steve/llm-optimizations/data/qwen36-ablation-tp2-piecewise-fulllayerlet-trace-blayoutfix-summary-20260620tp2piecewisefulllayerlettrace1.json
produced no C++ trace file, meaning qwen36_moe_w8a8_full_layerlet was not
entered.VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET=1 and
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_ALLOW_CAPTURE=1
also produced no C++ trace file:
/home/steve/llm-optimizations/data/qwen36-ablation-tp2-piecewise-fulllayerlet-offset-trace-blayoutfix-summary-20260620tp2piecewisefulllayerlettraceoffset1.json.Interrupted loose end:
/home/steve/src/vllm-xpu-kernels/vllm_xpu_kernels/fused_moe_interface.py:
VLLM_XPU_MOE_W8A8_FULL_LAYERLET_GATE_TRACE_FILE.tp2-piecewise-fulllayerlet-gatetrace-blayoutfix,
stamp 20260620tp2piecewisefulllayerletgatetrace1./home/steve/llm-optimizations/data/qwen36-full-layerlet-gate-trace-tp2-piecewise-20260620.jsonl
or
/home/steve/llm-optimizations/data/qwen36-full-layerlet-trace-tp2-piecewise-gatereason-20260620.jsonl
before the stop.Current active library hashes after trace instrumentation:
_xpu_C.abi3.so:
1797dca305004d09fe89d76f071766b290ecb356adf188faf1b2931a9742fa33libgrouped_gemm_xe_2.so:
d7ac20974b96f7429350ddb0319384386cf2433442bdda1e878537eca60b0be1Saved patches:
/home/steve/llm-optimizations/patches/vllm-xpu-kernels-qwen36-routegemm1-blayoutfix-20260620.patch/home/steve/llm-optimizations/patches/llm-optimizations-qwen36-routegemm1-blayoutfix-results-20260620.patchImmediate next command to resume:
TRACE=/home/steve/llm-optimizations/data/qwen36-full-layerlet-trace-tp2-piecewise-gatereason-20260620.jsonl
GATE=/home/steve/llm-optimizations/data/qwen36-full-layerlet-gate-trace-tp2-piecewise-20260620.jsonl
rm -f "$TRACE" "$GATE"
STAMP=20260620tp2piecewisefulllayerletgatetrace2 \
PORT=18184 \
TP_SIZE=2 \
ONEAPI_DEVICE_SELECTOR=level_zero:0,1 \
ZE_AFFINITY_MASK=0,1 \
QWEN36_XPU_PREFLIGHT=0 \
METRICS_REPEATS=1 \
METRICS_PROMPT_TOKENS=128 \
METRICS_OUTPUT_TOKENS=64 \
METRICS_WARMUP_OUTPUT_TOKENS=16 \
ABLATION_SKIP_CANARIES=1 \
ABLATION_RUN_QUALITY=0 \
ABLATION_SKIP_METRICS=0 \
VLLM_XPU_GDN_NATIVE_FALLBACK=prefill \
XPU_GRAPH=1 \
VLLM_XPU_ENABLE_XPU_GRAPH=1 \
VLLM_XPU_FORCE_GRAPH_WITH_COMM=1 \
VLLM_XPU_GRAPH_NOOP_COMM_CAPTURE=1 \
COMPILATION_CONFIG='{"cudagraph_mode":"PIECEWISE"}' \
VLLM_XPU_W8A8_EXPERIMENTAL_ALLOW=1 \
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET=1 \
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_ALLOW_CAPTURE=1 \
VLLM_XPU_MOE_W8A8_FULL_LAYERLET=1 \
VLLM_XPU_MOE_W8A8_FUSED_Q1=1 \
VLLM_XPU_MOE_W8A8_ROUTE_GEMM1=1 \
VLLM_XPU_MOE_W8A8_FULL_LAYERLET_TRACE_FILE="$TRACE" \
VLLM_XPU_MOE_W8A8_FULL_LAYERLET_TRACE_MAX_LINES=512 \
VLLM_XPU_MOE_W8A8_FULL_LAYERLET_GATE_TRACE_FILE="$GATE" \
VLLM_XPU_MOE_W8A8_FULL_LAYERLET_GATE_TRACE_MAX_LINES=512 \
/home/steve/llm-optimizations/scripts/run-qwen36-ablation-candidate.sh \
tp2-piecewise-fulllayerlet-gatetrace-blayoutfix
Decision tree when resuming:
num_rows != 1, the PIECEWISE bucket is not exercising the
single-token layerlet path; add a compatible decode-only gate or layerlet
variant for that shape.stream_capture_active blocks the fused offset gate, keep
VLLM_XPU_INT8_MOE_FUSED_PROLOGUE_OFFSET_ALLOW_CAPTURE=1 and find the next
false prerequisite.qwen36_moe_w8a8_full_layerlet should a full canary-clean endpoint A/B be
treated as a real layerlet result.The gate trace follow-up produced two useful artifacts:
/home/steve/llm-optimizations/data/qwen36-ablation-tp2-piecewise-fulllayerlet-gatetrace-blayoutfix-summary-20260621tp2piecewisefulllayerletgatetrace1.json
and
/home/steve/llm-optimizations/data/qwen36-full-layerlet-gate-trace-tp2-piecewise-20260621.jsonl./home/steve/llm-optimizations/data/qwen36-ablation-tp2-graphnone-fulllayerlet-mixedws-gatetrace-summary-20260621tp2graphnonefulllayerletmixedws1.json
and
/home/steve/llm-optimizations/data/qwen36-full-layerlet-gate-trace-tp2-graphnone-mixedws-20260621.jsonl.The PIECEWISE trace repeatedly had no scratch keys or W8A8 offset scratch and
never enabled the full-layerlet op (use_w8a8_full_layerlet=false). The
graph-none mixed-workspace run had scratch keys, W8A8 offsets, and finally
num_rows=1 with prologue_workspace=true and
use_w8a8_full_layerlet=true.
Interpretation: