# Recorded in knapcio/DeepSeek-V4.1-Flash-4x-DGX-Spark-TP4; commit ids and "this repository" below refer to that repository. four DGX Spark (GB10) as a switchless ring, dual plane (4 RDMA functions per node), patched NCCL 2.30.7 (NCCL_SWITCHLESS_RING_ONLY=1) image: Dockerfile.canary-roce built from this repository = 7ac7123 (v2) + the ring changes; FROM lmsysorg/sglang:dev-dsv41 @ sha256:4a5d132a env: the v2 production EXTRA_CONTAINER_ENV with EP_SIZE=1, plus scripts/ring_mesh/plan.py's line: DSV41_ROCE_RING=1 SGLANG_ROCE_ALLREDUCE=1 SGLANG_ROCE_MAX_SIZE=262144 DSV41_ROCE_GATHER=262144 B12X_ROCE_HCA=rocep1s0f0,rocep1s0f1,roceP2p1s0f0,roceP2p1s0f1 B12X_ROCE_PEER_HCA_MAPS=1=1/3,2=0/3,3=0/2;0=0/2,2=1/3,3=1/2;0=1/2,1=0/2,3=1/3;0=1/3,1=0/3,2=0/2 B12X_ROCE_TWO_WAVE_THRESHOLD_BYTES=0 mesh: dsv41-mesh.service on every node, hairpin_queue_size 8192, 8 routes / 8 tc rules (all in_hw) / 8 markers; ring order (sparkring) 0 -> 3 -> 2 -> 1 in TP ranks boot: RoCEnante ready: world=4 hcas=rocep1s0f0,rocep1s0f1,roceP2p1s0f0,roceP2p1s0f1 gid_index=3 max_size=262144 DSV41_ROCE_GATHER ready: max 262144 bytes per rank; DSV41_L2_PREFETCH: RoCE collectives prefetch the next weights into L2; DSV41_PREFILL_SP=1 ARMED proxy built from roce_ring/_roce_proxy.c (roce_proxy-8300454e819a61f5.so) max_total_num_tokens=5561856 (6335488 and 6350848 on the two previous boots) # decode step, engine spec_verify_ct (prose-type x4, code x1, 400 greedy tokens, ttft subtracted, 2 reps) prompt0: ms/step= 32.98 tok/step=2.02 tok/s= 61.3 prompt1: ms/step= 33.28 tok/step=2.01 tok/s= 60.4 prompt2: ms/step= 32.40 tok/step=1.94 tok/s= 59.9 prompt3: ms/step= 38.07 tok/step=3.74 tok/s= 98.2 prompt4: ms/step= 33.54 tok/step=2.23 tok/s= 66.6 ALL: median ms/step 33.28 (n=10) # decode step on sparkDash's prose and json prompts (256 tokens, ttft subtracted, 5 reps) prose: ms/step 36.94 tok/step 3.01 decode tok/s 81.2 (n=5) json : ms/step 40.27 tok/step 5.45 decode tok/s 134.7 (n=5) # ms per step at concurrency c (c distinct prompts, 300 greedy tokens, e2e/verify_ct incl. ttft) c1: 35.2ms/step (2.31 tok/step) c2: 47.8ms/step (2.07 tok/step) c4: 69.7ms/step (2.08 tok/step) c8: 104.0ms/step (2.13 tok/step) # sparkDash 1.8.8, 256 new tokens, temperature 0, thinking off, 2 prose c1 warm-ups discarded prose c1 per 80.89 agg 80.89 prose c2 per 58.39 agg 114.61 prose c4 per 39.36 agg 154.62 prose c8 per 28.06 agg 221.27 prose c16 per 22.95 agg 345.12 code c1 per 120.54 agg 120.54 code c2 per 88.73 agg 175.66 code c4 per 62.76 agg 234.14 code c8 per 41.23 agg 302.97 code c16 per 29.8 agg 446.57 structured c1 per 150.19 agg 150.19 structured c2 per 86.27 agg 168.99 structured c4 per 70.22 agg 262.9 structured c8 per 42.94 agg 284.41 structured c16 per 48.96 agg 547.67 json c1 per 134.17 agg 134.17 json c2 per 91.76 agg 179.03 json c4 per 72.82 agg 288.3 json c8 per 53.78 agg 417.44 json c16 per 43.63 agg 671.52 prefill [(4117, 3152.55), (16406, 5456.64), (32786, 5567.43), (65560, 5539.67), (131093, 5392.59), (262164, 4970.74)] # qeval (scripts/qeval.py, 75 tasks, c1), three runs after the benches 72/75 passed (96.0%), wall 1.9 min, median 81.81 tok/s; failed: code_interval_intersect, json_escape, math_m9 71/75 passed (94.7%), wall 2.0 min, median 84.74 tok/s; failed: code_interval_intersect, json_escape, math_m9, reason_r12 72/75 passed (96.0%), wall 1.9 min, median 85.93 tok/s; failed: code_interval_intersect, json_escape, math_m9 # phrase needle (code in the middle of numbered filler records) {"target": 6000, "prompt_tokens": 5845, "needle_at": "50%", "expected": "RING-6000-COBALT", "got": "RING-6000-COBALT", "result": "PASS", "seconds": 1.4, "prefill_tok_s": 4184} {"target": 64000, "prompt_tokens": 64331, "needle_at": "50%", "expected": "RING-4162-COBALT", "got": "RING-4162-COBALT", "result": "PASS", "seconds": 12.6, "prefill_tok_s": 5090} {"target": 1011000, "prompt_tokens": 1030651, "needle_at": "50%", "expected": "RING-3727-COBALT", "got": "RING-3727-COBALT", "result": "PASS", "seconds": 305.1, "prefill_tok_s": 3378} # one change at a time, same ring, 2026-09-25 NCCL ring vs RoCEnante over the mesh (c5cee32 stack, 80 KB cap, sparkring proxy): qeval median 76.34 -> 79.53 tok/s, both 72/75; per task ms/step -5.2 % median (51 tasks >= 64 tokens, faster on 50), tokens/step +0.0 % median (-17 % .. +14 %) proxy ROCE_IDLE_SPINS 200000 -> 20000000 (256 KB cap): ms/step c1 40.6 -> 37.1, c2 53.1 -> 49.4, c4 74.7 -> 70.3, c8 113.5 -> 110.8; active proxy thread in clock_nanosleep during c1 decode: 82/300 samples -> 0/300 80 KB cap, two-wave default -> 256 KB cap, two-wave off (hairpin 8192): ms/step c1 40.5 -> 40.6, c2 53.6 -> 53.1, c4 75.7 -> 74.7, c8 112.5 -> 113.5 c5cee32 -> v2 (both with the mesh and the proxy fix): ms/step c1 37.1 -> 36.1, c2 49.4 -> 48.4, c4 70.3 -> 69.4, c8 110.8 -> 108.3 # all-reduce us/op in CUDA graphs (40 per graph, 100 replays), 4 ranks over the mesh; drops = sum over 16 functions of rx_out_of_buffer hairpin 1024, two-wave 128K: 60K:23 80K:30 100K:42 120K:126 160K:73 200K:137 240K:90 360K:164 480K:191 720K:326 960K:449 hairpin 1024, drops: rows 1-8 (<=80K) 0; 100K 163 rx_out_of_buffer + 50 out_of_sequence; one 60K-960K sweep (two-wave off) 259612 rx_out_of_buffer + 55327 out_of_sequence hairpin 8192, two-wave 128K: 60K:24 80K:30 100K:35 120K:40 160K:53 240K:75 360K:89 480K:117 (0 drops) 720K:217 960K:274 (64 drops) hairpin 8192, two-wave off: 120K:40 160K:47 180K:50 240K:60 360K:78 480K:95 (0 drops) ib_write_lat 61440 B, flow label 16383: opposite node via a neighbour 9.99-10.18 us (4 paths), direct neighbour 8.47 us; neighbour IpForwDatagrams unchanged # tested and not adopted scheduler process pinned to the X925 cores (5-9,15-19): qeval 79.53 -> 75.15 (-5.5 %) RoCE proxy threads alone on dedicated X925 cores: qeval 78.96 -> 78.08 (within run-to-run spread)