Hi Christopher,
Thanks again for investigating the earlier r6h report and incorporating the startup rendezvous and fail-stop diagnostics into r6n.
I upgraded my three-node cluster to the promoted r6n recipe. I initially encountered two startup failures during attention profiling. A third attempt, with a small diagnostic-only worker patch, exposed a specific sparknet failure record:
RoCE collective on rank 0 saw nothing from rank 1, HCA 0, for 10.000 s
(timeout 10 s) at sequence 24; the GPU was stopped there
I then retained that diagnostic image and changed only SPARKNET_ROCE_TIMEOUT_MS from 10000 to 60000 in a separate local profile. The subsequent startup succeeded, now verified by logs and runtime inspection from all three nodes and an HTTP 200 response from /v1/models.
This is not a claim that the longer timeout alone fixed it. The preceding attempts populated additional JIT caches, and we have not repeated the comparison with matched cache states or qualified a cold-cache startup. The attached short serving excerpt shows usable generation, but no demonstrated speed improvement over my earlier r6h workload. It is not a matched benchmark or a long-duration stability qualification.
- Environment and deployed identity
- Two Gigabyte AItom nodes and one ASUS GX10; three GB10s with 128 GB unified memory each.
- TP3 direct-cabled triangle, two logical 200G paths per pair, with a separate management network.
- The fabric previously passed MTU-9000 ping tests; RDMA interfaces were ACTIVE with the expected IPv4 GIDs at index 3. Those earlier tests do not exclude every transport failure in this run.
- Kernel: 7.0.0-1019-nvidia-64k; page size: 65536.
- NVIDIA driver: 580.178.04; Memory Saver 0.2.0 (operator-supplied host inventory).
- Deployment checkout on all three nodes: 1ef4af67a68ed1a98a51d48c1367a1f1d6e325c3.
- Although that deployment commit’s subject mentions r6o/r6p experiments (#290), the selected image and local profile were r6n.
- Base image: vllm-ds41f-kkref:19f2c20ed4d6-r6n.
- Base image on the head: sha256:a08af6bf6b9bcfb7f0645b097afe524dcecac41a6814379a8bfe85750557ff0b.
- Prepared vLLM patch head: b1309b4a986d62ea1ea293c55d009ddcdd2a036b.
- Expected vLLM base tree: 0c60314da7616831d52187b98fcc01760138d798.
- sparknet revision: 3bc8d10104a5012244a0424d7dfc1107a72eafab; tree f72599fa212e894d67ee114553e628ab05daf2da; proxy ABI 10.
- Context: 524288; KV cache: 3758096384 bytes per rank (3.5 GiB).
- max_num_seqs=8; max_num_batched_tokens=4096; max graph capture size=48.
- DSpark: five draft tokens, greedy draft sampling, block rejection, adaptive verification enabled, cost scale 2.0, dead-row survival threshold 0.2.
- Vision enabled with up to four images per request.
- CPU set: 5-9,15-19; SPARKNET_ROCE_PROXY_CPU=big.
- Effective DSpark cost directory in the diagnostic attempt: /cache/kkref/dspark-costs/build-77b3fa3c35f1ce6d.
- Model mounted read-only at /models. New r6n cache mounted from /home/hxfhd/spark-ds41f-r6n/cache; the old r6h cache was retained separately.
Rank map:
0 aitopatom-3699 192.168.1.137
1 aitopatom-e74a 192.168.1.124
2 gx10-d966 192.168.1.120
The r6n image was built once on the head and distributed. The second node uses Docker’s containerd overlayfs snapshotter; the other nodes use overlay2. For the base r6n image, image IDs differed on that node, but canonical RootFS, Config and combined runtime-content hashes were verified equal. We therefore did not treat a differing image ID alone as evidence of different runtime contents.
- Attempts and evidence boundaries
The archive evidence/r6n-profilediag-failed-starts.tar.gz contains the launcher’s preserved inspect output and container logs for all three nodes for each failed attempt:
20261016T180457Z-dsv41-karmic-kraken
Original r6n, 10-second RoCE timeout; first r6n startup with its new cache.
Failed during attention profiling. The visible CUDA error was reported at
cuModuleLoadData while loading attention metadata code; cleanup synchronization
also failed. No named collective record was captured by the original path.
20261016T192415Z-dsv41-karmic-kraken
Original r6n, 10-second RoCE timeout; existing cache reused.
Failed again in attention profiling, with a similar module-load/cleanup error.
The three ranks reached the logged DSpark initialization point within about
0.164 seconds. Reusing the cache did not make this attempt succeed.
20261016T203511Z-dsv41-karmic-kraken
Diagnostic derivative, 10-second RoCE timeout.
Captured the rank-0 / rank-1 / HCA-0 / sequence-24 failure quoted above.
All three container inspections report OOMKilled=false.
A fourth attempt used the same diagnostic derivative and a 60-second timeout.
Its evidence is attached as evidence/r6n-success-evidence.tar.gz. All archive
checksums passed. Runtime inspections confirm the timeout, image tag and patched
worker hash on all three ranks; collection-summary.json reports no collection
issues. Details and initial serving measurements are below.
The archive directory timestamps are capture timestamps, not the timestamps of
the first failing GPU operation. The named sequence-24 diagnosis is established
for the third attempt only; we cannot retrospectively assign it to the first two.
-
Third-attempt timeline (UTC)
rank 0 rank 1 rank 2
Model loading complete 20:33:11.488 20:33:58.905 20:34:05.156
Encoder profiling log 20:34:08.149 20:34:08.203 20:34:08.191
DSpark dead-verification threshold log 20:34:12.847 20:34:13.018 20:34:13.011
Last visible vectorization warning 20:34:36.540 20:34:55.042 20:34:55.243
Rank 0 logged compilation of rope, indexer_mxfp4_quantize, cache_writer,
sparse_mla, indexer_score and indexer_topk variants between 20:34:25 and
20:34:49 (the timestamps inside the TileLang messages).
At approximately 20:35:05.148, rank 0 printed the diagnostic collective failure.
The Python exception chain in this attempt ran through the MoE path and
__tvm_ffi_gemm_bf16_partials, with cuModuleLoadData returning
CUDA_ERROR_LAUNCH_FAILED. Profiling teardown and the worker cleanup then also
reported CUDA launch failure.
Ranks 1 and 2 reported TCPStore disconnection at approximately 20:35:07, after
the head had failed. Their captured logs have no R6N_PROFILE_DIAG failure report.
They were still marked running when inspected; that is not evidence that their
model execution was progressing or healthy.
There is observable log buffering: rank 0’s TileLang messages carrying internal
20:34:25-49 timestamps reached the Docker log together around 20:35:05.148.
Consequently, the absence of equivalent detailed worker messages is not proof
that the workers were not compiling. The last compiler warning is also not a
reliable timestamp for arrival at a specific collective.
- Engram records at the rank-0 failure
Both tables (language_model.model.layers.1.engram.embed_tokens and
language_model.model.layers.14.engram.embed_tokens) reported:
sequence=2
CTRL_IDS_SEQ=2
CTRL_ROWS_SEQ=2
CTRL_TIMEOUT_US=10000000
CTRL_WAITED_US=0
CTRL_FAILED_SEQ=0
CTRL_PHASE=0
CTRL_PHASE_SEQ=2
failure() → None
Thus both rank-0 tables had published step 2 and were idle when inspected,
without a recorded Engram failure. This does not establish the Engram state on
ranks 1 and 2, and the control words are a sequential host snapshot rather than
an atomic cross-thread timeline.
The separate evidence/head-kernel.log contains an Xid 43 at 18:04:46 from the
FIRST failed attempt. It is not kernel-journal evidence for the third or fourth
attempt, and it does not identify the original stall by itself.
- Exact diagnostic change
The attached diagnostic/profile-diagnostics.diff changes only:
/opt/spark3/candidate/vllm/vllm/v1/worker/gpu_worker.py
In the inspected r6n source, normal Worker.execute_model/sample_tokens paths use
_collective_cause(self._roce_failure), but the two profile_run calls inside
Worker.determine_available_memory have no equivalent failure-report handling.
The profiling path calls the model runner directly, bypassing the normal
Worker.execute_model wrapper.
We added an exception handler around those two profile_run calls. Before the
worker’s existing _release_b12x_profile_state finally block runs, it logs:
- the current exception chain;
- self._roce_failure();
- the existing mapped-host control words and failure() report of each model
module with disk_rows;
- unavailable/closed-reader diagnostics and an end marker.
The handler then re-raises the existing exception. Diagnostic exceptions are
contained. It does not replace the existing cleanup, add CUDA synchronization,
change the computation or timeouts, or run on the successful serving path.
Internal profile_run teardown may already have happened before this handler;
this is additional visibility, not a claim to catch the earliest fault.
Original worker SHA256:
756d4ae951558fe346c49df79121f2d289e951521f179049191bffe9e91ad12e
Patched worker SHA256:
143438d93f2436e1466b3a2ba9797537a070731a79f9a541c329ef6d4d6eb7d7
Diagnostic image:
vllm-ds41f-kkref:19f2c20ed4d6-r6n-profilediag-v1
Head image ID:
sha256:ccefe0f6c28105a4146d02037b8ce92a4d1dcbdaaabc2ff60d009fa6309789f5
This is a local derivative, not an unchanged official r6n source tree.
Inherited source-tree labels describe the base. Added local.spark.trial labels
mark it modified and record the patched worker hash. The historical identity.json
in diagnostic/ describes the preparation-time status; the subsequent logs show
that the derivative was built and its diagnostic code executed on the cluster.
- Successful configuration trial
The final local overlay was:
{
“extends”: {
“config”: “config/cluster-r6n-profilediag.local.json”
},
“set”: {
“environment”: {
“SPARKNET_ROCE_TIMEOUT_MS”: “60000”
}
}
}
This retains the same diagnostic image, Engram timeout, model settings, cache
mount and explicitly pinned effective DSpark cost directory. It does not add the
old r6h manual JIT barrier or restore SPARKNET_ROCE_SPIN_LIMIT.
Verified successful-run timeline (UTC, 2026-10-16):
Head container started: 20:41:11.106
Application startup complete: 20:47:18.758 (about 6 min 8 s later)
/v1/models HTTP 200 checked: 21:08:02.563
Rank Local JIT start Local JIT finish Reported duration
0 20:44:41.523 20:45:19.887 38.36 s
1 20:44:41.616 20:46:28.808 107.19 s
2 20:44:41.568 20:46:29.331 107.76 s
The successful boot still had about 69.44 seconds of JIT-completion skew.
Thus this was not simply a near-zero-duration local warmup on every rank.
It nevertheless reused caches from earlier attempts; it was not a clean-cache
qualification. The post-JIT rendezvous and an individual GPU collective timeout
cover different waits, so 69 seconds of local-JIT skew does not mean a collective
exceeded the new 60-second deadline. These logs do not measure the longest
successful collective wait or prove that any wait needed more than 10 seconds.
All three containers were running and OOMKilled=false at collection, with the
expected deployment commit and worker SHA256. The head reports pinned DSpark
cost curves under build-77b3fa3c35f1ce6d. The three since-start kernel journal
queries returned “-- No entries --”; no new Xid is present in this evidence.
The captured logs show no CUDA exception, diagnostic failure record or visible
restart in this successful attempt. API readiness to collection spans about
20 min 44 s, with generation metrics covering approximately 12 minutes.
Startup still printed the 60-second shared-memory-broadcast waiting messages;
they were followed by successful progress. Those messages alone did not mean
startup had failed. Several client GET probes to unsupported API paths returned
404, while /v1/models and 30 logged /v1/chat/completions POST responses returned
200. HTTP 200 for a streaming response alone is not proof of its completion.
No serving-time JIT warning was found after application startup in this excerpt.
The observed success is encouraging, but cache population is a confounder:
failed attempts compiled additional kernels. We have not demonstrated that the
60-second setting was necessary, that 10 seconds still fails with the final
cache contents, or that an empty cache now starts reliably. A longer timeout
also delays detection of a real stall; we are treating it as a local workaround
pending your assessment, not proposing it as a new general default.
- Interpretation and questions
Confirmed:
- The diagnostic attempt stopped rank 0 on a timed-out one-shot collective.
- The missing peer named by its record was rank 1, HCA 0, sequence 24.
- The two rank-0 Engram tables had no recorded failure at inspection.
- Profiling still encountered compilation, after ranks reached nearly aligned
initialization points.
Plausible but not established:
- Compilation/module-loading skew inside profiling may have outlasted the
collective’s 10-second deadline. An entry rendezvous alone cannot prevent
ranks from diverging again within a phase.
- Module loading may interact with outstanding GPU waits. We did not capture
native/Python stacks at the stall, so this is not a demonstrated deadlock.
Unresolved:
- Whether rank 1 was compiling, loading a module, waiting on another operation,
or experiencing a proxy/transport problem when rank 0 timed out.
- How much the 60-second timeout contributed relative to cache reuse.
- Cold-cache reproducibility and sustained serving reliability.
Would it make sense to expose the existing collective/Engram failure records
in determine_available_memory as well as in the ordinary serving wrappers?
Also, is this a profiling path that should preload its kernels/modules before
issuing distributed work, or should we test a different startup-specific wait
policy? The successful boot logs are included, and I can run a focused follow-up.
I have retained the old r6h image/config/cache and rollback backups, the original
r6n image, and all failed-start evidence. The following performance observations
come from serving logs, not from startup duration.
- Initial serving speed versus my earlier r6h report
The r6n measurements below come from rank0/container-stdout.log in the successful
archive, with metric timestamps from 20:55:48 to 21:07:48 UTC (22:55:48 to
23:07:48 in Paris). There are 71 throughput records and 70 speculative-metric
records. These are server logging-window averages, not per-request latency
measurements. Running requests were 0 or 1 at each sample and waiting requests
were always 0; very brief concurrency between samples cannot be excluded.
Metric Earlier r6h This r6n trial
Sustained/reference generation average 45.9 tok/s See scopes below
Highest logged generation window 62.0 tok/s 54.7 tok/s
Draft acceptance 54.7%* 53.1% overall
Prefix-cache hit rate 96.6-97.5% 0.0-94.8%
Maximum reported GPU KV cache use 8.1% 9.5%
- r6h acceptance applies to the earlier approximately 4.5-minute sustained
segment. Its speed ranged from 32.0 to 59.4 tok/s; 62.0 was elsewhere in that
log. The r6h values are from my previous report, not recomputed from a raw r6h
log included in this package. The r6n scopes deliberately remain explicit:
- All 70 positive-generation windows: arithmetic mean 37.46 tok/s. This includes
prompt processing and request boundaries, so it is not a pure decode rate.
- All 48 positive-generation windows reporting zero prompt throughput:
mean 41.27 tok/s, median 43.25, range 23.3-54.7. Weighted acceptance for these
windows is 54.24%. Restricting further to Running=1 gives 44 windows and
41.34 tok/s, so the result is not driven by the four end-of-request samples.
- Longest consecutive zero-prompt, Running=1 sequence: 12 approximately 10-second
windows ending at 20:59:18 through 21:01:08 (about 120 seconds of represented
intervals). Mean 42.75 tok/s, range 30.0-50.9; acceptance 3758/6788 = 55.36%.
- Another uninterrupted sequence, nine windows ending at 21:05:28 through
21:06:48 (about 90 seconds), averages 45.91 tok/s, range 39.4-52.5, with
acceptance 3136/4923 = 63.70%. This is an illustrative faster segment, not
the overall result or a selected benchmark score.
- Overall weighted acceptance is 19007 accepted / 35789 drafted = 53.11%.
Percentages are weighted by drafted tokens, not averaged across windows.
The zero-prompt-window mean is approximately 10.1% below the earlier r6h
sustained mean; the longest r6n segment is approximately 6.9% below it. These are
arithmetic differences between different workloads/scopes, NOT measured engine
regressions. The faster 90-second r6n segment essentially matches the old mean.
At present I cannot report an improvement in single-stream speed.
The workloads, prompt/context lengths, request boundaries and cache histories
were not matched. The r6n prefix-cache statistic starts at zero and builds to
94.8%, whereas the prior r6h session was already at 96.6-97.5%. This affects the
comparability of the full workflow, especially prompt processing; it does not
by itself explain slower zero-prompt decode windows. Similar aggregate draft
acceptance also does not guarantee identical per-step compute or context cost.
The logs lack the request-level timing and replay data needed for a controlled
comparison, TTFT or inter-token-latency analysis. No accuracy comparison was run.
The timeout change is a startup workaround, not an established throughput
optimization. We have not measured a speed effect attributable to it. A useful
next comparison would replay identical single-stream agent requests, context,
sampling and cache conditions on the retained r6h and r6n configurations, but
that A/B experiment has not been performed here.
Reproducible extraction is included as analysis/analyze_success.py, with every
metric sample and its source line in analysis/success-metrics.json. The package
also includes a Chinese reading guid
r6n-author-feedback.zip (399.4 KB)
e and the unchanged raw success archive.
Thanks again for the work on this and for making the underlying failure records
available — the small diagnostic addition turned the generic CUDA error into a
specific collective timeout report.