Files
scriptc/scripts/ci-corpus-costs.mjs
T
Chris Tate a08abf4abc Balance the corpus shards by each program's measured cost
The differential and llvm-differential corpus split their programs across the ten Linux test shards by a hash of
the program's name, so a shard's share of the measured work varied from 565 to 913 s (plain) and 868 to 1358 s
(sanitized). corpusSelect assigns programs longest first to the lightest shard by their measured cost per flavor
(tests/harness/corpus-costs.json: the differential plus llvm-differential milliseconds on a CI runner, medians of
four green main runs), giving 740-741 s and 1098-1099 s. Both harnesses still select identically, so a shard's
compile cache serves both. Programs without a measurement weigh as much as the median one.
scripts/ci-corpus-costs.mjs refreshes the table from downloaded CI shard logs.
2026-10-15 19:49:01 -05:00

44 lines
2.0 KiB
JavaScript

#!/usr/bin/env node
// Refreshes tests/harness/corpus-costs.json from the logs of the Linux test shards of green CI runs on main.
//
// Usage: node scripts/ci-corpus-costs.mjs <job log> ...
//
// Each log is a downloaded "test (Linux, plain|san, i/10)" job log. vitest prints every case slower than its
// threshold with its duration; for each program this takes the median of its differential and of its
// llvm-differential case over the logs of its flavor, and stores their sum. Programs without a measurement keep
// their current value.
import { readFileSync, writeFileSync } from "node:fs";
const path = new URL("../tests/harness/corpus-costs.json", import.meta.url);
const table = JSON.parse(readFileSync(path, "utf8"));
const samples = { plain: {}, san: {} };
for (const log of process.argv.slice(2)) {
// Logs keep the terminal color codes (ESC [ ... m); drop them before matching.
const text = readFileSync(log, "utf8")
.replaceAll(String.fromCharCode(27), "")
.replace(/\[[0-9;]*m/g, "");
const flavor = /SCRIPTC_SAN: 1\b/.test(text) ? "san" : "plain";
for (const line of text.split("\n")) {
const m =
/^\S+\s+✓ (llvm differential corpus|differential corpus) \(.*?\) > (.*?) {2}(\d+)ms$/.exec(
line,
);
if (!m) continue;
const lane = m[1] === "differential corpus" ? "differential" : "llvm";
(samples[flavor][m[2]] ??= { differential: [], llvm: [] })[lane].push(Number(m[3]));
}
}
const median = (values) => [...values].sort((a, b) => a - b)[Math.floor(values.length / 2)] ?? 0;
let updated = 0;
for (const flavor of ["plain", "san"])
for (const [key, lanes] of Object.entries(samples[flavor])) {
table[flavor][key] = median(lanes.differential) + median(lanes.llvm);
updated++;
}
for (const flavor of ["plain", "san"])
table[flavor] = Object.fromEntries(
Object.entries(table[flavor]).sort(([a], [b]) => a.localeCompare(b)),
);
writeFileSync(path, JSON.stringify(table, null, 2) + "\n");
console.log(`updated ${updated} program costs`);