chadtmiller.com/knotless
git that lives in your Atmosphere account
1
10
The slowest test binaries run more of their tests at once
harness/cov.mjs+31−0
| @@ -49,6 +49,18 @@ | ||
// a PDS without spaces, which only those binaries' tests reach. A line | ||
// that falls short with it names a binary to add. | ||
// | ||
// A test binary runs as many of its tests at once as the CPUs it is | ||
// given, as libtest reads the cgroup, which for the integration tests is | ||
// too few: they wait on their harness, their runner and git far more than | ||
// they compute, so one in a container of 4 CPUs used under one of them, | ||
// and its slowest binary, run 4 tests at a time, set the whole run's | ||
// length. So a binary takes a thread for each two minutes of its last | ||
// run's thread time (its length times the threads it had, which more | ||
// threads do not change), never fewer than the CPUs and at most | ||
// COV_TEST_THREADS (8): the long ones finish nearer the short ones, and | ||
// the rest are left as they were. RUST_TEST_THREADS, set, is kept for | ||
// every binary. | ||
// | ||
// COV_JOBS sets how many run at once; COV_CLEAN=1 builds every workspace | ||
// crate again first; COV_ONLY=a,b runs only the test binaries named, to | ||
// try something quickly (failing the gate); COV_TOOLCHAIN names the | ||
| @@ -135,6 +147,16 @@ const buildJobs = () => { | ||
return caps.length ? Math.max(1, Math.min(...caps)) : undefined | ||
} | ||
const limited = buildJobs() | ||
const [quota, period] = cgroup('cpu.max').split(' ') | ||
const cpus = quota === 'max' ? availableParallelism() : Math.max(1, Math.floor(quota / period)) | ||
const threadsMax = Number(process.env.COV_TEST_THREADS) || 8 | ||
/// How many of `t`'s tests run at once (see the top). | ||
const threadsOf = (t) => { | ||
if (process.env.RUST_TEST_THREADS) return Number(process.env.RUST_TEST_THREADS) | ||
const ms = times[timeKey(t)] ?? 0 | ||
const had = times[`${timeKey(t)} threads`] ?? cpus | ||
return Math.max(cpus, Math.min(threadsMax, Math.ceil((ms * had) / 120_000))) | ||
} | ||
if (!env.CARGO_BUILD_JOBS && limited) { | ||
env.CARGO_BUILD_JOBS = String(limited) | ||
console.log( | ||
| @@ -415,6 +437,11 @@ if (reused.length) | ||
if (runs.length) { | ||
for (const n of Object.values(nets).flat()) n.file = await n.up | ||
console.log(`harnesses up in ${took()}`) | ||
const more = runs.filter((r) => threadsOf(r) > cpus) | ||
if (more.length) | ||
console.log( | ||
`more than ${cpus} tests at once: ${more.map((r) => `${r.net} ${r.name} ${threadsOf(r)}`).join(', ')}`, | ||
) | ||
} | ||
| ||
let dirs = 0 | ||
| @@ -429,6 +456,7 @@ const one = (t, args = []) => | ||
const plans = `${profiles}-plans` | ||
rmSync(plans, { recursive: true, force: true }) | ||
const began = Date.now() | ||
const threads = threadsOf(t) | ||
// The least busy harness of its net. | ||
const net = nets[t.net].reduce((a, b) => (b.busy < a.busy ? b : a)) | ||
net.busy++ | ||
| @@ -440,6 +468,7 @@ const one = (t, args = []) => | ||
KNOTLESS_TEST_NET: net.file, | ||
KNOTLESS_DEV_ENV: t.net === 'spaces' ? spaces : '', | ||
CARGO_MANIFEST_DIR: t.dir, | ||
RUST_TEST_THREADS: String(threads), | ||
KNOTLESS_ANSWERS: answers, | ||
KNOTLESS_PLANS: plans, | ||
}, | ||
| @@ -465,6 +494,7 @@ const one = (t, args = []) => | ||
timedOut, | ||
out, | ||
ms: Date.now() - began, | ||
threads, | ||
profiles, | ||
answers, | ||
plans, | ||
| @@ -549,6 +579,7 @@ const runAll = async (list) => { | ||
continue | ||
} | ||
times[timeKey(r)] = r.ms | ||
times[`${timeKey(r)} threads`] = r.threads | ||
for (const f of readdirSync(r.profiles)) profile(join(r.profiles, f)) | ||
answer(r.answers) | ||
plan(r.plans) | ||