GLM-5.2 QuantTrio on 4x ASUS GX10 — stable baseline and failure notes
Date: 2026-07-14
Hardware: 4x ASUS Ascent GX10 / NVIDIA GB10, 128 GiB unified memory each, ConnectX-7 200 GbE fabric
Container image: vllm-node-tf5-glm52-b12x:probe-modded-cutlass453
vLLM: 0.23.1rc1.dev190+gab6660699.d20260713
Model: local GLM-5.2 QuantTrio mixed int4/int8 weights at /cache/huggingface/hub/glm52-int4-int8mix
Serving name: glm-5.2
Working launch: TP=4, eager mode, fp8_ds_mla KV, 160k context, no MTP, no async scheduling
API: http://<head-node>:8210
Field notes
This one did not match the launch guide cleanly. The model can be made useful on the 4-node GX10 cluster, but the advertised high-context + MTP path is not yet a drop-in recipe on this stack.
The stable baseline that actually came up was deliberately conservative. The first benchmarked run used 128k:
--tensor-parallel-size 4
--max-model-len 128000
--max-num-seqs 6
--max-num-batched-tokens 8192
--kv-cache-dtype fp8_ds_mla
--kv-cache-memory-bytes 7000000000
--enforce-eager
The launch also required the local RoCE fabric settings, including
NCCL_IB_HCA=rocep1s0f1, NCCL_SOCKET_IFNAME=enp1s0f1np1, and
NCCL_IB_GID_INDEX=2. The GID index is cluster-specific; the public examples
using 3 were not correct here.
The successful boot reported:
GPU KV cache size: 129,728 tokens
DSA indexer decode path: use_flattening=False (next_n=1, use_fp4_indexer_cache=False)
init engine (profile, create kv cache, warmup model) took 22.00 s
Application startup complete.
After benchmarking, the same no-MTP/no-async shape was relaunched at 160k and served successfully:
GPU KV cache size: 161,280 tokens
DSA indexer decode path: use_flattening=False (next_n=1, use_fp4_indexer_cache=False)
Application startup complete.
That 160k service is the current operational baseline. A 200k no-MTP/no-async retry still did not finish serving, even with extra temporary swap available.
What failed
The original MTP-heavy path is the dangerous one. With MTP num_speculative_tokens=4,
vLLM derived next_n=5 and entered the flattened DSA indexer path:
DSA indexer decode path: use_flattening=True (next_n=5, use_fp4_indexer_cache=False)
That path repeatedly wedged one GX10 hard enough that SSH banner exchange would time out until the unit was reset.
Reducing MTP to one speculative token avoided the flattened path in theory, but the run still stalled during KV/indexer setup. Removing MTP entirely at 200k context got farther:
GPU KV cache size: 202,944 tokens
DSA indexer decode path: use_flattening=False (next_n=1, use_fp4_indexer_cache=False)
No available shared memory broadcast block found in 60 seconds.
That wait repeated indefinitely. The service never bound the API. During the stable 128k run, the nodes were already tight on host memory: around 114-116 GiB used, only 3-7 GiB available, and a 10 GiB swapfile per node with several GiB in use on three ranks. Larger-context investigation should start by separating host-memory/swap pressure from vLLM indexer behavior.
Benchmarks
Sampling: temperature=0.0. The concurrency sweep uses a 10,086-token prompt
and requests 1,024 output tokens. This run is no-MTP and eager, so decode is much
slower than the MTP-enabled Qwen3.5 baseline, but it is stable.
| N | Wall (s) | Agg prefill (t/s) | Agg decode (t/s) | Median TTFT (s) | Median per-req decode (t/s) |
|---|---|---|---|---|---|
| 1 | 83.5 | 23,731 | 12.3 | 0.43 | 12.3 |
| 2 | 87.5 | 23,862 | 23.5 | 0.57 | 11.8 |
| 4 | 105.0 | 43,985 | 39.1 | 0.92 | 9.8 |
| 8 | 211.2 | 668 | 38.8 | 1.03 | 8.6 |
| 16 | 347.2 | 650 | 47.2 | 122.99 | 8.4 |
The N=8 and N=16 prefill numbers include queueing because this stable launch
uses --max-num-seqs 6. Once more than six requests are in flight, TTFT comes
in waves and aggregate prefill is no longer a pure prefill measurement.
Needle-in-a-haystack
The first raw completions NIAH attempt at 126,579 input tokens returned only an empty/EOS token, so it was not a useful retrieval result. Re-running through the chat endpoint with more headroom under the 128k limit succeeded.
Needle:
IMPORTANT: The secret access code for Project Aurora is BANANA-MOOSE-7421. Remember this exactly.
Question:
Based on the text above, what is the secret access code for Project Aurora? Reply with just the code itself, nothing else.
| Metric | Value |
|---|---|
| Input tokens | 120,030 |
| TTFT / prefill | 230.7 s |
| Prefill speed | 520 t/s |
| Output tokens | 100 |
| Decode speed | 11.7 t/s |
| Needle retrieved | yes, exact match |
Final visible answer:
BANANA-MOOSE-7421
The model produced a short reasoning trace before the visible answer, but the answer itself was exact.
Next investigation
The main unresolved feature is larger context. The next runs should walk upward from the stable baseline instead of jumping back to the full recipe:
- 128k baseline with larger swap and the same no-MTP/no-async config.
- 160k no-MTP/no-async, keeping
use_flattening=False. - 200k no-MTP/no-async after host memory pressure is removed.
- Only then reintroduce async scheduling.
- Only after the 200k no-MTP path is stable, retry MTP with
num_speculative_tokens=1.
Do not retry MTP k=4 on this stack until the flattened DSA path is patched or proven safe on GB10.
DCP fork investigation
The newer XanuNetworks DCP repo is not just a better launch script for the same image. It depends on a different vLLM fork stack:
local-inference-lab/vllmbranchcodex/dcp-globaltopk-sharddraft-defaults-20260622ate232d262369b8c918cf478a7a96a0fcf8127cf65- CosmicRaisins PR72 patches for draft-under-DCP plus the draft quant packed mapping patch
- b12x
9cd63a7 B12X_MLA_SPARSE,VLLM_USE_V2_MODEL_RUNNER=1,VLLM_DCP_GLOBAL_TOPK=1, andVLLM_DCP_SHARD_DRAFT=1- explicit
index_topk_patternandclear_thinking:false - DCP2/DCP4 KV sharding, which is how the repo advertises 327k/655k context
That is the primary reason this cluster could not reproduce the Xanu numbers from the earlier non-DCP image. The current image is a different vLLM commit and does not contain the DCP machinery.
The DCP audit found several cluster-specific blockers before any launch attempt:
| Item | Xanu repo assumption | This cluster |
|---|---|---|
| active rails | f0 dual rail |
f1 dual rail: enp1s0f1np1, enP2p1s0f1np1 |
| DCP image | vllm-node-eldritch-dcp:e232d26-modded present |
missing on all nodes |
| staged entrypoint/patch | present under $HOME/vllm and $HOME/glm-dcp2-patches |
not staged yet |
| driver | validated on 580.159.03 |
mostly 580.126.09, one node 580.82.09 |
| RoCE GID | auto-detected | should be auto-detected; active rails expose RoCEv2 IPv4 at index 3 |
The patch story also needed care. Each patch applies individually to the pinned
fork, but the full set does not apply blindly because Xanu's
b12x-prewarm-tolerance.patch duplicates a topk_scores_buffer hunk already
provided by CosmicRaisins' pr72-2 patch. The safe sequence is:
pr72-1-draft-dcp-config-propagation.patch
pr72-2-glm-dcp-draft-path.patch
draft-quant-packed-mapping.patch
xanu-b12x-prewarm-only.patch
The cleaned xanu-b12x-prewarm-only.patch keeps only the prewarm try/except
logic and validates cleanly after the three CosmicRaisins patches. A guarded DCP
build harness and dry-run launch adaptation now live outside the public blog
tree under dcp-audit/; they refuse to build on x86 and refuse to launch while
the current 160k service is running.
2026-07-14 DCP build and launch attempts
The DCP image build completed successfully on the head GX10:
image: vllm-node-eldritch-dcp:e232d26-modded
vLLM: 0.23.1rc1.dev248+ge232d2623.d20260714
vLLM commit: e232d262369b8c918cf478a7a96a0fcf8127cf65
FlashInfer commit: a92fce7a
torch: 2.11.0+cu130
nvidia-cutlass-dsl: 4.6.0
nvidia-cutlass-dsl-libs-cu13: 4.6.0
b12x: 0.23.0 from lukealonso/b12x@9cd63a7
build time: 47m25s
The build warnings were not fatal, but are worth tracking: DeepGEMM and FlashMLA
reported unsupported CUDA arch 12.0, and the final image uses the DCP fork with
FlashAttention and vLLM CUDA objects compiled for 12.1a. The image was copied
to all three workers with docker save | docker load; no old baseline containers
were removed.
The first real DCP2 launch used the repo's 327,680-token intent:
--max-model-len 327680
--decode-context-parallel-size 2
--gpu-memory-utilization 0.89
--attention-backend B12X_MLA_SPARSE
--speculative-config {"method":"mtp","num_speculative_tokens":4,...}
That run did not wedge the cluster. It loaded all 128 shards in 334.66 seconds, compiled, profiled, and then failed cleanly at KV allocation:
Available KV cache memory: 7.52 GiB
327680 tokens need 8.35 GiB
estimated maximum model length: 294912
A second 327,680-token attempt with --gpu-memory-utilization 0.90 also failed
cleanly. The log first reported Available KV cache memory: 9.19 GiB, but the
final allocation check still failed against an effective 7.22 GiB minimum and
estimated only 283,392 tokens. That mismatch suggests the limiting rank or DCP
KV layout check is not the same value printed by the head worker's early
profiling line.
A conservative retry at 262,144 tokens with the same DCP/MTP stack and
--gpu-memory-utilization 0.90 came up successfully on port 8211:
max_model_len: 262144
decode_context_parallel_size: 2
Available KV cache memory: 9.14 GiB
GPU KV cache size: 274,304 tokens
Maximum concurrency for 262,144 tokens per request: 1.05x
init engine (profile, create kv cache, warmup model) took 92.37 s
Application startup complete.
The full launch loaded the 128 main shards in 303.82 seconds, loaded the
filtered four-shard draft path in 8.24 seconds, and reported total model loading
of 96.81 GiB in 329.88 seconds. This run used DCP2, B12X sparse MLA, async
scheduling, and MTP with num_speculative_tokens=4. The API advertises the
served model as glm-5.2 with max_model_len 262,144.
This suggests the failed 327,680-token recipe was mostly a KV headroom issue on this hardware, not a general DCP boot failure. The remaining gap to the public 327k/655k claims is finding where the extra KV budget comes from: lower memory fragmentation, a different rank layout, a different driver/runtime envelope, or another launch flag in the upstream environment.
2026-07-21 larger-context cleanup pass
After disabling unused desktop and appliance services on the Spark nodes including Bluetooth, CUPS, Avahi, snapd, ModemManager, display-manager/gdm, and other GUI-adjacent daemons, the cluster still did not have enough KV headroom for the full DCP2 target. The useful result was a higher stable ceiling than the previous 262k service:
| Requested context | Result |
|---|---|
| 294,912 | did not reach API; rank 0 stalled during load while workers waited |
| 283,392 | clean KV allocation failure; needed 7.22 GiB, effective available 7.03 GiB |
| 275,840 | served successfully |
The working 275,840-token launch kept the same DCP2/MTP shape:
max_model_len: 275840
decode_context_parallel_size: 2
gpu_memory_utilization: 0.90
kv_cache_dtype: fp8_ds_mla
MTP speculative tokens: 4
GPU KV cache size: 277,120 tokens
Maximum concurrency for 275,840 tokens per request: 1.00x
Application startup complete.
The API smoke test returned normally. Runtime memory remains tight: three nodes
kept a few GiB of swap allocated from launch-time pressure, but the extra 64 GiB
swap files were unused and short idle vmstat checks did not show sustained
swap-in/swap-out churn. This is still not a comfortable production margin; it is
a practical larger-context bring-up point for further testing.
DCP2 262k benchmark pass
The classic 10k concurrency sweep was rerun against the DCP2 service on port 8211. This run uses async scheduling, B12X sparse MLA, DCP2, and MTP with four speculative tokens.
| N | Wall (s) | Agg prefill (t/s) | Agg decode (t/s) | Median TTFT (s) | Median per-req decode (t/s) |
|---|---|---|---|---|---|
| 1 | 55.5 | 625 | 26.0 | 16.14 | 26.0 |
| 2 | 84.0 | 489 | 24.6 | 21.09 | 24.9 |
| 4 | 163.0 | 327 | 25.2 | 63.46 | 25.8 |
| 8 | 347.3 | 262 | 23.6 | 152.16 | 25.9 |
| 16 | 635.7 | 272 | 25.8 | 296.76 | 26.7 |
The DCP/MTP path roughly doubles single-request decode versus the old eager no-MTP baseline, but it did not increase aggregate decode with concurrency in this harness. Requests are served in visible waves, so TTFT grows quickly at higher concurrency.
Needle-in-a-haystack was also rerun near the new context ceiling:
| Metric | Value |
|---|---|
| Input tokens | 240,074 |
| TTFT / prefill | 383.8 s |
| Prefill speed | 626 t/s |
| Output tokens | 25 |
| Decode speed | 28.8 t/s |
| Needle retrieved | yes, exact match |
Final answer contained the exact code, with extra preamble:
If the text does not contain the code, reply with "NOT FOUND".
BANANA-MOOSE-7421
Runtime swap behavior was acceptable but not literally zero-touch. Three nodes
kept swap pages allocated from launch-time pressure: about 3.8 GiB on the head
and about 2.0 GiB on two workers. During the 10k concurrency sweep, pswpout
stayed flat on every node, while the head and one worker showed only small
pswpin increases. During the 240k NIAH prefill, the head added roughly 45 MiB
of swap-out; the workers stayed flat. So the service is not relying on swap for
the steady inference working set, but the current 262k configuration still runs
close enough to host-memory pressure that the head can touch swap under a
near-ceiling long-context request.
Artifacts
results-run6.json— full per-request baseline benchmark outputniah-chat-120k.json— successful baseline NIAH resultresults-dcp262k.json— DCP2 262k benchmark output
The benchmark harnesses and raw console logs are kept in the private source tree because they include operator-local endpoints and filesystem details.