All checks were successful
Deploy to Pages / build (push) Successful in 26s
88 lines
4 KiB
JavaScript
88 lines
4 KiB
JavaScript
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();
|
|
}
|