# Overnight TP3 optimization — results 2026-09-24, 03:11-11:11 IDT, three DGX Sparks (GB10), TP_SIZE=3 throughout. Plan: [merged-new.md](merged-new.md). Running log with every experiment: [overnight-progress.md](overnight-progress.md). Raw evidence (per-request JSONL, summaries, acceptance joins, boot logs, env files, profiles): [results/overnight-20260924-0311/](results/overnight-20260924-0311/). **Result: the fleet serves a configuration that is 17-40 % faster single-stream and 19 % faster at C4 than the one it started with, validated on two boots with no measured quality regression.** Biggest levers: TP3/EP1 (+8-12 %), the `wo_a` FP8 twin with DROP/MID, k=5 with the confidence cap (code), and Engram prefetch. Two operational defects were found and fixed on the way: rank 2 was running a different SGLang build from ranks 0/1, and the packed Engram shards were missing on every rank. | workload (5 reps, 512 tokens, from spark2) | original (b0) | final F1 (fresh tune) | final F2 (cache reuse) | change b0 -> F2 | |---|---|---|---|---| | C1 prose, greedy (tok/s) | 29.28 | 34.29 | **34.15** | **+16.6 %** | | C1 prose2, greedy (added at E2; E2 value in first column) | (29.56) | 32.27 | **32.56** | (+10.1 % vs E2) | | C1 code, greedy | 44.65 | 62.69 | **62.24** | **+39.4 %** | | C1 sampled chat (T=0.7) | 28.87 | 36.57 | **37.20** | **+28.9 %** | | C4 aggregate, 2 prose + 2 code | 63.78 | 76.03 | **75.79** | **+18.8 %** | | C4 per-stream decode | 19.46 | 24.52 | 24.31 | +24.9 % | | TTFT, C1 (52-token prompt) | 0.28-0.30 s | 0.25-0.29 s | 0.25-0.29 s | unchanged | | bs=1 step, prose / code | 73.2 / 74.0 ms | 58.7 / 68.5 ms | 59.0 / 69.7 ms | -14 / -4 ms (k 3 -> 5) | | acceptance, prose / code | 2.17 / 3.27 | 2.05 / 4.00 | 2.04 / 3.98 | code +22 % (k=5) | | effective KV pool (tokens) | 491,520 | 658,176 | 749,824 (pin) | see memory section | Quality on the final configuration (F1 and F2, plus E8 with identical settings): 8/8 checkable tasks, 8/8 under 4-way concurrency, greedy 256-token repeat 1 distinct of 3, needles found at 28.7k and 95.5k (F1) and at 190.9k prompt tokens (E8) with prose/code/sampled requests running alongside. No NaN/inf/OOM/traceback lines in any final-configuration log. With autotune reuse, the greedy code text was byte-identical between F1 and F2. The fleet is left serving the final configuration from the default `.env` via `./start.sh` (F3, rebooted ~09:47 after the E11 extra experiment; same settings and tactics as F2). ## How it was measured - `scripts/overnight/bench.sh LABEL 5 512` runs [benchmarks/overnight_bench.py](../benchmarks/overnight_bench.py) **from spark2** against the head: one discarded warm-up per workload and one C4 warm-up wave, then 5 repetitions per workload, 512 max tokens, thinking off. Workloads: greedy prose (lighthouse essay), greedy prose2 (fishing-village story, added at E2), greedy code (LRU cache + tests), sampled chat (T=0.7, top_p=0.95); C4 = four concurrent requests (2 prose + 2 code) x 5 waves. - Tokens are `usage.completion_tokens`; never SSE event counts. C1 decode tok/s = (tokens - 1) / (last content delta - first content delta); C4 aggregate = total completion tokens / wave wall time. Acceptance and step time come from the head's `Decode batch` lines inside each phase window ([scripts/overnight/accept.py](../scripts/overnight/accept.py): step = accept_len x bs / gen throughput). - `scripts/overnight/quality.sh LABEL [needle_k,...]` runs [benchmarks/overnight_quality.py](../benchmarks/overnight_quality.py): 8 checkable tasks (arithmetic, JSON schema, generated `is_prime` executed against 10 cases, sorting, counting, facts), the same 8 fired 4 at a time, a greedy repeat check, and needle retrieval. - Every experiment is its own env file (`.env` + the listed changes) in `results/.../envs/`, booted with `ENV_FILE= ./start.sh serve`. `start.sh` sources the env file with `set -a`, so command-line overrides of variables that `.env` defines are silently ignored; use an env file. - Foreign traffic: none during measurements (last foreign chat request 02:29). sparkDash on spark3 polls the API every 2 s (5 GETs, 0.06 % CPU); pausing it was refused by the session's safety policy, so it is a constant background in every number here. - **Cross-boot noise.** Until autotune retention worked (E7), FlashInfer re-drew its tactics every boot, which changes the numerics, hence the greedy text, hence acceptance: a few % of C1 tok/s between boots of one configuration. bs=1 step time is the steadier signal and is reported alongside. From E7 on, arms that differ only in volatile switches reuse one tactic set, and greedy code output was byte-identical across boots. ## Starting state and what was repaired | issue found | action | |---|---| | Packed Engram shards absent on all ranks (`packed=False`) | spark2's old TP2 shards (101.4 GB) moved to **spark1:`/home/mia/archive/dsv41-engram-tp2-spark2/`** (SHA-256 verified both sides, `SHA256SUMS` there; pointer at spark2:`/home/zurih/dsv41-engram/MOVED.txt`); TP3 shards packed on all three ranks; `packed=True` for layers 1 and 14 on ranks 0/1/2 after restart | | **Rank 2 ran a different SGLang build** (spark3's `lmsysorg/sglang:dev-dsv41` = 381b27ff / da64c5cbb, 40 source files different from ranks 0/1 incl. `deepseek_v4.py` and the DSpark worker) | spark3's images kept as `lmsysorg/sglang:dev-dsv41-da64c5cbb-20260911` and `dsv41-3x-spark:mixed-da64c5cbb-20260924`; the head's base (37939c26) loaded onto spark3; overlay rebuilt everywhere | | Autotune caches discarded twice per boot | `adapter/autotune_keep.py` ported, plus two fixes (below) | ## Accepted changes (in order of application) | step | change | evidence | |---|---|---| | B1 | packed Engram + identical images on all ranks | C4 +5 %, code +2 %; Engram waiting gone from the profile | | E2 | `DSV41_WO_A_W8=1`, `DSV41_WO_A_W8_DROP=1`, `DSV41_WO_A_W8_MID=1` (target layers only) | bs=1 step 72 -> 68-69 ms, bs=4 131.5 -> 125.6 ms; KV pool back to the 750k pin (DROP frees 1280 MB/rank) | | E4 | `DSPARK_BLOCK_SIZE=5` + `DSV41_VERIFY_CAP=conf:0.1` + `DSV41_BLOCK_VERIFY=1` | code +9.6 %, prose/sampled/C4 neutral vs k=3 | | E5 | `DSV41_DRAFT_HEAD_FP8=1` | step -0.1 to -1.3 ms on all C1 workloads, +210 MiB | | E6b | `DSV41_ENGRAM_PREFETCH=1` (check-mode pass first: 0 differing gathers on all ranks) | step -2.3 to -2.9 ms everywhere, C1 +3.7 to +4.2 % | | E8 | `EP_SIZE=1` (TP3/EP1) | C1 +7.8 to +12 %, C4 +9.7 %; NCCL 12.9 -> 5.6 ms/step | | infra | `DSV41_AUTOTUNE_KEEP=1` (fixed port) | all three ranks `reused` both caches on E7, E9 and the final repeat boot; greedy output reproducible across boots | ## Rejected or neutral | experiment | result | |---|---| | E1: `wo_a` W8 twin without DROP | does not boot at fraction 0.95: +688 MiB leaves "no GPU memory for the KV cache ... minimum viable 0.9503" | | E1b: W8 + DROP as shipped | booted but acceptance 1.0 (C1 ~14 tok/s): dev-dsv41's draft runs its own einsum on `self.wo_a.weight`, which DROP had replaced by a zero tag. **Fixed in the adapter** (draft left on bf16 in bridge mode) | | E1c: W8 + DROP without MID | C4 -8 %: 16-row verifies fall to per-call dequantize; MID fixes it (E2) | | E3: k=5 without the cap | code +11 %, prose -8 to -16 %, C4 -10 % | | E7: `DSV41_DRAFT_TAU=0.8` | sampled +1.2 % pooled over 15 runs, inside noise; not a demonstrated win, left off | | E9: `conf:0.2` instead of 0.1 | mixed within noise, C4 -2.8 % | | E10: k=3 without cap on EP1 | code -10.5 %, prose2 -3.3 %, prose/C4 equal: k=5 + `conf:0.1` stays | | E11: draft `wo_a` einsum on the fp8 twin (`DSV41_WO_A_W8_DRAFT=1`, new, default off) | C1 -0.8 to -1.3 %, C4 +1.3 % on the same tactics; step -1.5 to +0.2 ms, prose acceptance dipped; left off | ## Adapter fixes made tonight - `adapter/wo_a_w8.py`: in einsum-bridge mode (dev-dsv41) the draft's `wo_a` is never dropped (the draft never reaches the shared dispatch, so DROP broke it) and by default not twinned; the new `DSV41_WO_A_W8_DRAFT=1` (default off, forwarded by `start.sh`, volatile for autotune) routes the draft's own einsum to a twin instead (E11, neutral). - `adapter/autotune_keep.py` (ported from knapcio 4d8f4c0): 1. deletes a rank's cache when its sidecar does not match the launch. Otherwise, when no rank has a matching sidecar, every digest is "", the stock gate sees agreement, and FlashInfer still loads each rank's stale file (the cache key omits block size, graph sizes and adapter switches); 2. excludes `SGLANG_RUN_ID` (a per-boot timestamp the launcher sets) from the fingerprint; with it, no sidecar could ever match and nothing was reused; 3. volatile list extended with `DSV41_DRAFT_HEAD_FP8`, `DSV41_ENGRAM_PREFETCH(_CHECK)`. Tests extended for (1) and (2). - `start.sh`: forwards `DSV41_AUTOTUNE_KEEP` and `DSV41_ENGRAM_PREFETCH_CHECK` on head and workers, and `SGLANG_DSPARK_FOLDED_SAMPLING` only when set (unset stays the engine's AUTO, which chose the folded sampler on every boot tonight). ## Exact final configuration `.env` (installed 08:50; the previous file is `state/env.pre-overnight-20260924`). Changed keys only; everything else is unchanged (TP_SIZE=3, NNODES=3, MEM_FRACTION_STATIC / HEAD_MEM_FRACTION_STATIC 0.95, MAX_TOTAL_TOKENS 750000, MAX_RUNNING_REQUESTS 4, CHUNKED_PREFILL_SIZE 1024, DSV41_CACHE_GIB 0, DSV41_MXFP8_BACKEND b12x, SGLANG_FLASHINFER_MOE_FUSED_FINALIZE 0, PYTORCH_CUDA_ALLOC_CONF expandable_segments:False, CONTEXT_LENGTH 262144, NCCL settings as before): ``` EP_SIZE=1 DSV41_WO_A_W8=1 DSV41_WO_A_W8_MID=1 DSV41_WO_A_W8_DROP=1 DSV41_DRAFT_HEAD_FP8=1 DSV41_VERIFY_CAP=conf:0.1 DSV41_BLOCK_VERIFY=1 DSV41_ENGRAM_PREFETCH=1 DSPARK_BLOCK_SIZE=5 DSV41_AUTOTUNE_KEEP=1 ``` Images: `dsv41-3x-spark:local` rebuilt from this working tree on all three nodes on the common base `lmsysorg/sglang:dev-dsv41@sha256:3dbc3130…` (public on Docker Hub; arm64 image id 37939c26c0ba…, 2026-09-10, SGLang 0.0.0.dev0, FlashInfer 0.6.18). Packed Engram: head `~/dsv41-engram/engram-l{1,14}-r0of3.bin`, workers `/home/zurih/dsv41-3x-spark/engram/engram-l{1,14}-r{1,2}of3.bin` (31.5 GiB each). Engine-side facts on the final boots: folded DSpark sampling active (AUTO), verify cap captured for M=6/12/18/24, `packed=True` on all ranks, autotune caches `reused` on the repeat boot. ## Reproducible commands ```bash # build (all three nodes) and serve the final configuration ./start.sh build ./start.sh # = serve, reads .env # benchmark from spark2 and join acceptance/step from the head log RESULTS_DIR=docs/results/ scripts/overnight/bench.sh