Owner lanes: recipes/mimo26-*-sparkring. Operator directive 2026-09-29:
SparkRing is the fast line; MOPD only (RL retired — RL keeps training
artifacts); measure both c1 and c2 at every depth.
Image: ghcr.io/fujitsupolycom/sparkring@sha256:c977a2d2… (publication
dev-20260928-plainstatus-cuda1342-nccl2323-status033, image id
4b7049d1e00f…, base eugr/spark-vllm-b12x:nightly-20260924, CUDA 13.4.2,
NCCL 2.32.3, runtime-status 0.3.3). Anonymous pull verified; 14.2 GiB
compressed. Entrypoint is their toolchain.py serve wrapper — the harness
keeps their launcher contract, our sm121 supplies model path, rendezvous
flags, rank, env.
Known wins of this line over the eugr nightly: B12X selection-cache
correction (restart 200–225 s), MiMo vision attention-sinks fix, host-to-device
staging fix, tool-result contract (400/500), /v1/sparkring/status/view
dashboard.
spec num_speculative_tokens 5, kv_cache_dtype bfloat16, draft load
safetensors, --max-num-seqs 16, --max-num-batched-tokens 8192, bounce
loader {"read_mode":"bounce","io_threads":8} (--model-loader-extra-config),
compilation-config fuse_act_quant + cudagraph_mode FULL_AND_PIECEWISE +
capture sizes to 96, GMU 0.85. Env: VLLM_KV_CACHE_LAYOUT=BLHNC,
VLLM_LM_HEAD_A16=1, VLLM_MXFP8_LM_HEAD=0, VLLM_USE_V2_MODEL_RUNNER=1,
NCCL_IB_EXTENDED_IPV4_GIDS=1, RoCE allreduce/allgather caps,
NCCL_ALGO=Ring, channels 4/4, NCCL_P2P_LEVEL=SYS, … (full recipes are
the source of truth).
Documented deviations (uniform across the three variants):
NCCL_SWITCHLESS_RING_ONLY, B12X_ROCE_OPPOSITE_PATHS/PEER_HCA_MAP)
— their tp4 topology is direct-cycle-4, ours is the 200G switch fabric;
HCA/GID/socket come from the cluster [nccl] block (f0 pair on switch,
f1 pair on 2nodes-dac);61bea4a0…, index e16ec5b7…);max-model-len 524288 prod parity (their profile ships 262144; our A/B
found no behavioral difference — see §3);--no-enable-flashinfer-autotune
and our nightly's --kernel-config {…enable_flashinfer_autotune:false}
are mutually exclusive (engine ValueError) — their flag wins.Image sync: sm121 sync-image --recipe R --cluster C — one docker save +
parallel fan-out, digest-referenced images re-attached via pull-by-digest
after load (save/load drops RepoDigest otherwise: docker run <ref> would
fail with "No such image" on the peer). Measured 29.5 GiB → 3 peers: 206 s.
| recipe | cluster | nodes | state |
|---|---|---|---|
mimo26-flash-mopd-4x-sparkring |
dgx58-switch (bench) / any 4 | TP4 | benched: 165/184 (90/100) + honest depth curve (§3) |
mimo26-flash-mopd-2x-sparkring |
2nodes-dac (dgx1+dgx2) | TP2 | recipe + dry-run OK; their qualified direct-pair-2 intent, B12X_ROCE_PAIR_PATHS=2 kept, f1/GID-3 verified on-node |
mimo26-flash-mopd-2x-sparkring |
dgx58-switch (measured) / 2nodes-dac (intent) | TP2 | benched: 164/184 (89/100); c2 ≥ c1 up to 128K, 256K 27.4/25.0 |
mimo26-pro-mopd-8x-sparkring |
8nodes-switch (all 8) | TP8 | benched ×2: 167/184 (91/100) on the Sep-29 image; 164/184 (89/100) on the kraken image b01442df (2026-10-03, pluto/wired, PARTIAL — depth lost to the §4.1 client hang). First boot ~40 min incl. image sync + 394-candidate b12x autotune; KV pool 3.57M tokens (26.76 GiB auto) |
Facts for the Pro-MOPD 8x (verified 2026-09-29): snapshot
XiaomiMiMo/MiMo-V2.6-Pro-MOPD @ adea8e2c5373 on the NAS (≈573.5 GB),
arch MiMoV2ForCausalLM — same as Flash, and processor_config is
byte-equal to Flash's, so their qualified Omni hf-overrides transfer
verbatim; dflash/ draft present; quant = fp8 e4m3 + mxfp4_block_size
→ b12x MXFP4/MXFP8. Cache namespace mimo-v26-pro-mopd-* (compile caches
are per-model). Serve checklist: prod must be down for 8nodes-switch;
image currently only on dgx5-8 → --sync-image ships it to dgx1-4.
KV expectations (auto pools): Flash TP2 ≈ 10-15 GiB (weights ~87 GiB/rank), Pro TP8 ≈ 20-25 GiB (weights ~72 GiB/rank) — at ≈50K tok/GB that is 500-750K and 1M-1.2M tokens: fine for 256K×c2.
Symptom (2026-09-28/29, BOTH our nightly and the SparkRing image, identical numbers): depth suite tg t/s at c2 = 13.7 (@65K), 5.7 (@131K), 2.1 (@256K) while c1 stayed 37-47 everywhere.
Investigation trail (all findings kept because they are reusable):
return_token_ids/ignore_eos payload toggles — no effect.server→client, split per chatcmpl id): one stream
of each c2 pair trickled 19 s (2.5 tok/s) while its partner waited
(Waiting: 1 reqs) then finished in 3 s; engine log windows showed
prompt 6759 tok/s, generation 0.8 tok/s = a full ~67K monolith prefill
in flight during the "decode" window.tool-eval-bench invokes llama-benchy with no_cache=True
(→ --no-cache) and without --enable-prefix-caching (defaults
store_true): the runner takes the Standard-Run branch — context+prompt
as ONE ~67,595-token request, no warmup phase, full prefill every run.
At c2 the two cold prefills serialize through the 8192-token chunked
scheduler → exactly the admission block we measured; depth scaling of
the artifact (2.4s→39s per pair) reproduces 13.7/5.7/2.1 to the digit.
c1 was structurally immune (single stream interleaves its own prefill).Fix shipped in sm121 bench (d5a2032): both the main perf phase and
bench_depth pass --benchy-args=--enable-prefix-caching. Historical
c2 depth numbers (both engines, both lanes) are void — re-baseline before
quoting. Upstream issue (ready to file — llama-benchy/tool-eval-bench):
With defaults (
--no-cache, no--enable-prefix-caching) every depth point measures two serialized cold ~67K prefills instead of decode: c2 @65K/131K/256K reads 13.7/5.7/2.1 t/s against a healthy engine that does 40-50 t/s per stream in the same shape. One flag fixes it (13.7 → 52.1). Consider defaultingenable_prefix_cachingon wheneverdepth > 0.
mimo26-flash-mopd-4x-sparkring, report2026-09-29_160255_sparkring-honest-baseline)
Quality 165/184 (90/100); tg t/s warm-prefix:
| depth | c1 | c2 |
|---|---|---|
| 0 | 67.3 | 72.2 |
| 4096 | 62.1 | 78.9 |
| 8192 | 61.5 | 73.8 |
| 16384 | 55.2 | 69.9 |
| 65536 | 46.5 | 58.5 |
| 131072 | 37.4 | 47.0 |
| 262144 | 44.4 | 44.6 |
c2 ≥ c1 at every depth — concurrency is fine (matches SparkRing's own reports). Real effects worth further measurement: the flat-to-mild depth slope (67→45 over 256K), the c2 advantage collapsing by 256K, and the c1 128K dip (37.4 < 44.4 @256K) — one clean re-point needed to confirm.
b01442df)Bench client moved to pluto.lan (192.168.88.42, wired eth0) via
sm121 bench --ssh-host pluto.lan — a WiFi laptop client measurably
skews the perf table (kindling 4x A/B: pp2048@d0 3,667 wired vs ~2,700
WiFi, TTFT halved). TTB on pluto: 2.7.1.dev7+gbd35ba91b.
Results (all pluto/wired, hardmode seed 42, perf c1/c2, depth 16K..256K c1/c2):
| lane | quality | notes |
|---|---|---|
mimo26-flash-mopd-4x-sparkring (kraken) |
92/100 (169/184) | full incl. depth; report 2026-10-02_* (fef19db re-run); pp2048@d0 4,314 |
mimo26-flash-mopd-2x-sparkring |
90/100 (166/184) | report 2026-10-02_225515_mimo2x-pluto; pp2048@d0 2,973, pp16K 2,302, pp65K 1,558, pp128K 1,023, pp256K 612 |
mimo26-pro-mopd-8x-sparkring |
89/100 (164/184) (Deploy 80, Quality 89, Resp 59, median turn 2.3s) | PARTIAL: main bench + shallow perf only — the depth suite client-hung (§4.1); report 2026-10-03_155239_mimopro8x-pluto3-partial.{md,json}; pp2048@d0 2,386, d4K 2,270, d8K 2,086 |
mimo26-pro 6x (TP6) |
not buildable | model is 128 q-heads / 8 kv-heads (2⁷/2³): only TP∈{1,2,4,8} divide. vLLM's divide() rejects 6 at engine build. Kindling-style weight padding is NOT transferable to the main model: zero-padded KV heads enter the attention softmax (8→12 kv heads ≈ 1/3 of the denominator forced to e⁰) — fine for a verified drafter, direct quality loss for the served model. |
Three consecutive depth runs against the MiMo-Pro 8x serve (2026-10-03 14:56 / 16:58 / 18:10 pluto) hung identically; kindling 6x (11:11) and MiMo Flash 2x/4x depth runs the same days worked (16/16 in 13.5 min).
Forensics (run 3):
do_epoll_wait
(empty asyncio loop), 10 OpenBLAS threads in futex; one ESTAB keep-alive
to dgx1:30000 with 0 bytes queued both ways.run_suite print); stdout is pipe
block-buffered, so absence of output ≠ early exit, but combined with the
idle loop + zero requests the client is stuck before the first depth
request (corpus/tokenizer load or session setup) with no CPU burn.--no-warmup --skip-coherence --latency-mode generation path; main bench
(same TTB build, depths 0/4K/8K) worked 30 s earlier in the same run.Suspect: client-side TTB/llama-benchy bug in the deep-depth invocation
(path tool-eval-bench --perf-only --skip-tool-eval --depth 16384,... via
uvx llama-benchy), not the cluster. Repro needed with the Flash 8x serve
(or kindling) to localize server-vs-harness; parked per operator.
# bench lane (dgx58-switch), image pre-warmed:
sm121 serve --recipe mimo26-flash-mopd-4x-sparkring --cluster dgx58-switch
sm121 bench --recipe mimo26-flash-mopd-4x-sparkring --cluster dgx58-switch \
--label <name> --tokenizer /tmp/mimo-tok # quality + perf c1/c2 + depth c1/c2
# 2-node DAC (needs dgx1+dgx2 free):
sm121 serve --recipe mimo26-flash-mopd-2x-sparkring --cluster 2nodes-dac
# Pro 8x (needs the whole fleet, image sync included):
sm121 serve --recipe mimo26-pro-mopd-8x-sparkring --cluster 8nodes-switch --sync-image
Diagnosis instruments used (reusable): raw TCP SSE observer
(/tmp/wire-probe.py pattern — recv() timestamps separate server flush
from client processing), node-side tcpdump -tttt -A -vv 'tcp port 30000
and src <node>' split per chatcmpl id, VLLM_LOGGING_LEVEL=DEBUG (drop
after use), and driver scripts driving llama-benchy's own classes
(PromptGenerator/LLMClient) to isolate library vs CLI behavior.
4nodes-switch is free (for a like-for-like
sparkring-vs-nightly pair, if still wanted)./tmp/qwen_monitor.py (Sep-26 leftover, 2-min qwen3.8
probes against :30000 → 404 spam after dgx5 changed workload) killed
2026-09-29; file kept for reference.