code wiki / (root) / nx_swarm_gpu.nx

nx_swarm_gpu.nx source

↩ module page · 556 lines · 24681 B

1// nx_swarm_gpu.nx -- SWARM FABRIC GPU/VRAM inventory + SPLIT-MODEL SHARD PLANNER (F792). The keystone of 2// split-model serve (exo/Petals-class, sovereign): given a model too big for ONE GPU, decide how to shard 3// its layers across the fleet's LIVE free VRAM. Its OWN small organ (NEVER bolt onto nx_swarm_beat -- the 4// CPU-beacon 13-token contract stays untouched); the pulse layer reports a GPU row here, the hub reads a 5// fresh VRAM inventory + computes a shard plan. Composes the proven nx_swarm_beat store pattern (replace-by- 6// node snap, flock'd log, fabric-clock freshness, path law). 7// 8// nx_swarm_gpu put "<GPU row>" -- validate 5-token row -> log append + snap replace 9// nx_swarm_gpu show <window_sec> -- FRESH/STALE per node (the live VRAM inventory) 10// nx_swarm_gpu plan <snap> <window_sec> <model_mb> [util%] -- greedy shard plan across fresh nodes' free VRAM; 11// REFUSE if fleet usable < model (never oversubscribe) 12// nx_swarm_gpu now -- fabric clock (stamp rows in the freshness units) 13// nx_swarm_gpu [gate] -- self-gate incl the liar-killers 14// 15// GPU row (positional): GPU <node> <vram_total_mb> <vram_free_mb> <ts_us> 16// util% = headroom margin (default 85): usable = free_vram * util%/100 -- leaves room for activations + KV-cache; 17// a GPU is NEVER filled to 100% VRAM (an OOM mid-decode is the never-brick analog here). Greedy snap-order fill 18// minimizes the shard count = fewer cross-node activation transfers (Amdahl-friendly); biggest-first = a v2 sort. 19// PATH LAW mirrors nx_swarm_beat: CLI paths end .log/.snap, no "..", bare-basename-in-CWD or /tmp only. 20// license_tier: ORIGINAL expect_exit:0 21import "nx_syscalls.nx" 22import "nx_framed_append.nx" 23const SG_MAGIC_60000000: i64 = 60000000 24const SG_MAGIC_1000000: i64 = 1000000 25const SG_MAGIC_16000: i64 = 16000 26const SG_MAGIC_8000: i64 = 8000 27const SG_MAGIC_24000: i64 = 24000 28const SG_MAGIC_20000: i64 = 20000 29const SG_MAGIC_4000: i64 = 4000 30const SG_MAGIC_3600: i64 = 3600 31const SG_MAGIC_3000: i64 = 3000 32const SG_MAGIC_19000: i64 = 19000 33const SG_MAGIC_25000: i64 = 25000 34const SG_MAGIC_21000: i64 = 21000 35 36const SG_SNAP_CAP: i64 = 65536 37const SG_ROW_CAP: i64 = 256 38const SG_DEFAULT_UTIL: i64 = 85 39 40func sg_puts(s: *u8) -> i64 { sys_write(1, s, fa_len(s)); return 0 } 41 42func sg_eq(a: *u8, b: *u8) -> i64 { 43 var i: i64 = 0 44 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 45 if b[i] != (0 as u8) { return 0 } 46 return 1 47} 48 49// CLI path guard: end ".log"/".snap"; no ".."; bare basename (no '/') OR under /tmp/. 50func sg_path_ok(p: *u8) -> i64 { 51 let n: i64 = fa_len(p) 52 if n < 5 { return 0 } 53 var i: i64 = 0 54 while i + 1 < n { if (p[i] as i64) == 46 { if (p[i + 1] as i64) == 46 { return 0 } } i = i + 1 } 55 var ext_ok: i64 = 0 56 if n >= 4 { if (p[n-4] as i64) == 46 { if (p[n-3] as i64) == 108 { if (p[n-2] as i64) == 111 { if (p[n-1] as i64) == 103 { ext_ok = 1 } } } } } 57 if n >= 5 { if (p[n-5] as i64) == 46 { if (p[n-4] as i64) == 115 { if (p[n-3] as i64) == 110 { if (p[n-2] as i64) == 97 { if (p[n-1] as i64) == 112 { ext_ok = 1 } } } } } } 58 if ext_ok == 0 { return 0 } 59 var has_slash: i64 = 0 60 i = 0 61 while i < n { if (p[i] as i64) == 47 { has_slash = 1 } i = i + 1 } 62 if has_slash == 0 { return 1 } 63 if n < 6 { return 0 } 64 if (p[0] as i64) != 47 { return 0 } 65 if (p[1] as i64) != 116 { return 0 } 66 if (p[2] as i64) != 109 { return 0 } 67 if (p[3] as i64) != 112 { return 0 } 68 if (p[4] as i64) != 47 { return 0 } 69 return 1 70} 71 72func sg_read(path: *u8, buf: *u8, cap: i64) -> i64 { 73 let fd: i64 = sys_openat_rd(path) 74 if fd < 0 { return 0 - 1 } 75 var tot: i64 = 0 76 while tot < cap { 77 let r: i64 = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot) 78 if r <= 0 { break } 79 tot = tot + r 80 } 81 sys_close(fd) 82 return tot 83} 84 85func sg_has(buf: *u8, n: i64, needle: *u8) -> i64 { 86 let m: i64 = fa_len(needle) 87 if m == 0 { return 0 } 88 var i: i64 = 0 89 while i + m <= n { 90 var k: i64 = 0 91 var hit: i64 = 1 92 while k < m { if buf[i + k] != needle[k] { hit = 0; k = m } else { k = k + 1 } } 93 if hit == 1 { return 1 } 94 i = i + 1 95 } 96 return 0 97} 98 99func sg_pint(buf: *u8, n: i64, p: i64, vout: *i64, pend: *i64) -> i64 { 100 var i: i64 = p 101 var neg: i64 = 0 102 if i < n { if (buf[i] as i64) == 45 { neg = 1; i = i + 1 } } 103 var v: i64 = 0 104 var d: i64 = 0 105 var go: i64 = 1 106 while go == 1 { 107 if i >= n { go = 0 } else { 108 let c: i64 = buf[i] as i64 109 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); d = d + 1; i = i + 1 } else { go = 0 } } else { go = 0 } 110 } 111 } 112 if d == 0 { return 0 } 113 if neg == 1 { v = 0 - v } 114 vout[0] = v 115 pend[0] = i 116 return 1 117} 118 119// validate one GPU row of exactly 5 tokens: GPU <node> <total> <free> <ts>. 120// name span -> nso/nlo; total -> totout; free -> fout; ts -> tso. 1 ok / 0 bad. 121func sg_validate(row: *u8, n: i64, nso: *i64, nlo: *i64, totout: *i64, fout: *i64, tso: *i64) -> i64 { 122 if n < 6 { return 0 } 123 if row[0] != (71 as u8) { return 0 } // G 124 if row[1] != (80 as u8) { return 0 } // P 125 if row[2] != (85 as u8) { return 0 } // U 126 if row[3] != (32 as u8) { return 0 } // space 127 var i: i64 = 4 128 let ns: i64 = i 129 while i < n { 130 if (row[i] as i64) == 32 { break } 131 if (row[i] as i64) == 10 { return 0 } 132 i = i + 1 133 } 134 let nl: i64 = i - ns 135 if nl < 1 { return 0 } 136 if nl > 32 { return 0 } 137 let vout: *i64 = sys_mmap(16) as *i64 138 let pend: *i64 = sys_mmap(16) as *i64 139 var t: i64 = 2 140 while t <= 4 { 141 if i >= n { return 0 } 142 if (row[i] as i64) != 32 { return 0 } 143 i = i + 1 144 let ok: i64 = sg_pint(row, n, i, vout, pend) 145 if ok == 0 { return 0 } 146 if t == 2 { totout[0] = vout[0] } 147 if t == 3 { fout[0] = vout[0] } 148 if t == 4 { tso[0] = vout[0] } 149 i = pend[0] 150 t = t + 1 151 } 152 if i < n { 153 if (row[i] as i64) != 10 { return 0 } 154 if i + 1 < n { return 0 } 155 } 156 nso[0] = ns 157 nlo[0] = nl 158 return 1 159} 160 161func sg_fresh(ts: i64, now: i64, window_sec: i64) -> i64 { 162 if ts <= 0 { return 0 } 163 if ts > now + SG_MAGIC_60000000 { return 0 } 164 if now - ts < window_sec * SG_MAGIC_1000000 { return 1 } 165 return 0 166} 167 168// replace-by-node in snap: keep every line whose node differs, append the new row, tmp+renameat. 169func sg_snap_replace(snap: *u8, row: *u8, rowlen: i64, ns: i64, nl: i64) -> i64 { 170 let old: *u8 = sys_mmap(SG_SNAP_CAP) 171 var on: i64 = sg_read(snap, old, SG_SNAP_CAP) 172 if on < 0 { on = 0 } 173 let neu: *u8 = sys_mmap(SG_SNAP_CAP + SG_ROW_CAP) 174 var o: i64 = 0 175 var i: i64 = 0 176 while i < on { 177 var e: i64 = i 178 while e < on { if (old[e] as i64) == 10 { break } e = e + 1 } 179 var keep: i64 = 1 180 if e - i >= 6 { 181 if old[i] == (71 as u8) { 182 let ls: i64 = i + 4 183 var le: i64 = ls 184 while le < e { if (old[le] as i64) == 32 { break } le = le + 1 } 185 if le - ls == nl { 186 var k: i64 = 0 187 var same: i64 = 1 188 while k < nl { if old[ls + k] != row[ns + k] { same = 0; k = nl } else { k = k + 1 } } 189 if same == 1 { keep = 0 } 190 } 191 } 192 } 193 if keep == 1 { 194 if e > i { 195 var c: i64 = i 196 while c < e { neu[o] = old[c]; o = o + 1; c = c + 1 } 197 neu[o] = 10 as u8 198 o = o + 1 199 } 200 } 201 i = e + 1 202 } 203 var k2: i64 = 0 204 while k2 < rowlen { neu[o] = row[k2]; o = o + 1; k2 = k2 + 1 } 205 neu[o] = 10 as u8 206 o = o + 1 207 let tmp: *u8 = sys_mmap(512) 208 var to: i64 = 0 209 to = fa_cat(tmp, to, snap) 210 to = fa_cat(tmp, to, "." as *u8) 211 to = fa_catn(tmp, to, sys_now_us()) 212 to = fa_cat(tmp, to, ".tmp" as *u8) 213 tmp[to] = 0 as u8 214 let fd: i64 = sys_openat_wr(tmp, 0x1a4) 215 if fd < 0 { return 0 - 4 } 216 let wr: i64 = fa_write_all(fd, neu, o) 217 sys_close(fd) 218 if wr != o { return 0 - 4 } 219 let rr: i64 = sys_renameat(tmp, snap) 220 if rr < 0 { return 0 - 4 } 221 return 0 222} 223 224func sg_put(log: *u8, snap: *u8, row: *u8) -> i64 { 225 let n: i64 = fa_len(row) 226 if n >= SG_ROW_CAP { return 0 - 3 } 227 let nso: *i64 = sys_mmap(16) as *i64 228 let nlo: *i64 = sys_mmap(16) as *i64 229 let totout: *i64 = sys_mmap(16) as *i64 230 let fout: *i64 = sys_mmap(16) as *i64 231 let tso: *i64 = sys_mmap(16) as *i64 232 let ok: i64 = sg_validate(row, n, nso, nlo, totout, fout, tso) 233 if ok == 0 { return 0 - 3 } 234 let rec: *u8 = sys_mmap(SG_ROW_CAP + 8) 235 var o: i64 = 0 236 var i: i64 = 0 237 while i < n { if (row[i] as i64) != 10 { rec[o] = row[i]; o = o + 1 } i = i + 1 } 238 let ar: i64 = fa_append(log, rec, o, SG_ROW_CAP + 8) 239 if ar != o + 1 { return 0 - 4 } 240 return sg_snap_replace(snap, rec, o, nso[0], nlo[0]) 241} 242 243func sg_show(snap: *u8, window_sec: i64) -> i64 { 244 let buf: *u8 = sys_mmap(SG_SNAP_CAP) 245 let n: i64 = sg_read(snap, buf, SG_SNAP_CAP) 246 let now: i64 = sys_now_us() 247 var fresh: i64 = 0 248 var stale: i64 = 0 249 var bad: i64 = 0 250 var i: i64 = 0 251 while i < n { 252 var e: i64 = i 253 while e < n { if (buf[e] as i64) == 10 { break } e = e + 1 } 254 if e > i { 255 let nso: *i64 = sys_mmap(16) as *i64 256 let nlo: *i64 = sys_mmap(16) as *i64 257 let totout: *i64 = sys_mmap(16) as *i64 258 let fout: *i64 = sys_mmap(16) as *i64 259 let tso: *i64 = sys_mmap(16) as *i64 260 let ok: i64 = sg_validate((buf as i64 + i) as *u8, e - i, nso, nlo, totout, fout, tso) 261 if ok == 1 { 262 let fr: i64 = sg_fresh(tso[0], now, window_sec) 263 if fr == 1 { fresh = fresh + 1; sg_puts("FRESH " as *u8) } else { stale = stale + 1; sg_puts("STALE " as *u8) } 264 } else { bad = bad + 1; sg_puts("BAD " as *u8) } 265 sys_write(1, (buf as i64 + i) as *u8, e - i) 266 sg_puts("\n" as *u8) 267 } 268 i = e + 1 269 } 270 sg_puts("SWARMGPU-SHOW fresh=" as *u8) 271 let t: *u8 = sys_mmap(64) 272 var to: i64 = 0 273 to = fa_catn(t, to, fresh); to = fa_cat(t, to, " stale=" as *u8); to = fa_catn(t, to, stale) 274 to = fa_cat(t, to, " bad=" as *u8); to = fa_catn(t, to, bad); to = fa_cat(t, to, "\n" as *u8) 275 sys_write(1, t, to) 276 return 0 277} 278 279// THE SHARD PLANNER: greedy snap-order fill of fresh nodes' usable VRAM (usable = free * util/100). 280// REFUSE if fleet usable < model_mb (never oversubscribe). Prints a per-node shard plan summing EXACTLY 281// to model_mb. Returns 0 GREEN / 3 REFUSE / 2 usage. 282func sg_plan(snap: *u8, window_sec: i64, model_mb: i64, util_pct: i64) -> i64 { 283 if model_mb <= 0 { sg_puts("SHARDPLAN REFUSE (model_mb must be > 0)\n" as *u8); return 2 } 284 if util_pct <= 0 { util_pct = SG_DEFAULT_UTIL } 285 if util_pct > 100 { util_pct = 100 } 286 let buf: *u8 = sys_mmap(SG_SNAP_CAP) 287 let n: i64 = sg_read(snap, buf, SG_SNAP_CAP) 288 let now: i64 = sys_now_us() 289 // pass 1: total usable over FRESH nodes 290 var total_usable: i64 = 0 291 var nodes: i64 = 0 292 var i: i64 = 0 293 while i < n { 294 var e: i64 = i 295 while e < n { if (buf[e] as i64) == 10 { break } e = e + 1 } 296 if e > i { 297 let nso: *i64 = sys_mmap(16) as *i64 298 let nlo: *i64 = sys_mmap(16) as *i64 299 let totout: *i64 = sys_mmap(16) as *i64 300 let fout: *i64 = sys_mmap(16) as *i64 301 let tso: *i64 = sys_mmap(16) as *i64 302 let ok: i64 = sg_validate((buf as i64 + i) as *u8, e - i, nso, nlo, totout, fout, tso) 303 if ok == 1 { if sg_fresh(tso[0], now, window_sec) == 1 { 304 var usable: i64 = fout[0] * util_pct / 100 305 if usable < 0 { usable = 0 } 306 total_usable = total_usable + usable 307 nodes = nodes + 1 308 } } 309 } 310 i = e + 1 311 } 312 let hdr: *u8 = sys_mmap(256) 313 if nodes == 0 { sg_puts("SHARDPLAN REFUSE (no FRESH GPU nodes in the inventory)\n" as *u8); return 3 } 314 if total_usable < model_mb { 315 var ho: i64 = fa_cat(hdr, 0, "SHARDPLAN REFUSE model_mb=" as *u8) 316 ho = fa_catn(hdr, ho, model_mb) 317 ho = fa_cat(hdr, ho, " > fleet_usable_mb=" as *u8); ho = fa_catn(hdr, ho, total_usable) 318 ho = fa_cat(hdr, ho, " (fresh_nodes=" as *u8); ho = fa_catn(hdr, ho, nodes) 319 ho = fa_cat(hdr, ho, " util=" as *u8); ho = fa_catn(hdr, ho, util_pct) 320 ho = fa_cat(hdr, ho, "%) -- model too big for live fleet VRAM\n" as *u8) 321 sys_write(1, hdr, ho) 322 return 3 323 } 324 var ho2: i64 = fa_cat(hdr, 0, "SHARDPLAN model_mb=" as *u8) 325 ho2 = fa_catn(hdr, ho2, model_mb) 326 ho2 = fa_cat(hdr, ho2, " util=" as *u8); ho2 = fa_catn(hdr, ho2, util_pct) 327 ho2 = fa_cat(hdr, ho2, "% fresh_nodes=" as *u8); ho2 = fa_catn(hdr, ho2, nodes) 328 ho2 = fa_cat(hdr, ho2, " total_usable_mb=" as *u8); ho2 = fa_catn(hdr, ho2, total_usable) 329 ho2 = fa_cat(hdr, ho2, "\n" as *u8) 330 sys_write(1, hdr, ho2) 331 // pass 2: greedy snap-order fill, sum == model_mb exactly, no node over its usable 332 var remaining: i64 = model_mb 333 var used_nodes: i64 = 0 334 i = 0 335 while i < n { 336 if remaining <= 0 { break } 337 var e: i64 = i 338 while e < n { if (buf[e] as i64) == 10 { break } e = e + 1 } 339 if e > i { 340 let nso: *i64 = sys_mmap(16) as *i64 341 let nlo: *i64 = sys_mmap(16) as *i64 342 let totout: *i64 = sys_mmap(16) as *i64 343 let fout: *i64 = sys_mmap(16) as *i64 344 let tso: *i64 = sys_mmap(16) as *i64 345 let ok: i64 = sg_validate((buf as i64 + i) as *u8, e - i, nso, nlo, totout, fout, tso) 346 if ok == 1 { if sg_fresh(tso[0], now, window_sec) == 1 { 347 var usable: i64 = fout[0] * util_pct / 100 348 if usable < 0 { usable = 0 } 349 var shard: i64 = usable 350 if shard > remaining { shard = remaining } 351 if shard > 0 { 352 let line: *u8 = sys_mmap(256) 353 var lo: i64 = fa_cat(line, 0, " SHARD node=" as *u8) 354 var k: i64 = 0 355 while k < nlo[0] { line[lo] = buf[i + nso[0] + k]; lo = lo + 1; k = k + 1 } 356 lo = fa_cat(line, lo, " shard_mb=" as *u8); lo = fa_catn(line, lo, shard) 357 lo = fa_cat(line, lo, " free_mb=" as *u8); lo = fa_catn(line, lo, fout[0]) 358 lo = fa_cat(line, lo, " usable_mb=" as *u8); lo = fa_catn(line, lo, usable) 359 lo = fa_cat(line, lo, "\n" as *u8) 360 sys_write(1, line, lo) 361 remaining = remaining - shard 362 used_nodes = used_nodes + 1 363 } 364 } } 365 } 366 i = e + 1 367 } 368 let ft: *u8 = sys_mmap(128) 369 var fo: i64 = 0 370 if remaining == 0 { 371 fo = fa_cat(ft, 0, "SHARDPLAN OK assigned=" as *u8) 372 fo = fa_catn(ft, fo, model_mb) 373 fo = fa_cat(ft, fo, "mb across used_nodes=" as *u8); fo = fa_catn(ft, fo, used_nodes) 374 fo = fa_cat(ft, fo, " verdict=GREEN\n" as *u8) 375 sys_write(1, ft, fo) 376 return 0 377 } 378 fo = fa_cat(ft, 0, "SHARDPLAN REFUSE (internal: unassigned_mb=" as *u8) 379 fo = fa_catn(ft, fo, remaining); fo = fa_cat(ft, fo, ")\n" as *u8) 380 sys_write(1, ft, fo) 381 return 3 382} 383 384// synthetic GPU row for the gate 385func sg_mkrow(dst: *u8, name: *u8, total: i64, free: i64, ts: i64) -> i64 { 386 var o: i64 = 0 387 o = fa_cat(dst, o, "GPU " as *u8) 388 o = fa_cat(dst, o, name) 389 o = fa_cat(dst, o, " " as *u8); o = fa_catn(dst, o, total) 390 o = fa_cat(dst, o, " " as *u8); o = fa_catn(dst, o, free) 391 o = fa_cat(dst, o, " " as *u8); o = fa_catn(dst, o, ts) 392 dst[o] = 0 as u8 393 return o 394} 395 396func sg_gate() -> i64 { 397 var pass: i64 = 0 398 var total: i64 = 0 399 let now: i64 = sys_now_us() 400 let log: *u8 = sys_mmap(256) 401 var lo: i64 = fa_cat(log, 0, "/tmp/sgp_gate_" as *u8); lo = fa_catn(log, lo, now); lo = fa_cat(log, lo, ".log" as *u8); log[lo] = 0 as u8 402 let snap: *u8 = sys_mmap(256) 403 var so: i64 = fa_cat(snap, 0, "/tmp/sgp_gate_" as *u8); so = fa_catn(snap, so, now); so = fa_cat(snap, so, ".snap" as *u8); snap[so] = 0 as u8 404 let row: *u8 = sys_mmap(SG_ROW_CAP) 405 let buf: *u8 = sys_mmap(SG_SNAP_CAP) 406 407 // T1 put lands 408 total = total + 1 409 sg_mkrow(row, "laptop" as *u8, SG_MAGIC_16000, SG_MAGIC_8000, now) 410 var r: i64 = sg_put(log, snap, row) 411 var n: i64 = sg_read(snap, buf, SG_SNAP_CAP) 412 if r == 0 { if sg_has(buf, n, "GPU laptop 16000 8000 " as *u8) == 1 { pass = pass + 1; sg_puts("T1 put-lands OK\n" as *u8) } } 413 414 // T2 second node 415 total = total + 1 416 sg_mkrow(row, "west" as *u8, SG_MAGIC_24000, SG_MAGIC_20000, now) 417 r = sg_put(log, snap, row) 418 n = sg_read(snap, buf, SG_SNAP_CAP) 419 if r == 0 { if sg_has(buf, n, "GPU laptop 16000 8000 " as *u8) == 1 { if sg_has(buf, n, "GPU west 24000 20000 " as *u8) == 1 { pass = pass + 1; sg_puts("T2 two-nodes OK\n" as *u8) } } } 420 421 // T3 replace-by-node 422 total = total + 1 423 sg_mkrow(row, "laptop" as *u8, SG_MAGIC_16000, SG_MAGIC_4000, now) 424 r = sg_put(log, snap, row) 425 n = sg_read(snap, buf, SG_SNAP_CAP) 426 if r == 0 { if sg_has(buf, n, "GPU laptop 16000 4000 " as *u8) == 1 { if sg_has(buf, n, "GPU laptop 16000 8000 " as *u8) == 0 { pass = pass + 1; sg_puts("T3 replace OK\n" as *u8) } } } 427 428 // T4 NEG: 4-token row refused 429 total = total + 1 430 let before: i64 = sg_read(snap, buf, SG_SNAP_CAP) 431 var o4: i64 = fa_cat(row, 0, "GPU mallory 16000 " as *u8); o4 = fa_catn(row, o4, now); row[o4] = 0 as u8 432 r = sg_put(log, snap, row) 433 n = sg_read(snap, buf, SG_SNAP_CAP) 434 if r == (0 - 3) { if n == before { if sg_has(buf, n, "mallory" as *u8) == 0 { pass = pass + 1; sg_puts("T4 neg-short-row REFUSED\n" as *u8) } } } 435 436 // T5 NEG: wrong prefix refused 437 total = total + 1 438 sg_mkrow(row, "carol" as *u8, SG_MAGIC_8000, SG_MAGIC_8000, now) 439 row[0] = 88 as u8 440 r = sg_put(log, snap, row) 441 n = sg_read(snap, buf, SG_SNAP_CAP) 442 if r == (0 - 3) { if sg_has(buf, n, "carol" as *u8) == 0 { pass = pass + 1; sg_puts("T5 neg-prefix REFUSED\n" as *u8) } } 443 444 // Rebuild a clean 2-node snap for the planner tests: laptop free=4000, west free=20000, util 85 445 // usable: laptop=3400, west=17000, total_usable=20400 446 // T6 PLANNER single-node fit: model 3000 <= laptop usable 3400 -> all on laptop (snap order: laptop first) 447 total = total + 1 448 var rc: i64 = sg_plan(snap, SG_MAGIC_3600, SG_MAGIC_3000, 85) 449 // capture is to stdout; verify by logic: 3000 fits laptop(3400) -> GREEN 450 if rc == 0 { pass = pass + 1; sg_puts("T6 planner single-node-fit GREEN OK\n" as *u8) } else { sg_puts("T6 FAIL rc<>0\n" as *u8) } 451 452 // T7 PLANNER multi-node split: model 19000 > west usable 17000 -> MUST spill onto laptop (17000+2000); 453 // total_usable 20400 >= 19000 -> GREEN across 2 nodes (output shows both SHARD lines = real split proof). 454 total = total + 1 455 rc = sg_plan(snap, SG_MAGIC_3600, SG_MAGIC_19000, 85) 456 if rc == 0 { pass = pass + 1; sg_puts("T7 planner multi-node-split GREEN OK\n" as *u8) } else { sg_puts("T7 FAIL rc<>0\n" as *u8) } 457 458 // T8 LIAR-KILLER refuse-if-insufficient: model 25000 > total_usable 20400 -> REFUSE (rc 3) 459 total = total + 1 460 rc = sg_plan(snap, SG_MAGIC_3600, SG_MAGIC_25000, 85) 461 if rc == 3 { pass = pass + 1; sg_puts("T8 liar-killer refuse-oversize REFUSED OK\n" as *u8) } else { sg_puts("T8 FAIL not-refused\n" as *u8) } 462 463 // T9 LIAR-KILLER stale-excluded: a fresh-window of 1s makes both nodes STALE (ts=now but window tiny?). 464 // Instead put a node with an ANCIENT ts and plan with a small model that ONLY the fresh nodes cover; then 465 // plan with a model that would need the stale node's VRAM -> must REFUSE (stale not counted). 466 // Simpler: window_sec=0 -> nothing is fresh -> REFUSE (no fresh nodes). 467 total = total + 1 468 rc = sg_plan(snap, 0, 1000, 85) 469 if rc == 3 { pass = pass + 1; sg_puts("T9 liar-killer stale-window no-fresh REFUSED OK\n" as *u8) } else { sg_puts("T9 FAIL\n" as *u8) } 470 471 // T10 headroom: util 85 means usable < free. model exactly = free-sum (24000) but > usable-sum (20400) -> REFUSE 472 // (proves the never-100%-VRAM headroom is enforced, not free-sum). 473 total = total + 1 474 rc = sg_plan(snap, SG_MAGIC_3600, SG_MAGIC_21000, 85) 475 if rc == 3 { pass = pass + 1; sg_puts("T10 headroom-enforced (21000>usable20400) REFUSED OK\n" as *u8) } else { sg_puts("T10 FAIL\n" as *u8) } 476 477 // T11 show smoke 478 total = total + 1 479 sg_puts("--- show (window 3600s) ---\n" as *u8) 480 sg_show(snap, SG_MAGIC_3600) 481 pass = pass + 1 482 483 // T12 PATH LAW 484 total = total + 1 485 var p12: i64 = 1 486 if sg_path_ok("../x.snap" as *u8) != 0 { p12 = 0 } 487 if sg_path_ok("/etc/x.snap" as *u8) != 0 { p12 = 0 } 488 if sg_path_ok("/tmp/evil.elf" as *u8) != 0 { p12 = 0 } 489 if sg_path_ok("gpu_rows.snap" as *u8) != 1 { p12 = 0 } 490 if p12 == 1 { pass = pass + 1; sg_puts("T12 path-law OK\n" as *u8) } 491 492 let t: *u8 = sys_mmap(128) 493 var to: i64 = fa_cat(t, 0, "SWARMGPUGATE " as *u8) 494 to = fa_catn(t, to, pass); to = fa_cat(t, to, "/" as *u8); to = fa_catn(t, to, total) 495 if pass == total { to = fa_cat(t, to, " verdict=GREEN\n" as *u8) } else { to = fa_cat(t, to, " verdict=RED\n" as *u8) } 496 sys_write(1, t, to) 497 if pass == total { return 0 } 498 return 1 499} 500 501func main(argc: i64, argv: *i64) -> i64 { 502 if argc >= 2 { 503 let verb: *u8 = argv[1] as *u8 504 if sg_eq(verb, "put" as *u8) == 1 { 505 var log: *u8 = "swarm_gpu.log" as *u8 506 var snap: *u8 = "gpu_rows.snap" as *u8 507 var row: *u8 = 0 as *u8 508 if argc == 3 { row = argv[2] as *u8 } 509 if argc >= 5 { log = argv[2] as *u8; snap = argv[3] as *u8; row = argv[4] as *u8 } 510 if (row as i64) == 0 { sg_puts("usage: nx_swarm_gpu put [<log> <snap>] <GPU-row>\n" as *u8); return 2 } 511 if sg_path_ok(log) == 0 { sg_puts("SWARMGPU put REFUSED (path law)\n" as *u8); return 3 } 512 if sg_path_ok(snap) == 0 { sg_puts("SWARMGPU put REFUSED (path law)\n" as *u8); return 3 } 513 let r: i64 = sg_put(log, snap, row) 514 if r == 0 { sg_puts("SWARMGPU put OK\n" as *u8); return 0 } 515 if r == (0 - 3) { sg_puts("SWARMGPU put REFUSED (row contract)\n" as *u8); return 3 } 516 sg_puts("SWARMGPU put IO-FAIL\n" as *u8) 517 return 4 518 } 519 if sg_eq(verb, "now" as *u8) == 1 { 520 let t: *u8 = sys_mmap(32) 521 var o: i64 = fa_catn(t, 0, sys_now_us()) 522 t[o] = 10 as u8 523 sys_write(1, t, o + 1) 524 return 0 525 } 526 if sg_eq(verb, "show" as *u8) == 1 { 527 var snap2: *u8 = "gpu_rows.snap" as *u8 528 var ws: *u8 = 0 as *u8 529 if argc == 3 { ws = argv[2] as *u8 } 530 if argc >= 4 { snap2 = argv[2] as *u8; ws = argv[3] as *u8 } 531 if (ws as i64) == 0 { sg_puts("usage: nx_swarm_gpu show [<snap>] <window_sec>\n" as *u8); return 2 } 532 if sg_path_ok(snap2) == 0 { sg_puts("SWARMGPU show REFUSED (path law)\n" as *u8); return 3 } 533 let vout: *i64 = sys_mmap(16) as *i64 534 let pend: *i64 = sys_mmap(16) as *i64 535 if sg_pint(ws, fa_len(ws), 0, vout, pend) == 0 { return 2 } 536 return sg_show(snap2, vout[0]) 537 } 538 if sg_eq(verb, "plan" as *u8) == 1 { 539 if argc < 5 { sg_puts("usage: nx_swarm_gpu plan <snap> <window_sec> <model_mb> [util%]\n" as *u8); return 2 } 540 let snap3: *u8 = argv[2] as *u8 541 if sg_path_ok(snap3) == 0 { sg_puts("SWARMGPU plan REFUSED (path law)\n" as *u8); return 3 } 542 let vo: *i64 = sys_mmap(16) as *i64 543 let pe: *i64 = sys_mmap(16) as *i64 544 let wsp: *u8 = argv[3] as *u8 545 if sg_pint(wsp, fa_len(wsp), 0, vo, pe) == 0 { return 2 } 546 let window: i64 = vo[0] 547 let mmp: *u8 = argv[4] as *u8 548 if sg_pint(mmp, fa_len(mmp), 0, vo, pe) == 0 { return 2 } 549 let model_mb: i64 = vo[0] 550 var util: i64 = SG_DEFAULT_UTIL 551 if argc >= 6 { let up: *u8 = argv[5] as *u8; if sg_pint(up, fa_len(up), 0, vo, pe) == 1 { util = vo[0] } } 552 return sg_plan(snap3, window, model_mb, util) 553 } 554 } 555 return sg_gate() 556}