0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00
astro/benchmark/bench/render.js

122 lines
3.2 KiB
JavaScript
Raw Normal View History

import fs from 'node:fs/promises';
import http from 'node:http';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
2024-11-06 07:33:14 -05:00
import { markdownTable } from 'markdown-table';
import { waitUntilBusy } from 'port-authority';
2024-11-06 07:33:14 -05:00
import { exec } from 'tinyexec';
import { renderPages } from '../make-project/render-default.js';
2024-11-06 07:33:14 -05:00
import { astroBin, calculateStat } from './_util.js';
2023-03-06 09:55:44 -05:00
const port = 4322;
export const defaultProject = 'render-default';
/**
* @param {URL} projectDir
* @param {URL} outputFile
*/
export async function run(projectDir, outputFile) {
const root = fileURLToPath(projectDir);
console.log('Building...');
2024-08-28 09:52:49 -05:00
await exec(astroBin, ['build'], {
nodeOptions: {
cwd: root,
stdio: 'inherit',
},
2024-11-04 09:49:33 -05:00
throwOnError: true,
2023-03-06 09:55:44 -05:00
});
console.log('Previewing...');
2024-08-28 09:52:49 -05:00
const previewProcess = exec(astroBin, ['preview', '--port', port], {
nodeOptions: {
cwd: root,
stdio: 'inherit',
},
2024-11-04 09:49:33 -05:00
throwOnError: true,
2023-03-06 09:55:44 -05:00
});
console.log('Waiting for server ready...');
await waitUntilBusy(port, { timeout: 5000 });
console.log('Running benchmark...');
const result = await benchmarkRenderTime();
console.log('Killing server...');
if (!previewProcess.kill('SIGTERM')) {
console.warn('Failed to kill server process id:', previewProcess.pid);
}
console.log('Writing results to', fileURLToPath(outputFile));
await fs.writeFile(outputFile, JSON.stringify(result, null, 2));
console.log('Result preview:');
console.log('='.repeat(10));
console.log(`#### Render\n\n`);
console.log(printResult(result));
console.log('='.repeat(10));
console.log('Done!');
}
2024-11-06 07:33:14 -05:00
export async function benchmarkRenderTime(portToListen = port) {
2023-03-06 09:55:44 -05:00
/** @type {Record<string, number[]>} */
const result = {};
for (const fileName of renderPages) {
2023-03-06 09:55:44 -05:00
// Render each file 100 times and push to an array
for (let i = 0; i < 100; i++) {
const pathname = '/' + fileName.slice(0, -path.extname(fileName).length);
2024-11-06 07:33:14 -05:00
const renderTime = await fetchRenderTime(`http://localhost:${portToListen}${pathname}`);
2023-03-06 09:55:44 -05:00
if (!result[pathname]) result[pathname] = [];
result[pathname].push(renderTime);
}
}
/** @type {Record<string, import('./_util.js').Stat>} */
2023-03-06 09:55:44 -05:00
const processedResult = {};
for (const [pathname, times] of Object.entries(result)) {
// From the 100 results, calculate average, standard deviation, and max value
processedResult[pathname] = calculateStat(times);
2023-03-06 09:55:44 -05:00
}
return processedResult;
}
/**
* @param {Record<string, import('./_util.js').Stat>} result
2023-03-06 09:55:44 -05:00
*/
function printResult(result) {
return markdownTable(
[
['Page', 'Avg (ms)', 'Stdev (ms)', 'Max (ms)'],
...Object.entries(result).map(([pathname, { avg, stdev, max }]) => [
pathname,
avg.toFixed(2),
stdev.toFixed(2),
max.toFixed(2),
]),
],
{
align: ['l', 'r', 'r', 'r'],
2024-11-06 07:33:14 -05:00
},
2023-03-06 09:55:44 -05:00
);
}
/**
* Simple fetch utility to get the render time sent by `@benchmark/timer` in plain text
2023-03-06 09:55:44 -05:00
* @param {string} url
* @returns {Promise<number>}
*/
function fetchRenderTime(url) {
return new Promise((resolve, reject) => {
const req = http.request(url, (res) => {
res.setEncoding('utf8');
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('error', (e) => reject(e));
res.on('end', () => resolve(+data));
});
req.on('error', (e) => reject(e));
req.end();
});
}