This commit is contained in:
parent
7959fe85cc
commit
b9485e4097
32 changed files with 4692 additions and 65 deletions
88
scripts/benchmark-planner.mjs
Normal file
88
scripts/benchmark-planner.mjs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { createServer } from 'vite';
|
||||
|
||||
const server = await createServer({ server: { middlewareMode: true }, appType: 'custom' });
|
||||
try {
|
||||
const [{ planFlooring }, { planSkirting }, { plannerCases }, metrics] = await Promise.all([
|
||||
server.ssrLoadModule('/src/lib/flooring.ts'),
|
||||
server.ssrLoadModule('/src/lib/skirting.ts'),
|
||||
server.ssrLoadModule('/benchmarks/cases.ts'),
|
||||
server.ssrLoadModule('/benchmarks/metrics.ts'),
|
||||
]);
|
||||
const cases = plannerCases();
|
||||
const random = process.argv.includes('--random');
|
||||
if (random) cases.forEach(({ project }) => (project.flooring.optimisation = 'random'));
|
||||
const results = [];
|
||||
// Warm up the geometry and planners before taking timings.
|
||||
planFlooring(cases[0].project.rooms, cases[0].project.flooring, cases[0].project.tolerance);
|
||||
for (const { name, project: p } of cases) {
|
||||
const start = performance.now();
|
||||
const flooring = planFlooring(p.rooms, p.flooring, p.tolerance);
|
||||
const floorMs = performance.now() - start;
|
||||
const skirtStart = performance.now();
|
||||
const skirting = planSkirting(p.rooms, p.skirting, p.tolerance);
|
||||
results.push({
|
||||
name,
|
||||
flooring: metrics.flooringMetrics(flooring, p.flooring),
|
||||
skirting: metrics.skirtingMetrics(skirting),
|
||||
floorMs: Math.round(floorMs * 10) / 10,
|
||||
skirtMs: Math.round((performance.now() - skirtStart) * 10) / 10,
|
||||
});
|
||||
}
|
||||
const baselinePath = new URL('../benchmarks/baseline.json', import.meta.url);
|
||||
if (process.argv.includes('--write-baseline')) {
|
||||
await writeFile(baselinePath, JSON.stringify(results, null, 2) + '\n');
|
||||
console.log(`Recorded ${results.length} baseline cases.`);
|
||||
} else {
|
||||
const comparison = process.argv
|
||||
.find((arg) => arg.startsWith('--compare='))
|
||||
?.slice('--compare='.length);
|
||||
const baseline = JSON.parse(await readFile(comparison ?? baselinePath, 'utf8'));
|
||||
const sum = (rows, key, metric) => rows.reduce((total, row) => total + row[key][metric], 0);
|
||||
const summary = [];
|
||||
for (const kind of ['flooring', 'skirting']) {
|
||||
const compare =
|
||||
kind === 'flooring'
|
||||
? (a, b) => metrics.compareFlooring(a, b, random)
|
||||
: metrics.compareSkirting;
|
||||
const changes = results.map((result) => {
|
||||
const previous = baseline.find((row) => row.name === result.name);
|
||||
if (!previous) throw new Error(`No baseline for ${result.name}`);
|
||||
return { name: result.name, change: compare(result[kind], previous[kind]) };
|
||||
});
|
||||
summary.push({
|
||||
planner: kind,
|
||||
improved: changes.filter((row) => row.change < 0).length,
|
||||
tied: changes.filter((row) => row.change === 0).length,
|
||||
regressed: changes.filter((row) => row.change > 0).length,
|
||||
'cuts before': sum(baseline, kind, 'cuts'),
|
||||
'cuts after': sum(results, kind, 'cuts'),
|
||||
'boards before': sum(baseline, kind, 'boards'),
|
||||
'boards after': sum(results, kind, 'boards'),
|
||||
errors: sum(results, kind, 'errors'),
|
||||
});
|
||||
const regressions = changes.filter((row) => row.change > 0).map((row) => row.name);
|
||||
if (regressions.length) console.log(`${kind} regressions: ${regressions.join(', ')}`);
|
||||
}
|
||||
console.table(summary);
|
||||
console.table(
|
||||
['shapingCuts', 'ripCuts', 'patternPenalty', 'repeats', 'similarSteps', 'staircases'].map(
|
||||
(metric) => ({
|
||||
metric,
|
||||
before: sum(baseline, 'flooring', metric),
|
||||
after: sum(results, 'flooring', metric),
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
for (const key of ['floorMs', 'skirtMs']) {
|
||||
const times = results.map((row) => row[key]).sort((a, b) => a - b);
|
||||
console.log(
|
||||
`${key}: total ${times.reduce((a, b) => a + b, 0).toFixed(1)} ms; p95 ${times[Math.floor(times.length * 0.95)]} ms; max ${times.at(-1)} ms`,
|
||||
);
|
||||
}
|
||||
const output = process.argv.find((arg) => arg.startsWith('--output='))?.slice('--output='.length);
|
||||
if (output) await writeFile(output, JSON.stringify(results, null, 2) + '\n');
|
||||
} finally {
|
||||
await server.close();
|
||||
}
|
||||
Loading…
Reference in a new issue