SPB Git

spb/wp33_uqo

Public MIT

Gate F figure/table generators; implementation section: threads and dtypes lessons

Simon-Pierre Boucher committed (Nov 1, 2026) parent 093c50d Browse files

3 changed files +38 −2

modified paper/sections/implementation.tex +6 −0
@@ -21,6 +21,12 @@ of \code{pread}, 0.06~ms of array creation and the remainder in the lazy install
21 21 $\max|\Delta\mathrm{logit}| = 0.0195$ against mlx-lm; using mlx-lm's compiled SwiGLU gives $0.0$. Numerical parity in float16 is
22 22 a property of the exact kernel sequence, not only of the weights, and the test suite pins it.
23 23
24 +\paragraph{Threads and dtypes.} Two defects were found by the exactness tests rather than by inspection. MLX arrays created in reader
25 +threads cannot be evaluated in another thread (streams are thread-local), so reader threads now return NumPy views and the consuming
26 +thread creates the MLX arrays. And the first transport cast activations to float16 on the wire, which is exact for OLMoE (float16) but
27 +not for Qwen3 (bfloat16): the first two-node sweep on Qwen3 showed $\max|\Delta\mathrm{logit}| = 0.65$ and was discarded; activations and
28 +results now travel in the model's dtype and the Qwen3 two-node path is bit-identical again.
29 +
24 30 \paragraph{Reproducibility.} Every benchmark writes JSON lines with the command, git commit, host, chip, memory, macOS,
25 31 library versions, seed and timestamps; raw logs are append-only under \code{benchmarks/raw/} and tables and figures are
26 32 generated from them by scripts. Model revisions are pinned by commit hash in \code{benchmarks/configs/models.yaml};
modified scripts/make_figures.py +24 −1
@@ -100,5 +100,28 @@ def fig_attribution():
100 100 ax.set_title("OLMoE, 16 slots, cold NVMe (M5 Max): where the decode step waits", fontsize=9)
101 101 save(fig, "attribution")
102 102
103 +def fig_gateF():
104 + p = os.path.join(ROOT, "benchmarks/raw/gateF/two_node_qwen3_m3u96a_m3u96b.jsonl")
105 + if not os.path.exists(p): return
106 + recs = [json.loads(l) for l in open(p) if l.strip()]; recs = [r for r in recs if not r["warmup"]]
107 + groups = {}
108 + for r in recs: groups.setdefault((r["capacity"], r["placement"].split(":")[0]), []).append(r)
109 + caps = sorted({k[0] for k in groups}); pls = ["none", "odd", "popular"]
110 + fig, axes = plt.subplots(1, 2, figsize=(7.2, 2.8))
111 + w = 0.25
112 + for i, pl in enumerate(pls):
113 + tps = [statistics.median(x["gen_tps"] for x in groups.get((c, pl), [])) if (c, pl) in groups else 0 for c in caps]
114 + axes[0].bar([j + (i - 1) * w for j in range(len(caps))], tps, width=w, color=PAL[i], label={"none": "local (cold NVMe)", "odd": "odd experts remote", "popular": "least popular remote"}[pl])
115 + st = [statistics.median(x["stall_s"] / x["gen_tokens"] * 1e3 for x in groups.get((c, pl), [])) if (c, pl) in groups else 0 for c in caps]
116 + rw = [statistics.median((x["remote"]["remote_wait_s"] if x.get("remote") else 0) / x["gen_tokens"] * 1e3 for x in groups.get((c, pl), [])) if (c, pl) in groups else 0 for c in caps]
117 + axes[1].bar([j + (i - 1) * w for j in range(len(caps))], st, width=w, color=PAL[i], alpha=0.5)
118 + axes[1].bar([j + (i - 1) * w for j in range(len(caps))], rw, bottom=st, width=w, color=PAL[i])
119 + for ax in axes:
120 + ax.set_xticks(range(len(caps))); ax.set_xticklabels([f"C={c} of 128" for c in caps])
121 + axes[0].set_ylabel("decode tok/s (median)"); axes[0].legend(frameon=False, fontsize=7)
122 + axes[1].set_ylabel("ms per token: local stall (light) + remote wait (dark)")
123 + fig.suptitle("Qwen3-30B-A3B, M3U96a coordinator (cold NVMe) + M3U96b worker over 1 GbE", fontsize=9)
124 + save(fig, "gateF_qwen3")
125 +
103 126 if __name__ == "__main__":
104 − fig_sim_hit_vs_capacity(); fig_capacity_sweep(); fig_two_node(); fig_attribution()
127 + fig_sim_hit_vs_capacity(); fig_capacity_sweep(); fig_two_node(); fig_attribution(); fig_gateF()
modified scripts/make_tables.py +8 −1
@@ -43,7 +43,7 @@ def bench_summary(paths, label, caption, keys=("backend", "capacity", "predictor
43 43 for k in sorted(groups, key=lambda x: tuple(str(v) for v in x)):
44 44 g = groups[k]
45 45 med = lambda f: statistics.median([r[f] for r in g if r.get(f) is not None]) if any(r.get(f) is not None for r in g) else None
46 − cfg = (f"C={k[1]}, {k[2]}, m={k[3]}" if keys[2] == "predictor" else f"C={k[1]}, fused={k[2]}") + (", cold" if k[4] else "") if k[0] == "titan" else "--"
46 + cfg = (f"C={k[1]}, {k[2]}, m={k[3]}" if keys[2] == "predictor" else (f"C={k[1]}, placement={k[2]}" if keys[2] == "placement" else f"C={k[1]}, fused={k[2]}")) + (", cold" if k[4] else "") if k[0].startswith("titan") else "--"
47 47 f = lambda v, fmt: (fmt % v) if v is not None else "--"
48 48 lines.append(f"{k[0].replace('_', '\\_')} & {cfg.replace('_', '\\_')} & {len(g)} & {f(med('gen_tps'), '%.1f')} & {f(med('itl_ms_p95'), '%.1f')} & {f(med('cache_hit_rate'), '%.3f')} & {f(med('stall_s'), '%.2f')} & {f(med('ttft_ms'), '%.0f')} \\\\")
49 49 lines += ["\\bottomrule", "\\end{tabular}", "\\begin{tablenotes}\\footnotesize", "\\item Sources: " + ", ".join(f"\\code{{{os.path.relpath(p, ROOT).replace('_', '\\_')}}}" for p in paths) + ".", "\\end{tablenotes}", "\\end{table}"]
@@ -63,6 +63,13 @@ if __name__ == "__main__":
63 63 fused = os.path.join(ROOT, "benchmarks/raw/gateD/fused_upgate_laptop.jsonl")
64 64 if os.path.exists(fused):
65 65 bench_summary([fused], "tab:fused", "H4: fused up+gate gather\\_qmm vs two kernels (laptop; 64 slots resident, 128 tokens, 12 runs; 16 slots cold, 64 tokens, 4 runs).", keys=("backend", "capacity", "fused_upgate", "m", "nocache", "split"))
66 + gf = os.path.join(ROOT, "benchmarks/raw/gateF/two_node_qwen3_m3u96a_m3u96b.jsonl")
67 + if os.path.exists(gf):
68 + recs = [json.loads(l) for l in open(gf) if l.strip()]
69 + for r in recs: r["placement"] = r["placement"].split(":")[0]
70 + tmp = gf + ".tmp"; open(tmp, "w").write("\n".join(json.dumps(r) for r in recs) + "\n")
71 + bench_summary([tmp], "tab:gateF", "Gate F: Qwen3-30B-A3B on M3U96a (bounded residency, cold NVMe) with M3U96b as expert worker over 1 GbE; placements: none (all local), odd (half remote, static), popular (least popular half remote, frequency-based); 64 tokens, held-out general/code, 8 runs per row.", keys=("backend", "capacity", "placement", "m", "nocache", "split"))
72 + os.remove(tmp)
66 73 q = os.path.join(ROOT, "benchmarks/raw/gateC/qwen3_m3u96a/baseline.jsonl")
67 74 if os.path.exists(q):
68 75 bench_summary([q], "tab:qwen3", "Qwen3-30B-A3B-Instruct-2507 4-bit on M3U96a (M3 Ultra, 96 GB): mlx-lm vs TITAN all-resident (128 slots, preloaded), 64 tokens, 3 repeats.")
69 76
Menu

Theme