From 16f2d51531accabe310a35b28fe464437dfd4fdd Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 07:04:12 +0000 Subject: [PATCH] check:mst-clustering times its own CPU against a 1,500 ms budget, so a loaded box cannot fail it gates:post runs several checks at once, and a Date.now() budget measured the neighbours too: 723 ms of work failed the 3,000 ms budget once. The budget is now the process's own CPU (process.cpuUsage, user + system); the output says "CPU" and prints the wall time beside it as not budgeted. The budget moves to 1,500 ms, near the log-midpoint of the two builds measured here: the util takes 0.5 to 0.6 s of CPU, and the pre-924a185aa all-pairs path 4.4 to 6.9 s. Each side keeps about 2.5x, so a CPU 1.5x faster or slower still tells them apart. Two-sided, on the 8-core box under load: 24 copies at once all PASS (CPU mean 464 ms, max 625 ms, while their wall time reached 4,076 ms); the previous Date.now() check under the same load: 18 of 24 FAIL. Against the pre-924a185aa MSTClusteringUtil it FAILS: 4,418 to 6,921 ms of CPU across four runs. --- internal/check-mst-clustering.mjs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/check-mst-clustering.mjs b/internal/check-mst-clustering.mjs index 1cece684b..2c1a2f276 100644 --- a/internal/check-mst-clustering.mjs +++ b/internal/check-mst-clustering.mjs @@ -9,7 +9,16 @@ * MSTClusteringUtil now runs Prim in O(n²). * * ASSERTS (1) on sizes 2-400, capped and uncapped, the util's tree has the SAME edge count and total - * length as a reference all-pairs Kruskal written here; (2) 2,107 pins in under 3 s. + * length as a reference all-pairs Kruskal written here; (2) 2,107 pins in under 1.5 s of CPU. + * + * ★ CPU TIME, NOT WALL TIME (2026-09-29). gates:post runs several checks at once on a shared box, and a + * Date.now() budget measured the neighbours as well: 723 ms of work failed a 3,000 ms budget once. The + * budget is now this process's own CPU (process.cpuUsage, user + system), which the load does not + * inflate. Measured on a loaded 8-core box, the util takes about 0.5 s of CPU (24 copies at once: max + * 0.6 s, against 5.3 s of wall), and the pre-924a185aa all-pairs path 4.4 to 6.9 s. The budget sits near + * the log-midpoint of the two, 1,500 ms, so each side keeps a margin of about 2.5x (0.6 s is 2.4x under + * it, 4.4 s is 2.9x over): a CPU 1.5x faster (a laptop, a CI runner) cannot let the old path pass, and + * one 1.5x slower cannot fail the new path. * EXIT 0 pass · 1 fail */ import MST from '../library/utils/MSTClusteringUtil.js' @@ -39,11 +48,12 @@ for (const [n, cap] of [[2, Infinity], [3, Infinity], [50, Infinity], [400, Infi console.log(` ${ok ? 'ok ' : 'FAIL'} ${String(n).padStart(4)} pins, cap ${cap}: ${L.length} edges, length ${len.toFixed(4)} (reference ${ref.n}, ${ref.len.toFixed(4)})`) } console.warn = quiet -const t = Date.now() +const t = Date.now(), cpu0 = process.cpuUsage() const L = new MST({}).generateAllMSTLines({ A: pts(1500, 1.1), B: pts(607, 0.9) }) -const ms = Date.now() - t -const fast = ms < 3000 && L.length === 2105 +const cpu = process.cpuUsage(cpu0), ms = Math.round((cpu.user + cpu.system) / 1000), wall = Date.now() - t +const BUDGET_CPU_MS = 1500 +const fast = ms < BUDGET_CPU_MS && L.length === 2105 if (!fast) bad++ -console.log(` ${fast ? 'ok ' : 'FAIL'} 2,107 pins in two groups: ${L.length} lines in ${ms} ms (budget 3000 ms)`) +console.log(` ${fast ? 'ok ' : 'FAIL'} 2,107 pins in two groups: ${L.length} lines in ${ms} ms CPU (budget ${BUDGET_CPU_MS} ms CPU; wall ${wall} ms, not budgeted)`) if (bad) { console.log(`MST-CLUSTERING: FAIL — ${bad} case(s).`); process.exit(1) } console.log('MST-CLUSTERING: PASS — a true minimum spanning tree/forest, and fast enough for the main thread.')