From bf71e965c7d1045a589fe569a5de4daeba99f8d7 Mon Sep 17 00:00:00 2001 From: bluwy Date: Sat, 25 Feb 2023 00:03:31 +0800 Subject: [PATCH 01/35] Add timer setting --- packages/astro/src/@types/astro.ts | 2 + packages/astro/src/core/build/static-build.ts | 21 ++++--- packages/astro/src/core/config/settings.ts | 2 + packages/astro/src/core/config/timer.ts | 60 +++++++++++++++++++ pnpm-workspace.yaml | 1 + 5 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 packages/astro/src/core/config/timer.ts diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 868ba7fc2aef..199250d21efa 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -16,6 +16,7 @@ import type { z } from 'zod'; import type { SerializedSSRManifest } from '../core/app/types'; import type { PageBuildData } from '../core/build/types'; import type { AstroConfigSchema } from '../core/config'; +import type { AstroTimer } from '../core/config/timer'; import type { AstroCookies } from '../core/cookies'; import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server'; import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js'; @@ -992,6 +993,7 @@ export interface AstroSettings { tsConfigPath: string | undefined; watchFiles: string[]; forceDisableTelemetry: boolean; + timer: AstroTimer; } export type AsyncRendererComponentFn = ( diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 1a6d3e367924..4fde3016cc50 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -33,6 +33,8 @@ export async function staticBuild(opts: StaticBuildOptions) { throw new AstroError(AstroErrorData.NoAdapterInstalled); } + settings.timer.start('SSR build'); + // The pages to be built for rendering purposes. const pageInput = new Set(); @@ -43,10 +45,6 @@ export async function staticBuild(opts: StaticBuildOptions) { // Build internals needed by the CSS plugin const internals = createBuildInternals(); - const timer: Record = {}; - - timer.buildStart = performance.now(); - for (const [component, pageData] of Object.entries(allPages)) { const astroModuleURL = new URL('./' + component, settings.config.root); const astroModuleId = prependForwardSlash(component); @@ -70,10 +68,13 @@ export async function staticBuild(opts: StaticBuildOptions) { registerAllPlugins(container); // Build your project (SSR application code, assets, client JS, etc.) - timer.ssr = performance.now(); + const ssrTime = performance.now(); info(opts.logging, 'build', `Building ${settings.config.output} entrypoints...`); const ssrOutput = await ssrBuild(opts, internals, pageInput, container); - info(opts.logging, 'build', dim(`Completed in ${getTimeStat(timer.ssr, performance.now())}.`)); + info(opts.logging, 'build', dim(`Completed in ${getTimeStat(ssrTime, performance.now())}.`)); + + settings.timer.end('SSR build'); + settings.timer.end('Client build'); const rendererClientEntrypoints = settings.renderers .map((r) => r.clientEntrypoint) @@ -91,23 +92,27 @@ export async function staticBuild(opts: StaticBuildOptions) { } // Run client build first, so the assets can be fed into the SSR rendered version. - timer.clientBuild = performance.now(); const clientOutput = await clientBuild(opts, internals, clientInput, container); - timer.generate = performance.now(); await runPostBuildHooks(container, ssrOutput, clientOutput); + settings.timer.end('Client build'); + switch (settings.config.output) { case 'static': { + settings.timer.start('Static generate'); await generatePages(opts, internals); await cleanServerOutput(opts); + settings.timer.end('Static generate'); return; } case 'server': { + settings.timer.start('Server generate'); await generatePages(opts, internals); await cleanStaticOutput(opts, internals); info(opts.logging, null, `\n${bgMagenta(black(' finalizing server assets '))}\n`); await ssrMoveAssets(opts); + settings.timer.end('Server generate'); return; } } diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts index c9ac9da5eb67..05e3da1c3745 100644 --- a/packages/astro/src/core/config/settings.ts +++ b/packages/astro/src/core/config/settings.ts @@ -5,6 +5,7 @@ import { fileURLToPath, pathToFileURL } from 'url'; import jsxRenderer from '../../jsx/renderer.js'; import { createDefaultDevConfig } from './config.js'; import { loadTSConfig } from './tsconfig.js'; +import { AstroTimer } from './timer.js'; export function createBaseSettings(config: AstroConfig): AstroSettings { return { @@ -19,6 +20,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings { scripts: [], watchFiles: [], forceDisableTelemetry: false, + timer: new AstroTimer(), }; } diff --git a/packages/astro/src/core/config/timer.ts b/packages/astro/src/core/config/timer.ts new file mode 100644 index 000000000000..d661295b3d30 --- /dev/null +++ b/packages/astro/src/core/config/timer.ts @@ -0,0 +1,60 @@ +interface Stat { + elapsedTime: number; + heapUsedChange: number; + heapUsedTotal: number; +} + +interface OngoingStat { + startTime: number; + startHeap: number; +} + +/** + * Timer to track certain operations' performance. Used by Astro's scripts only. + * Set `process.env.ENABLE_ASTRO_TIMER` truthy to enable. + */ +export class AstroTimer { + private enabled: boolean; + private ongoingTimers: Map = new Map(); + private stats: Record = {}; + + constructor() { + this.enabled = !!process.env.ENABLE_ASTRO_TIMER; + } + + /** + * Start a timer for a scope with a given name. + */ + start(name: string) { + if (!this.enabled) return; + this.ongoingTimers.set(name, { + startTime: performance.now(), + startHeap: process.memoryUsage().heapUsed, + }); + } + + /** + * End a timer for a scope with a given name. + */ + end(name: string) { + if (!this.enabled) return; + const stat = this.ongoingTimers.get(name); + if (!stat) return; + const endHeap = process.memoryUsage().heapUsed; + this.stats[name] = { + elapsedTime: performance.now() - stat.startTime, + heapUsedChange: endHeap - stat.startHeap, + heapUsedTotal: endHeap, + }; + this.ongoingTimers.delete(name); + } + + /** + * Write stats to `process.env.ENABLE_ASTRO_TIMER` + */ + finish() { + if (!this.enabled) return; + // @ts-expect-error + process.env.ASTRO_BENCH_STATS = this.stats; + } +} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 937aa21ca887..fb7deebf6742 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,3 +3,4 @@ packages: - 'examples/**/*' - 'smoke/**/*' - 'scripts' + - 'benchmark' From 0d7a1486be04aaa48f89d83b77ba6b268124a704 Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 27 Feb 2023 16:23:43 +0800 Subject: [PATCH 02/35] Setup benchmark code --- .eslintrc.cjs | 6 + .gitignore | 2 + benchmark/_bench-template.js | 15 +++ benchmark/bench-memory.js | 15 +++ benchmark/bench-server-stress.js | 74 ++++++++++++ benchmark/index.js | 76 ++++++++++++ benchmark/make-project.js | 88 ++++++++++++++ benchmark/package.json | 17 +++ package.json | 3 +- pnpm-lock.yaml | 196 ++++++++++++++++++++++++++++++- 10 files changed, 489 insertions(+), 3 deletions(-) create mode 100644 benchmark/_bench-template.js create mode 100644 benchmark/bench-memory.js create mode 100644 benchmark/bench-server-stress.js create mode 100755 benchmark/index.js create mode 100644 benchmark/make-project.js create mode 100644 benchmark/package.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2652a454ee2b..a7ec9c8eaccf 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -38,5 +38,11 @@ module.exports = { 'no-console': ['error', { allow: ['warn', 'error', 'info', 'debug'] }], }, }, + { + files: ['benchmark/**/*.js'], + rules: { + 'no-console': 'off', + }, + }, ], }; diff --git a/.gitignore b/.gitignore index df661ac83e9a..00a4a77236c2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ dist/ _site/ scripts/smoke/*-main/ scripts/memory/project/src/pages/ +benchmark/projects/ +benchmark/results/ *.log package-lock.json .turbo/ diff --git a/benchmark/_bench-template.js b/benchmark/_bench-template.js new file mode 100644 index 000000000000..7871e4096506 --- /dev/null +++ b/benchmark/_bench-template.js @@ -0,0 +1,15 @@ +/** Default project to run for this benchmark if not specified */ +export const defaultProject = 'project-name'; + +/** + * @param {URL} projectDir + * @param {URL} outputFile + */ +export async function run(projectDir, outputFile) {} + +/** + * + * @param {any} outputA + * @param {any} outputB + */ +export async function compare(outputA, outputB) {} diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js new file mode 100644 index 000000000000..9445cbdfdf92 --- /dev/null +++ b/benchmark/bench-memory.js @@ -0,0 +1,15 @@ +/** Default project to run for this benchmark if not specified */ +export const defaultProject = 'memory-default'; + +/** + * @param {URL} projectDir + * @param {URL} outputFile + */ +export async function run(projectDir, outputFile) {} + +/** + * + * @param {any} outputA + * @param {any} outputB + */ +export async function compare(outputA, outputB) {} diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js new file mode 100644 index 000000000000..af3a3ecfd020 --- /dev/null +++ b/benchmark/bench-server-stress.js @@ -0,0 +1,74 @@ +import fs from 'fs/promises'; +import { fileURLToPath } from 'url'; +import autocannon from 'autocannon'; +import { execaCommand } from 'execa'; +import { waitUntilBusy } from 'port-authority'; + +const astro = fileURLToPath(new URL('../packages/astro/astro.js', import.meta.url)); +const port = 4321; + +export const defaultProject = 'server-stress-default'; + +/** + * @param {URL} projectDir + * @param {URL} outputFile + */ +export async function run(projectDir, outputFile) { + const root = fileURLToPath(projectDir); + + console.log('Building...'); + await execaCommand(`${astro} build`, { + cwd: root, + stdio: 'inherit', + }); + + console.log('Previewing...'); + const previewProcess = execaCommand(`${astro} preview --port ${port}`, { + cwd: root, + stdio: 'inherit', + }); + + console.log('Waiting for server ready...'); + await waitUntilBusy(port, { timeout: 5000 }); + + console.log('Running benchmark...'); + const result = await benchmarkCannon(); + + 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('Done!'); +} + +export async function compare(outputA, outputB) {} + +/** + * @returns {Promise} + */ +async function benchmarkCannon() { + return new Promise((resolve, reject) => { + const instance = autocannon( + { + url: `http://localhost:${port}`, + connections: 100, + duration: 30, + pipelining: 10, + }, + (err, result) => { + if (err) { + reject(err); + } else { + // @ts-expect-error untyped but documented + instance.stop(); + resolve(result); + } + } + ); + autocannon.track(instance); + }); +} diff --git a/benchmark/index.js b/benchmark/index.js new file mode 100755 index 000000000000..35bc267e2c73 --- /dev/null +++ b/benchmark/index.js @@ -0,0 +1,76 @@ +import fs from 'fs/promises'; +import path from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; +import mri from 'mri'; +import { makeProject } from './make-project.js'; + +/** + * astro-benchmark [command] [options] + * + * Options: + * --project + * --output + * --compare , + * + * Examples: + * ```bash + * # Run all benchmarks + * astro-benchmark + * ``` + */ + +const args = mri(process.argv.slice(2)); + +const commandName = args._[0]; +const benchmarks = { + memory: () => import('./bench-memory.js'), + 'server-stress': () => import('./bench-server-stress.js'), +}; + +if (commandName && !(commandName in benchmarks)) { + console.error(`Invalid benchmark name: ${commandName}`); + process.exit(1); +} + +// If we're running a compare command, run it and exit +if (args.compare) { + const [outputA, outputB] = args.compare.split(',').map((s) => s.trim()); + if (!outputA || !outputB) { + console.error( + `Invalid --compare value: ${args.compare}, must have two files, separated by a comma.` + ); + process.exit(1); + } + const bench = benchmarks[commandName]; + const benchMod = await bench(); + await benchMod.compare(path.resolve(outputA), path.resolve(outputB)); + process.exit(0); +} + +const defaultOutputFile = args.output + ? path.resolve(args.output) + : fileURLToPath(new URL(`./results/bench-${Date.now()}.json`, import.meta.url)); + +// Prepare output file directory +await fs.mkdir(path.dirname(defaultOutputFile), { recursive: true }); + +if (commandName) { + // Run single benchmark + const bench = benchmarks[commandName]; + const benchMod = await bench(); + const projectDir = await makeProject(args.project || benchMod.defaultProject); + const outputFile = pathToFileURL(defaultOutputFile); + await benchMod.run(projectDir, outputFile); +} else { + // Run all benchmarks + for (const name in benchmarks) { + const bench = benchmarks[name]; + const benchMod = await bench(); + const projectDir = await makeProject(args.project || benchMod.defaultProject); + // Prefix output file with benchmark name to avoid conflict + const parsed = path.parse(defaultOutputFile); + parsed.base = `${name}-${parsed.base}`; + const outputFile = pathToFileURL(path.format(parsed)); + await benchMod.run(projectDir, outputFile); + } +} diff --git a/benchmark/make-project.js b/benchmark/make-project.js new file mode 100644 index 000000000000..18bdea2349ec --- /dev/null +++ b/benchmark/make-project.js @@ -0,0 +1,88 @@ +import fs from 'fs/promises'; + +/** + * @param {string} name + */ +export async function makeProject(name) { + console.log('Making project:', name); + const projectDir = new URL(`./projects/${name}/`, import.meta.url); + + switch (name) { + case 'memory-default': + await makeProjectMemoryDefault(projectDir); + break; + case 'server-stress-default': + await makeProjectServerStressDefault(projectDir); + break; + default: + throw new Error('Unknown project name: ' + name); + } + + console.log('Finished making project:', name); + return projectDir; +} + +async function makeProjectMemoryDefault(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); + + const promises = []; + + for (let i = 0; i < 500; i++) { + const content = `\ +--- +const i = ${i}; +--- + +{i} +`; + promises.push( + fs.writeFile(new URL(`./src/pages/page-${i}.astro`, projectDir), content, 'utf-8') + ); + } + + await Promise.all(promises); +} + +async function makeProjectServerStressDefault(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); + + await fs.writeFile( + new URL('./src/pages/index.astro', projectDir), + `\ +--- +const content = + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; +--- + + + + + + + Astro + + +

Astro

+
+ ${Array.from({ length: 60 }).map(() => '

{content}

')} +
+ +`, + 'utf-8' + ); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; +import nodejs from '@astrojs/node'; + +export default defineConfig({ + output: 'server', + adapter: nodejs({ mode: 'standalone' }), +});`, + 'utf-8' + ); +} diff --git a/benchmark/package.json b/benchmark/package.json new file mode 100644 index 000000000000..2c2c04f52053 --- /dev/null +++ b/benchmark/package.json @@ -0,0 +1,17 @@ +{ + "name": "astro-benchmark", + "private": true, + "type": "module", + "version": "0.0.0", + "bin": { + "astro-benchmark": "./index.js" + }, + "dependencies": { + "@astrojs/node": "workspace:*", + "astro": "workspace:*", + "autocannon": "^7.10.0", + "execa": "^6.1.0", + "mri": "^1.2.0", + "port-authority": "^2.0.1" + } +} \ No newline at end of file diff --git a/package.json b/package.json index f119d34d0598..be37d41c7159 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,8 @@ } }, "dependencies": { - "@astrojs/webapi": "workspace:*" + "@astrojs/webapi": "workspace:*", + "astro-benchmark": "workspace:*" }, "devDependencies": { "@changesets/changelog-github": "0.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 257e854965ce..17c9692147ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,6 +21,7 @@ importers: '@types/node': ^18.7.21 '@typescript-eslint/eslint-plugin': ^5.27.1 '@typescript-eslint/parser': ^5.27.1 + astro-benchmark: workspace:* del: ^7.0.0 esbuild: ^0.15.18 eslint: ^8.17.0 @@ -38,6 +39,7 @@ importers: typescript: ~4.7.3 dependencies: '@astrojs/webapi': link:packages/webapi + astro-benchmark: link:benchmark devDependencies: '@changesets/changelog-github': 0.4.4 '@changesets/cli': 2.23.0_kcozqtpxuwjzskw6zg5royevn4 @@ -61,6 +63,22 @@ importers: turbo: 1.2.5 typescript: 4.7.4 + benchmark: + specifiers: + '@astrojs/node': workspace:* + astro: workspace:* + autocannon: ^7.10.0 + execa: ^6.1.0 + mri: ^1.2.0 + port-authority: ^2.0.1 + dependencies: + '@astrojs/node': link:../packages/integrations/node + astro: link:../packages/astro + autocannon: 7.10.0 + execa: 6.1.0 + mri: 1.2.0 + port-authority: 2.0.1 + examples/basics: specifiers: astro: ^2.0.15 @@ -3878,6 +3896,10 @@ packages: leven: 3.1.0 dev: false + /@assemblyscript/loader/0.19.23: + resolution: {integrity: sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==} + dev: false + /@astro-community/astro-embed-integration/0.1.2_astro@packages+astro: resolution: {integrity: sha512-ONBDHkOUZ7ssQNzRc5XRZtBBJR0zC68Gm2FCm5w6fxxciDkRkU9Zn9BSssgaNrLPfsXycxFLtQZT3dX9ZPsAxw==} peerDependencies: @@ -5632,6 +5654,13 @@ packages: mime: 3.0.0 dev: true + /@colors/colors/1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + requiresBuild: true + dev: false + optional: true + /@csstools/postcss-cascade-layers/1.1.1_postcss@8.4.21: resolution: {integrity: sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==} engines: {node: ^12 || ^14 || >=16} @@ -8139,11 +8168,44 @@ packages: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} dev: false + /asynckit/0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + /at-least-node/1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} dev: false + /autocannon/7.10.0: + resolution: {integrity: sha512-PY1UrXL4NHE7J0hA6GGN2r8xjiAePS/bii3Hz7NOvp4JO3xDNBgRftDjfAxj1t6FDWXiXEOuKF/pdDiisIS8ZA==} + hasBin: true + dependencies: + chalk: 4.1.2 + char-spinner: 1.0.1 + cli-table3: 0.6.3 + color-support: 1.1.3 + cross-argv: 2.0.0 + form-data: 4.0.0 + has-async-hooks: 1.0.0 + hdr-histogram-js: 3.0.0 + hdr-histogram-percentiles-obj: 3.0.0 + http-parser-js: 0.5.8 + hyperid: 3.1.1 + lodash.chunk: 4.2.0 + lodash.clonedeep: 4.5.0 + lodash.flatten: 4.4.0 + manage-path: 2.0.0 + on-net-listen: 1.1.2 + pretty-bytes: 5.6.0 + progress: 2.0.3 + reinterval: 1.1.0 + retimer: 3.0.0 + semver: 7.3.8 + subarg: 1.0.0 + timestring: 6.0.0 + dev: false + /autoprefixer/10.4.13_postcss@8.4.21: resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} engines: {node: ^10 || ^12 || >=14} @@ -8524,6 +8586,10 @@ packages: engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: false + /char-spinner/1.0.1: + resolution: {integrity: sha512-acv43vqJ0+N0rD+Uw3pDHSxP30FHrywu2NO6/wBaHChJIizpDeBUd6NjqhNhy9LGaEAhZAXn46QzmlAvIWd16g==} + dev: false + /character-entities-html4/2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -8622,6 +8688,15 @@ packages: engines: {node: '>=6'} dev: false + /cli-table3/0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: false + /cliui/6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -8696,6 +8771,13 @@ packages: resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} dev: false + /combined-stream/1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + /comma-separated-tokens/2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: false @@ -8774,6 +8856,10 @@ packages: resolution: {integrity: sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==} dev: true + /cross-argv/2.0.0: + resolution: {integrity: sha512-YIaY9TR5Nxeb8SMdtrU8asWVM4jqJDNDYlKV21LxtYcfNJhp1kEsgSa6qXwXgzN0WQWGODps0+TlGp2xQSHwOg==} + dev: false + /cross-spawn/5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -9046,6 +9132,11 @@ packages: slash: 4.0.0 dev: true + /delayed-stream/1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + /delegates/1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} dev: false @@ -10030,6 +10121,15 @@ packages: dependencies: is-callable: 1.2.7 + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + /format/0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -10376,6 +10476,10 @@ packages: engines: {node: '>=6'} dev: true + /has-async-hooks/1.0.0: + resolution: {integrity: sha512-YF0VPGjkxr7AyyQQNykX8zK4PvtEDsUJAPqwu06UFz1lb6EvI53sPh5H1kWxg8NXI5LsfRCZ8uX9NkYDZBb/mw==} + dev: false + /has-bigints/1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -10579,6 +10683,19 @@ packages: space-separated-tokens: 2.0.2 dev: false + /hdr-histogram-js/3.0.0: + resolution: {integrity: sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==} + engines: {node: '>=14'} + dependencies: + '@assemblyscript/loader': 0.19.23 + base64-js: 1.5.1 + pako: 1.0.11 + dev: false + + /hdr-histogram-percentiles-obj/3.0.0: + resolution: {integrity: sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==} + dev: false + /he/1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true @@ -10629,6 +10746,10 @@ packages: statuses: 2.0.1 toidentifier: 1.0.1 + /http-parser-js/0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + dev: false + /http-proxy-agent/4.0.1: resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} engines: {node: '>= 6'} @@ -10662,6 +10783,13 @@ packages: resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} engines: {node: '>=12.20.0'} + /hyperid/3.1.1: + resolution: {integrity: sha512-RveV33kIksycSf7HLkq1sHB5wW0OwuX8ot8MYnY++gaaPXGFfKpBncHrAWxdpuEeRlazUMGWefwP1w6o6GaumA==} + dependencies: + uuid: 8.3.2 + uuid-parse: 1.1.0 + dev: false + /iconv-lite/0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -11265,10 +11393,22 @@ packages: dependencies: p-locate: 5.0.0 + /lodash.chunk/4.2.0: + resolution: {integrity: sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==} + dev: false + + /lodash.clonedeep/4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: false + /lodash.debounce/4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: false + /lodash.flatten/4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + dev: false + /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -11372,6 +11512,10 @@ packages: semver: 6.3.0 dev: false + /manage-path/2.0.0: + resolution: {integrity: sha512-NJhyB+PJYTpxhxZJ3lecIGgh4kwIY2RAh44XvAz9UlqthlQwtPBf62uBVR8XaD8CRuSjQ6TnZH2lNJkbLPZM2A==} + dev: false + /map-obj/1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -11974,14 +12118,12 @@ packages: /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - dev: true /mime-types/2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 - dev: true /mime/1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} @@ -12082,6 +12224,10 @@ packages: /minimist/1.2.7: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} + /minimist/1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: false + /minipass/3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -12381,6 +12527,11 @@ packages: ee-first: 1.1.1 dev: false + /on-net-listen/1.1.2: + resolution: {integrity: sha512-y1HRYy8s/RlcBvDUwKXSmkODMdx4KSuIvloCnQYJ2LdBBC1asY4HtfhXwe3UWknLakATZDnbzht2Ijw3M1EqFg==} + engines: {node: '>=9.4.0 || ^8.9.4'} + dev: false + /once/1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -12553,6 +12704,10 @@ packages: netmask: 2.0.2 dev: true + /pako/1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: false + /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -12723,6 +12878,10 @@ packages: playwright-core: 1.30.0 dev: true + /port-authority/2.0.1: + resolution: {integrity: sha512-Hz/WvSNt5+7x+Rq1Cn6DetJOZxKtLDehJ1mLCYge6ju4QvSF/PHvRgy94e1SKJVI96AJTcqEdNwkkaAFad+TXQ==} + dev: false + /postcss-attribute-case-insensitive/5.0.2_postcss@8.4.21: resolution: {integrity: sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==} engines: {node: ^12 || ^14 || >=16} @@ -13227,6 +13386,11 @@ packages: engines: {node: '>=6'} dev: false + /progress/2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: false + /prompts/2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -13529,6 +13693,10 @@ packages: unified: 10.1.2 dev: false + /reinterval/1.1.0: + resolution: {integrity: sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==} + dev: false + /remark-code-titles/0.1.2: resolution: {integrity: sha512-KsHQbaI4FX8Ozxqk7YErxwmBiveUqloKuVqyPG2YPLHojpgomodWgRfG4B+bOtmn/5bfJ8khw4rR0lvgVFl2Uw==} dependencies: @@ -13691,6 +13859,10 @@ packages: unified: 10.1.2 dev: false + /retimer/3.0.0: + resolution: {integrity: sha512-WKE0j11Pa0ZJI5YIk0nflGI7SQsfl2ljihVy7ogh7DeQSeYAUi0ubZ/yEueGtDfUPk6GH5LRw1hBdLq4IwUBWA==} + dev: false + /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -14334,6 +14506,12 @@ packages: inline-style-parser: 0.1.1 dev: false + /subarg/1.0.0: + resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + dependencies: + minimist: 1.2.8 + dev: false + /suf-log/2.5.3: resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} dependencies: @@ -14513,6 +14691,11 @@ packages: engines: {node: '>=6'} dev: false + /timestring/6.0.0: + resolution: {integrity: sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==} + engines: {node: '>=8'} + dev: false + /tiny-glob/0.2.9: resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} dependencies: @@ -15094,6 +15277,15 @@ packages: /util-deprecate/1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /uuid-parse/1.1.0: + resolution: {integrity: sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==} + dev: false + + /uuid/8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + /uvu/0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} From ac88714b929253b193872c2a1c80715e10506f29 Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 27 Feb 2023 16:41:21 +0800 Subject: [PATCH 03/35] Setup memory benchmark --- benchmark/bench-memory.js | 23 +++++++++++++++++++- benchmark/index.js | 28 ++++++++++++++----------- packages/astro/src/core/build/index.ts | 3 +++ packages/astro/src/core/config/timer.ts | 12 ++++++----- 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 9445cbdfdf92..8763adcfd841 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -1,3 +1,9 @@ +import { fileURLToPath } from 'url'; +import { execaCommand } from 'execa'; + +const astro = fileURLToPath(new URL('../packages/astro/astro.js', import.meta.url)); +const port = 4321; + /** Default project to run for this benchmark if not specified */ export const defaultProject = 'memory-default'; @@ -5,7 +11,22 @@ export const defaultProject = 'memory-default'; * @param {URL} projectDir * @param {URL} outputFile */ -export async function run(projectDir, outputFile) {} +export async function run(projectDir, outputFile) { + const root = fileURLToPath(projectDir); + + console.log('Building and benchmarking...'); + await execaCommand(`${astro} build`, { + cwd: root, + stdio: 'inherit', + env: { + ASTRO_TIMER_PATH: fileURLToPath(outputFile), + }, + }); + + console.log('Results written to', fileURLToPath(outputFile)); + + console.log('Done!'); +} /** * diff --git a/benchmark/index.js b/benchmark/index.js index 35bc267e2c73..3491f5609034 100755 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -47,19 +47,12 @@ if (args.compare) { process.exit(0); } -const defaultOutputFile = args.output - ? path.resolve(args.output) - : fileURLToPath(new URL(`./results/bench-${Date.now()}.json`, import.meta.url)); - -// Prepare output file directory -await fs.mkdir(path.dirname(defaultOutputFile), { recursive: true }); - if (commandName) { // Run single benchmark const bench = benchmarks[commandName]; const benchMod = await bench(); const projectDir = await makeProject(args.project || benchMod.defaultProject); - const outputFile = pathToFileURL(defaultOutputFile); + const outputFile = await getOutputFile(commandName); await benchMod.run(projectDir, outputFile); } else { // Run all benchmarks @@ -67,10 +60,21 @@ if (commandName) { const bench = benchmarks[name]; const benchMod = await bench(); const projectDir = await makeProject(args.project || benchMod.defaultProject); - // Prefix output file with benchmark name to avoid conflict - const parsed = path.parse(defaultOutputFile); - parsed.base = `${name}-${parsed.base}`; - const outputFile = pathToFileURL(path.format(parsed)); + const outputFile = await getOutputFile(name); await benchMod.run(projectDir, outputFile); } } + +async function getOutputFile(benchmarkName) { + let file; + if (args.output) { + file = pathToFileURL(path.resolve(args.output)); + } else { + file = new URL(`./results/${benchmarkName}-bench-${Date.now()}.json`, import.meta.url); + } + + // Prepare output file directory + await fs.mkdir(new URL('./', file), { recursive: true }); + + return file; +} diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 638d78d105b9..e07c94f5ce48 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -167,6 +167,9 @@ class AstroBuilder { buildMode: this.settings.config.output, }); } + + // Benchmark results + this.settings.timer.writeStats(); } /** Build the given Astro project. */ diff --git a/packages/astro/src/core/config/timer.ts b/packages/astro/src/core/config/timer.ts index d661295b3d30..4c15bd9f085c 100644 --- a/packages/astro/src/core/config/timer.ts +++ b/packages/astro/src/core/config/timer.ts @@ -1,3 +1,5 @@ +import fs from 'fs'; + interface Stat { elapsedTime: number; heapUsedChange: number; @@ -11,7 +13,7 @@ interface OngoingStat { /** * Timer to track certain operations' performance. Used by Astro's scripts only. - * Set `process.env.ENABLE_ASTRO_TIMER` truthy to enable. + * Set `process.env.ASTRO_TIMER_PATH` truthy to enable. */ export class AstroTimer { private enabled: boolean; @@ -19,7 +21,7 @@ export class AstroTimer { private stats: Record = {}; constructor() { - this.enabled = !!process.env.ENABLE_ASTRO_TIMER; + this.enabled = !!process.env.ASTRO_TIMER_PATH; } /** @@ -50,11 +52,11 @@ export class AstroTimer { } /** - * Write stats to `process.env.ENABLE_ASTRO_TIMER` + * Write stats to `process.env.ASTRO_TIMER_PATH` */ - finish() { + writeStats() { if (!this.enabled) return; // @ts-expect-error - process.env.ASTRO_BENCH_STATS = this.stats; + fs.writeFileSync(process.env.ASTRO_TIMER_PATH, JSON.stringify(this.stats, null, 2)); } } From 330a4c87c746f7d212c8ab34a8b232c9b7d48195 Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 27 Feb 2023 22:27:47 +0800 Subject: [PATCH 04/35] Add compare function --- benchmark/bench-memory.js | 35 +++++++++++++++++++++---- benchmark/bench-server-stress.js | 19 +++++++++++++- benchmark/index.js | 12 ++++++++- benchmark/package.json | 1 + packages/astro/src/core/config/timer.ts | 3 ++- pnpm-lock.yaml | 2 ++ 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 8763adcfd841..1a861796a6cd 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -1,8 +1,10 @@ import { fileURLToPath } from 'url'; import { execaCommand } from 'execa'; +import { markdownTable } from 'markdown-table'; + +/** @typedef {Record} AstroTimerStat */ const astro = fileURLToPath(new URL('../packages/astro/astro.js', import.meta.url)); -const port = 4321; /** Default project to run for this benchmark if not specified */ export const defaultProject = 'memory-default'; @@ -29,8 +31,31 @@ export async function run(projectDir, outputFile) { } /** - * - * @param {any} outputA - * @param {any} outputB + * @param {{ name: string, output: AstroTimerStat}} resultA + * @param {{ name: string, output: AstroTimerStat}} resultB */ -export async function compare(outputA, outputB) {} +export async function compare(resultA, resultB) { + /** + * @param {AstroTimerStat} output + */ + const printResult = (output) => { + return markdownTable([ + ['', 'Elapsed time (s)', 'Memory used (MB)', 'Final memory (MB)'], + ...Object.entries(output).map(([name, stat]) => [ + name, + Math.round(stat.elapsedTime), + Math.round(stat.heapUsedChange / 1024 / 1024), + Math.round(stat.heapUsedTotal / 1024 / 1024), + ]), + ]); + }; + + return `\ +### ${resultA.name} + +${printResult(resultA.output)} + +### ${resultB.name} + +${printResult(resultB.output)}`; +} diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index af3a3ecfd020..a943f41e4b3b 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -45,7 +45,24 @@ export async function run(projectDir, outputFile) { console.log('Done!'); } -export async function compare(outputA, outputB) {} +/** + * @param {{ name: string, output: import('autocannon').Result}} resultA + * @param {{ name: string, output: import('autocannon').Result}} resultB + */ +export async function compare(resultA, resultB) { + const resultRegex = /Req\/Bytes.*read/s; + const textA = autocannon.printResult(resultA.output).match(resultRegex)?.[0]; + const textB = autocannon.printResult(resultB.output).match(resultRegex)?.[0]; + + return `\ +### ${resultA.name} + +${textA} + +### ${resultB.name} + +${textB}`; +} /** * @returns {Promise} diff --git a/benchmark/index.js b/benchmark/index.js index 3491f5609034..48bab438892a 100755 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -43,7 +43,17 @@ if (args.compare) { } const bench = benchmarks[commandName]; const benchMod = await bench(); - await benchMod.compare(path.resolve(outputA), path.resolve(outputB)); + const result = await benchMod.compare( + { + name: path.basename(outputA, '.json'), + output: JSON.parse(await fs.readFile(path.resolve(outputA), 'utf-8')), + }, + { + name: path.basename(outputB, '.json'), + output: JSON.parse(await fs.readFile(path.resolve(outputB), 'utf-8')), + } + ); + console.log(result); process.exit(0); } diff --git a/benchmark/package.json b/benchmark/package.json index 2c2c04f52053..064cd601f642 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -11,6 +11,7 @@ "astro": "workspace:*", "autocannon": "^7.10.0", "execa": "^6.1.0", + "markdown-table": "^3.0.3", "mri": "^1.2.0", "port-authority": "^2.0.1" } diff --git a/packages/astro/src/core/config/timer.ts b/packages/astro/src/core/config/timer.ts index 4c15bd9f085c..3f7a7d69dd7f 100644 --- a/packages/astro/src/core/config/timer.ts +++ b/packages/astro/src/core/config/timer.ts @@ -1,6 +1,7 @@ import fs from 'fs'; -interface Stat { +// Type used by `bench-memory.js` +export interface Stat { elapsedTime: number; heapUsedChange: number; heapUsedTotal: number; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17c9692147ac..fb7a7ebea7ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,6 +69,7 @@ importers: astro: workspace:* autocannon: ^7.10.0 execa: ^6.1.0 + markdown-table: ^3.0.3 mri: ^1.2.0 port-authority: ^2.0.1 dependencies: @@ -76,6 +77,7 @@ importers: astro: link:../packages/astro autocannon: 7.10.0 execa: 6.1.0 + markdown-table: 3.0.3 mri: 1.2.0 port-authority: 2.0.1 From ddec009a087243a0626614f3e50ebd90fc875388 Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 27 Feb 2023 22:34:02 +0800 Subject: [PATCH 05/35] Add result preview --- benchmark/bench-memory.js | 39 ++++++++++++++++++-------------- benchmark/bench-server-stress.js | 3 +++ package.json | 4 ++-- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 1a861796a6cd..f9ff34ed204d 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -1,3 +1,4 @@ +import fs from 'fs/promises'; import { fileURLToPath } from 'url'; import { execaCommand } from 'execa'; import { markdownTable } from 'markdown-table'; @@ -15,17 +16,21 @@ export const defaultProject = 'memory-default'; */ export async function run(projectDir, outputFile) { const root = fileURLToPath(projectDir); + const outputFilePath = fileURLToPath(outputFile); console.log('Building and benchmarking...'); await execaCommand(`${astro} build`, { cwd: root, stdio: 'inherit', env: { - ASTRO_TIMER_PATH: fileURLToPath(outputFile), + ASTRO_TIMER_PATH: outputFilePath, }, }); - console.log('Results written to', fileURLToPath(outputFile)); + console.log('Raw results written to', outputFilePath); + + console.log('Result preview:'); + console.log(printResult(JSON.parse(await fs.readFile(outputFilePath, 'utf-8')))); console.log('Done!'); } @@ -35,21 +40,6 @@ export async function run(projectDir, outputFile) { * @param {{ name: string, output: AstroTimerStat}} resultB */ export async function compare(resultA, resultB) { - /** - * @param {AstroTimerStat} output - */ - const printResult = (output) => { - return markdownTable([ - ['', 'Elapsed time (s)', 'Memory used (MB)', 'Final memory (MB)'], - ...Object.entries(output).map(([name, stat]) => [ - name, - Math.round(stat.elapsedTime), - Math.round(stat.heapUsedChange / 1024 / 1024), - Math.round(stat.heapUsedTotal / 1024 / 1024), - ]), - ]); - }; - return `\ ### ${resultA.name} @@ -59,3 +49,18 @@ ${printResult(resultA.output)} ${printResult(resultB.output)}`; } + +/** + * @param {AstroTimerStat} output + */ +function printResult(output) { + return markdownTable([ + ['', 'Elapsed time (s)', 'Memory used (MB)', 'Final memory (MB)'], + ...Object.entries(output).map(([name, stat]) => [ + name, + Math.round(stat.elapsedTime), + Math.round(stat.heapUsedChange / 1024 / 1024), + Math.round(stat.heapUsedTotal / 1024 / 1024), + ]), + ]); +} diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index a943f41e4b3b..6ab8108c0cd7 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -42,6 +42,9 @@ export async function run(projectDir, outputFile) { console.log('Writing results to', fileURLToPath(outputFile)); await fs.writeFile(outputFile, JSON.stringify(result, null, 2)); + console.log('Result preview:'); + console.log(autocannon.printResult(result)); + console.log('Done!'); } diff --git a/package.json b/package.json index be37d41c7159..ebe8684dca26 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "test:vite-ci": "turbo run test --filter=astro --output-logs=new-only --no-deps --concurrency=1", "test:e2e": "cd packages/astro && pnpm playwright install && pnpm run test:e2e", "test:e2e:match": "cd packages/astro && pnpm playwright install && pnpm run test:e2e:match", - "benchmark": "pnpm --filter @benchmark/simple run build && pnpm dlx concurrently -k -s first --raw \"node packages/astro/test/benchmark/simple/server.mjs\" \"pnpm dlx autocannon -c 100 -d 30 -p 10 localhost:3002/\"", + "benchmark": "astro-benchmark", "lint": "eslint --cache .", "version": "changeset version && pnpm install --no-frozen-lockfile && pnpm run format", "preinstall": "npx only-allow pnpm" @@ -102,4 +102,4 @@ "turbo": "1.2.5", "typescript": "~4.7.3" } -} +} \ No newline at end of file From e7f2dccbc5b23d733048179ae3cd664e51e3f130 Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 27 Feb 2023 22:46:35 +0800 Subject: [PATCH 06/35] Setup results preview --- benchmark/bench-server-stress.js | 2 +- benchmark/index.js | 34 +++++++++++++++++--------------- benchmark/make-project.js | 3 +++ 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index 6ab8108c0cd7..01cec8a1a7a8 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -89,6 +89,6 @@ async function benchmarkCannon() { } } ); - autocannon.track(instance); + autocannon.track(instance, { renderResultsTable: false }); }); } diff --git a/benchmark/index.js b/benchmark/index.js index 48bab438892a..bc12a385eb27 100755 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -1,26 +1,28 @@ import fs from 'fs/promises'; import path from 'path'; -import { fileURLToPath, pathToFileURL } from 'url'; +import { pathToFileURL } from 'url'; import mri from 'mri'; import { makeProject } from './make-project.js'; -/** - * astro-benchmark [command] [options] - * - * Options: - * --project - * --output - * --compare , - * - * Examples: - * ```bash - * # Run all benchmarks - * astro-benchmark - * ``` - */ - const args = mri(process.argv.slice(2)); +if (args.help || args.h) { + console.log(`\ +astro-benchmark [options] + +Command + [empty] Run all benchmarks + memory Run build memory and speed test + server-stress Run server stress test + +Options + --project Project to use for benchmark, see make-project.js + --output Output file to write results to + --compare , Compare two output files +`); + process.exit(0); +} + const commandName = args._[0]; const benchmarks = { memory: () => import('./bench-memory.js'), diff --git a/benchmark/make-project.js b/benchmark/make-project.js index 18bdea2349ec..cdd2a739781c 100644 --- a/benchmark/make-project.js +++ b/benchmark/make-project.js @@ -7,6 +7,9 @@ export async function makeProject(name) { console.log('Making project:', name); const projectDir = new URL(`./projects/${name}/`, import.meta.url); + /** + * NOTE: Here are the list of projects supported to generate programmatically + */ switch (name) { case 'memory-default': await makeProjectMemoryDefault(projectDir); From 5cc93cab087c81e6bdcbafbcb274580c71129791 Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 27 Feb 2023 23:54:38 +0800 Subject: [PATCH 07/35] Simplify script for CI --- benchmark/bench-memory.js | 6 +++++- benchmark/bench-server-stress.js | 28 ++++++++-------------------- benchmark/ci-helper.js | 10 ++++++++++ benchmark/index.js | 30 ++---------------------------- 4 files changed, 25 insertions(+), 49 deletions(-) create mode 100644 benchmark/ci-helper.js diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index f9ff34ed204d..ae8a3428b5e4 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -13,8 +13,9 @@ export const defaultProject = 'memory-default'; /** * @param {URL} projectDir * @param {URL} outputFile + * @param {string} [title] */ -export async function run(projectDir, outputFile) { +export async function run(projectDir, outputFile, title) { const root = fileURLToPath(projectDir); const outputFilePath = fileURLToPath(outputFile); @@ -30,7 +31,10 @@ export async function run(projectDir, outputFile) { console.log('Raw results written to', outputFilePath); console.log('Result preview:'); + console.log('='.repeat(10)); + if (title) console.log(`#### Server stress (${title})\n`); console.log(printResult(JSON.parse(await fs.readFile(outputFilePath, 'utf-8')))); + console.log('='.repeat(10)); console.log('Done!'); } diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index 01cec8a1a7a8..9ac5244e763a 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -12,8 +12,9 @@ export const defaultProject = 'server-stress-default'; /** * @param {URL} projectDir * @param {URL} outputFile + * @param {string} [title] */ -export async function run(projectDir, outputFile) { +export async function run(projectDir, outputFile, title) { const root = fileURLToPath(projectDir); console.log('Building...'); @@ -43,30 +44,14 @@ export async function run(projectDir, outputFile) { await fs.writeFile(outputFile, JSON.stringify(result, null, 2)); console.log('Result preview:'); + console.log('='.repeat(10)); + if (title) console.log(`#### Server stress (${title})\n`); console.log(autocannon.printResult(result)); + console.log('='.repeat(10)); console.log('Done!'); } -/** - * @param {{ name: string, output: import('autocannon').Result}} resultA - * @param {{ name: string, output: import('autocannon').Result}} resultB - */ -export async function compare(resultA, resultB) { - const resultRegex = /Req\/Bytes.*read/s; - const textA = autocannon.printResult(resultA.output).match(resultRegex)?.[0]; - const textB = autocannon.printResult(resultB.output).match(resultRegex)?.[0]; - - return `\ -### ${resultA.name} - -${textA} - -### ${resultB.name} - -${textB}`; -} - /** * @returns {Promise} */ @@ -85,6 +70,9 @@ async function benchmarkCannon() { } else { // @ts-expect-error untyped but documented instance.stop(); + if (process.env.CI) { + result = result.match(/Req\/Bytes.*read/s)?.[0]; + } resolve(result); } } diff --git a/benchmark/ci-helper.js b/benchmark/ci-helper.js new file mode 100644 index 000000000000..fce738841441 --- /dev/null +++ b/benchmark/ci-helper.js @@ -0,0 +1,10 @@ +const benchLogs = process.argv[2]; +const resultRegex = /==========(.*?)==========/gs; + +let processedLog = ''; +let m; +while ((m = resultRegex.exec(benchLogs))) { + processedLog += m[1] + '\n'; +} + +console.log(processedLog); diff --git a/benchmark/index.js b/benchmark/index.js index bc12a385eb27..3d4fe858efe9 100755 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -18,7 +18,6 @@ Command Options --project Project to use for benchmark, see make-project.js --output Output file to write results to - --compare , Compare two output files `); process.exit(0); } @@ -34,38 +33,13 @@ if (commandName && !(commandName in benchmarks)) { process.exit(1); } -// If we're running a compare command, run it and exit -if (args.compare) { - const [outputA, outputB] = args.compare.split(',').map((s) => s.trim()); - if (!outputA || !outputB) { - console.error( - `Invalid --compare value: ${args.compare}, must have two files, separated by a comma.` - ); - process.exit(1); - } - const bench = benchmarks[commandName]; - const benchMod = await bench(); - const result = await benchMod.compare( - { - name: path.basename(outputA, '.json'), - output: JSON.parse(await fs.readFile(path.resolve(outputA), 'utf-8')), - }, - { - name: path.basename(outputB, '.json'), - output: JSON.parse(await fs.readFile(path.resolve(outputB), 'utf-8')), - } - ); - console.log(result); - process.exit(0); -} - if (commandName) { // Run single benchmark const bench = benchmarks[commandName]; const benchMod = await bench(); const projectDir = await makeProject(args.project || benchMod.defaultProject); const outputFile = await getOutputFile(commandName); - await benchMod.run(projectDir, outputFile); + await benchMod.run(projectDir, outputFile, args.title); } else { // Run all benchmarks for (const name in benchmarks) { @@ -73,7 +47,7 @@ if (commandName) { const benchMod = await bench(); const projectDir = await makeProject(args.project || benchMod.defaultProject); const outputFile = await getOutputFile(name); - await benchMod.run(projectDir, outputFile); + await benchMod.run(projectDir, outputFile, args.title); } } From c33ae38bd236ef59d2ba4278599ad51d8b38fa51 Mon Sep 17 00:00:00 2001 From: bluwy Date: Mon, 27 Feb 2023 23:57:21 +0800 Subject: [PATCH 08/35] Update CI --- .github/workflows/benchmark.yml | 42 +++++++++++++++----------------- benchmark/bench-server-stress.js | 2 +- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 54b33e9d3c9d..42470d803063 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -16,13 +16,8 @@ jobs: permissions: contents: read outputs: - PR-BENCH-16: ${{ steps.benchmark-pr.outputs.BENCH_RESULT16 }} - PR-BENCH-18: ${{ steps.benchmark-pr.outputs.BENCH_RESULT18 }} - MAIN-BENCH-16: ${{ steps.benchmark-main.outputs.BENCH_RESULT16 }} - MAIN-BENCH-18: ${{ steps.benchmark-main.outputs.BENCH_RESULT18 }} - strategy: - matrix: - node-version: [16, 18] + PR-BENCH: ${{ steps.benchmark-pr.outputs.BENCH_RESULT16 }} + MAIN-BENCH: ${{ steps.benchmark-main.outputs.BENCH_RESULT18 }} steps: - uses: actions/checkout@v3 with: @@ -45,13 +40,20 @@ jobs: - name: Build Packages run: pnpm run build + - name: Get bench command + id: bench-command + run: | + benchcmd=$(echo "${{ github.event.comment.body }}" | grep '!bench' | awk -F ' ' '{print $2}') + echo "::set-output name=bench::$benchcmd" + shell: bash + - name: Run benchmark id: benchmark-pr run: | - pnpm run --silent benchmark 2> ./bench-result.md - result=$(awk '/requests in/' ./bench-result.md) - echo "::set-output name=BENCH_RESULT${{matrix.node-version}}::$result" - echo "$result" + result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**PR**") + processed=$(node ./benchmark/ci-helper.js "$result") + echo "::set-output name=BENCH_RESULT::$processed" + echo "$processed" shell: bash # main benchmark @@ -70,10 +72,10 @@ jobs: - name: Run benchmark id: benchmark-main run: | - pnpm run --silent benchmark 2> ./bench-result.md - result=$(awk '/requests in/' ./bench-result.md) - echo "::set-output name=BENCH_RESULT${{matrix.node-version}}::$result" - echo "$result" + result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**MAIN**") + processed=$(node ./benchmark/ci-helper.js "$result") + echo "::set-output name=BENCH_RESULT::$processed" + echo "$processed" shell: bash output-benchmark: @@ -89,12 +91,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} pr_number: ${{ github.event.issue.number }} message: | - **Node**: 16 - **PR**: ${{ needs.benchmark.outputs.PR-BENCH-16 }} - **MAIN**: ${{ needs.benchmark.outputs.MAIN-BENCH-16 }} - - --- + ${{ needs.benchmark.outputs.PR-BENCH }} - **Node**: 18 - **PR**: ${{ needs.benchmark.outputs.PR-BENCH-18 }} - **MAIN**: ${{ needs.benchmark.outputs.MAIN-BENCH-18 }} + ${{ needs.benchmark.outputs.MAIN-BENCH }} diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index 9ac5244e763a..a016a0dc8bcd 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -71,7 +71,7 @@ async function benchmarkCannon() { // @ts-expect-error untyped but documented instance.stop(); if (process.env.CI) { - result = result.match(/Req\/Bytes.*read/s)?.[0]; + result = result.match(/^.*?requests in.*?read$/m)?.[0]; } resolve(result); } From 02ec66f5ca2e044f582d8b02e758001c3994ec4c Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 00:00:15 +0800 Subject: [PATCH 09/35] Cleanup --- benchmark/_bench-template.js | 10 ++-------- benchmark/bench-memory.js | 15 --------------- package.json | 2 +- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/benchmark/_bench-template.js b/benchmark/_bench-template.js index 7871e4096506..2c61342a78e0 100644 --- a/benchmark/_bench-template.js +++ b/benchmark/_bench-template.js @@ -4,12 +4,6 @@ export const defaultProject = 'project-name'; /** * @param {URL} projectDir * @param {URL} outputFile + * @param {string} [title] */ -export async function run(projectDir, outputFile) {} - -/** - * - * @param {any} outputA - * @param {any} outputB - */ -export async function compare(outputA, outputB) {} +export async function run(projectDir, outputFile, title) {} diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index ae8a3428b5e4..1a428c1a52c3 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -39,21 +39,6 @@ export async function run(projectDir, outputFile, title) { console.log('Done!'); } -/** - * @param {{ name: string, output: AstroTimerStat}} resultA - * @param {{ name: string, output: AstroTimerStat}} resultB - */ -export async function compare(resultA, resultB) { - return `\ -### ${resultA.name} - -${printResult(resultA.output)} - -### ${resultB.name} - -${printResult(resultB.output)}`; -} - /** * @param {AstroTimerStat} output */ diff --git a/package.json b/package.json index ebe8684dca26..7ca43f566a8c 100644 --- a/package.json +++ b/package.json @@ -102,4 +102,4 @@ "turbo": "1.2.5", "typescript": "~4.7.3" } -} \ No newline at end of file +} From b086a674b6a505b52367af58ddc7ef7a0965e6af Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 00:07:09 +0800 Subject: [PATCH 10/35] Temp remove fork guard --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 42470d803063..22ab0339d33c 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -11,7 +11,7 @@ env: jobs: benchmark: - if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} runs-on: ubuntu-latest permissions: contents: read From fb3e8a8345732a94cca2054917690b442b232255 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 00:12:49 +0800 Subject: [PATCH 11/35] Fix stuff --- .github/workflows/benchmark.yml | 10 +++++----- benchmark/bench-server-stress.js | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 22ab0339d33c..9c15be758aa3 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -16,8 +16,8 @@ jobs: permissions: contents: read outputs: - PR-BENCH: ${{ steps.benchmark-pr.outputs.BENCH_RESULT16 }} - MAIN-BENCH: ${{ steps.benchmark-main.outputs.BENCH_RESULT18 }} + PR-BENCH: ${{ steps.benchmark-pr.outputs.BENCH_RESULT }} + MAIN-BENCH: ${{ steps.benchmark-main.outputs.BENCH_RESULT }} steps: - uses: actions/checkout@v3 with: @@ -44,7 +44,7 @@ jobs: id: bench-command run: | benchcmd=$(echo "${{ github.event.comment.body }}" | grep '!bench' | awk -F ' ' '{print $2}') - echo "::set-output name=bench::$benchcmd" + echo "bench=$benchcmd >> $GITHUB_OUTPUT" shell: bash - name: Run benchmark @@ -52,7 +52,7 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**PR**") processed=$(node ./benchmark/ci-helper.js "$result") - echo "::set-output name=BENCH_RESULT::$processed" + echo "BENCH_RESULT=$processed >> $GITHUB_OUTPUT" echo "$processed" shell: bash @@ -74,7 +74,7 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**MAIN**") processed=$(node ./benchmark/ci-helper.js "$result") - echo "::set-output name=BENCH_RESULT::$processed" + echo "BENCH_RESULT=$processed >> $GITHUB_OUTPUT" echo "$processed" shell: bash diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index a016a0dc8bcd..9367e04a724d 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -46,7 +46,11 @@ export async function run(projectDir, outputFile, title) { console.log('Result preview:'); console.log('='.repeat(10)); if (title) console.log(`#### Server stress (${title})\n`); - console.log(autocannon.printResult(result)); + let text = autocannon.printResult(result); + if (process.env.CI) { + text = text.match(/^.*?requests in.*?read$/m)?.[0]; + } + console.log(text); console.log('='.repeat(10)); console.log('Done!'); @@ -70,9 +74,6 @@ async function benchmarkCannon() { } else { // @ts-expect-error untyped but documented instance.stop(); - if (process.env.CI) { - result = result.match(/^.*?requests in.*?read$/m)?.[0]; - } resolve(result); } } From 60439cab59b8b6da45fb7dcf4fa3bf14a08f8b13 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 00:24:10 +0800 Subject: [PATCH 12/35] Fix again --- .github/workflows/benchmark.yml | 2 +- benchmark/bench-memory.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9c15be758aa3..e148ac1911fe 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -79,7 +79,7 @@ jobs: shell: bash output-benchmark: - if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} needs: [benchmark] runs-on: ubuntu-latest permissions: diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 1a428c1a52c3..0ca36a63e8a8 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -32,7 +32,7 @@ export async function run(projectDir, outputFile, title) { console.log('Result preview:'); console.log('='.repeat(10)); - if (title) console.log(`#### Server stress (${title})\n`); + if (title) console.log(`#### Memonry (${title})\n`); console.log(printResult(JSON.parse(await fs.readFile(outputFilePath, 'utf-8')))); console.log('='.repeat(10)); From 8b16d239991e5f51cf5691334bb98fedca7fe2c0 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 00:32:56 +0800 Subject: [PATCH 13/35] Fix quotes --- .github/workflows/benchmark.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index e148ac1911fe..5377775979f7 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -44,7 +44,7 @@ jobs: id: bench-command run: | benchcmd=$(echo "${{ github.event.comment.body }}" | grep '!bench' | awk -F ' ' '{print $2}') - echo "bench=$benchcmd >> $GITHUB_OUTPUT" + echo "bench=$benchcmd" >> $GITHUB_OUTPUT shell: bash - name: Run benchmark @@ -52,8 +52,7 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**PR**") processed=$(node ./benchmark/ci-helper.js "$result") - echo "BENCH_RESULT=$processed >> $GITHUB_OUTPUT" - echo "$processed" + echo "BENCH_RESULT=$processed" >> $GITHUB_OUTPUT shell: bash # main benchmark @@ -74,8 +73,7 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**MAIN**") processed=$(node ./benchmark/ci-helper.js "$result") - echo "BENCH_RESULT=$processed >> $GITHUB_OUTPUT" - echo "$processed" + echo "BENCH_RESULT=$processed" >> $GITHUB_OUTPUT shell: bash output-benchmark: From cedb14f7bdc452b585af3c4d3f335f764035ad1a Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 12:01:43 +0800 Subject: [PATCH 14/35] Fix multiline output --- .github/workflows/benchmark.yml | 8 ++++++-- benchmark/bench-memory.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 5377775979f7..1ddfea4cda8a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -52,7 +52,9 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**PR**") processed=$(node ./benchmark/ci-helper.js "$result") - echo "BENCH_RESULT=$processed" >> $GITHUB_OUTPUT + echo "BENCH_RESULT<> $GITHUB_OUTPUT + echo "$processed" >> $GITHUB_OUTPUT + echo "BENCHEOF" >> $GITHUB_OUTPUT shell: bash # main benchmark @@ -73,7 +75,9 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**MAIN**") processed=$(node ./benchmark/ci-helper.js "$result") - echo "BENCH_RESULT=$processed" >> $GITHUB_OUTPUT + echo "BENCH_RESULT<> $GITHUB_OUTPUT + echo "$processed" >> $GITHUB_OUTPUT + echo "BENCHEOF" >> $GITHUB_OUTPUT shell: bash output-benchmark: diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 0ca36a63e8a8..5ce3309650a0 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -32,7 +32,7 @@ export async function run(projectDir, outputFile, title) { console.log('Result preview:'); console.log('='.repeat(10)); - if (title) console.log(`#### Memonry (${title})\n`); + if (title) console.log(`#### Memory (${title})\n`); console.log(printResult(JSON.parse(await fs.readFile(outputFilePath, 'utf-8')))); console.log('='.repeat(10)); From bd4ece02334a6fc76b23cfb17cce3cd265971e73 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 15:03:46 +0800 Subject: [PATCH 15/35] Simplify title --- .github/workflows/benchmark.yml | 6 ++++-- benchmark/_bench-template.js | 7 +++++-- benchmark/bench-memory.js | 5 ++--- benchmark/bench-server-stress.js | 5 ++--- benchmark/index.js | 4 ++-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 1ddfea4cda8a..7439429f462b 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -50,8 +50,9 @@ jobs: - name: Run benchmark id: benchmark-pr run: | - result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**PR**") + result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }}) processed=$(node ./benchmark/ci-helper.js "$result") + processed="### PR Benchmark\n\n$processed" echo "BENCH_RESULT<> $GITHUB_OUTPUT echo "$processed" >> $GITHUB_OUTPUT echo "BENCHEOF" >> $GITHUB_OUTPUT @@ -73,8 +74,9 @@ jobs: - name: Run benchmark id: benchmark-main run: | - result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }} --title "**MAIN**") + result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }}) processed=$(node ./benchmark/ci-helper.js "$result") + processed="### Main Benchmark\n\n$processed" echo "BENCH_RESULT<> $GITHUB_OUTPUT echo "$processed" >> $GITHUB_OUTPUT echo "BENCHEOF" >> $GITHUB_OUTPUT diff --git a/benchmark/_bench-template.js b/benchmark/_bench-template.js index 2c61342a78e0..867ecf13b6c3 100644 --- a/benchmark/_bench-template.js +++ b/benchmark/_bench-template.js @@ -2,8 +2,11 @@ export const defaultProject = 'project-name'; /** + * Run benchmark on `projectDir` and write results to `outputFile`. + * Use `console.log` to report the results too. Logs that start with 10 `=` + * and end with 10 `=` will be extracted by CI to display in the PR comment. + * Usually after the first 10 `=` you'll want to add a title like `#### Test`. * @param {URL} projectDir * @param {URL} outputFile - * @param {string} [title] */ -export async function run(projectDir, outputFile, title) {} +export async function run(projectDir, outputFile) {} diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 5ce3309650a0..88cdd759dbc5 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -13,9 +13,8 @@ export const defaultProject = 'memory-default'; /** * @param {URL} projectDir * @param {URL} outputFile - * @param {string} [title] */ -export async function run(projectDir, outputFile, title) { +export async function run(projectDir, outputFile) { const root = fileURLToPath(projectDir); const outputFilePath = fileURLToPath(outputFile); @@ -32,7 +31,7 @@ export async function run(projectDir, outputFile, title) { console.log('Result preview:'); console.log('='.repeat(10)); - if (title) console.log(`#### Memory (${title})\n`); + console.log(`#### Memory\n\n`); console.log(printResult(JSON.parse(await fs.readFile(outputFilePath, 'utf-8')))); console.log('='.repeat(10)); diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index 9367e04a724d..2387f162ab76 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -12,9 +12,8 @@ export const defaultProject = 'server-stress-default'; /** * @param {URL} projectDir * @param {URL} outputFile - * @param {string} [title] */ -export async function run(projectDir, outputFile, title) { +export async function run(projectDir, outputFile) { const root = fileURLToPath(projectDir); console.log('Building...'); @@ -45,7 +44,7 @@ export async function run(projectDir, outputFile, title) { console.log('Result preview:'); console.log('='.repeat(10)); - if (title) console.log(`#### Server stress (${title})\n`); + console.log(`#### Server stress\n\n`); let text = autocannon.printResult(result); if (process.env.CI) { text = text.match(/^.*?requests in.*?read$/m)?.[0]; diff --git a/benchmark/index.js b/benchmark/index.js index 3d4fe858efe9..1ed0e5d33017 100755 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -39,7 +39,7 @@ if (commandName) { const benchMod = await bench(); const projectDir = await makeProject(args.project || benchMod.defaultProject); const outputFile = await getOutputFile(commandName); - await benchMod.run(projectDir, outputFile, args.title); + await benchMod.run(projectDir, outputFile); } else { // Run all benchmarks for (const name in benchmarks) { @@ -47,7 +47,7 @@ if (commandName) { const benchMod = await bench(); const projectDir = await makeProject(args.project || benchMod.defaultProject); const outputFile = await getOutputFile(name); - await benchMod.run(projectDir, outputFile, args.title); + await benchMod.run(projectDir, outputFile); } } From dc0b232b04c975900f998a6468f17f5705b06ba3 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 15:10:07 +0800 Subject: [PATCH 16/35] Fix memory numbers --- benchmark/bench-memory.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 88cdd759dbc5..c30b9cfa975b 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -42,13 +42,18 @@ export async function run(projectDir, outputFile) { * @param {AstroTimerStat} output */ function printResult(output) { - return markdownTable([ - ['', 'Elapsed time (s)', 'Memory used (MB)', 'Final memory (MB)'], - ...Object.entries(output).map(([name, stat]) => [ - name, - Math.round(stat.elapsedTime), - Math.round(stat.heapUsedChange / 1024 / 1024), - Math.round(stat.heapUsedTotal / 1024 / 1024), - ]), - ]); + return markdownTable( + [ + ['', 'Elapsed time (s)', 'Memory used (MB)', 'Final memory (MB)'], + ...Object.entries(output).map(([name, stat]) => [ + name, + (stat.elapsedTime / 1000).toFixed(2), + (stat.heapUsedChange / 1024 / 1024).toFixed(2), + (stat.heapUsedTotal / 1024 / 1024).toFixed(2), + ]), + ], + { + align: ['l', 'r', 'r', 'r'], + } + ); } From eb14430dc938424abfe2ce7be6aec15dffda7dd6 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 15:20:58 +0800 Subject: [PATCH 17/35] Remove astro bin dir --- benchmark/bench-memory.js | 6 +++--- benchmark/bench-server-stress.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index c30b9cfa975b..88cc815d5938 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -5,7 +5,7 @@ import { markdownTable } from 'markdown-table'; /** @typedef {Record} AstroTimerStat */ -const astro = fileURLToPath(new URL('../packages/astro/astro.js', import.meta.url)); +const benchmarkDir = fileURLToPath(new URL('./', import.meta.url)); /** Default project to run for this benchmark if not specified */ export const defaultProject = 'memory-default'; @@ -19,8 +19,8 @@ export async function run(projectDir, outputFile) { const outputFilePath = fileURLToPath(outputFile); console.log('Building and benchmarking...'); - await execaCommand(`${astro} build`, { - cwd: root, + await execaCommand(`pnpm astro build --root ${root}`, { + cwd: benchmarkDir, stdio: 'inherit', env: { ASTRO_TIMER_PATH: outputFilePath, diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index 2387f162ab76..381a09442fc6 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -4,7 +4,7 @@ import autocannon from 'autocannon'; import { execaCommand } from 'execa'; import { waitUntilBusy } from 'port-authority'; -const astro = fileURLToPath(new URL('../packages/astro/astro.js', import.meta.url)); +const benchmarkDir = fileURLToPath(new URL('./', import.meta.url)); const port = 4321; export const defaultProject = 'server-stress-default'; @@ -17,14 +17,14 @@ export async function run(projectDir, outputFile) { const root = fileURLToPath(projectDir); console.log('Building...'); - await execaCommand(`${astro} build`, { - cwd: root, + await execaCommand(`pnpm astro build --root ${root}`, { + cwd: benchmarkDir, stdio: 'inherit', }); console.log('Previewing...'); - const previewProcess = execaCommand(`${astro} preview --port ${port}`, { - cwd: root, + const previewProcess = execaCommand(`pnpm astro preview --root ${root} --port ${port}`, { + cwd: benchmarkDir, stdio: 'inherit', }); From e150378d7fafb4b37adaab0557c5ed222fda3369 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 15:54:29 +0800 Subject: [PATCH 18/35] Fix gc --- .github/workflows/benchmark.yml | 4 ++-- benchmark/bench-memory.js | 7 +++---- benchmark/bench-server-stress.js | 10 +++++----- benchmark/util.js | 3 +++ packages/astro/src/core/config/timer.ts | 2 ++ 5 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 benchmark/util.js diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 7439429f462b..f3dd1d48b34e 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -52,8 +52,8 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }}) processed=$(node ./benchmark/ci-helper.js "$result") - processed="### PR Benchmark\n\n$processed" echo "BENCH_RESULT<> $GITHUB_OUTPUT + echo "### PR Benchmark" >> $GITHUB_OUTPUT echo "$processed" >> $GITHUB_OUTPUT echo "BENCHEOF" >> $GITHUB_OUTPUT shell: bash @@ -76,8 +76,8 @@ jobs: run: | result=$(pnpm run --silent benchmark ${{ steps.bench-command.outputs.bench }}) processed=$(node ./benchmark/ci-helper.js "$result") - processed="### Main Benchmark\n\n$processed" echo "BENCH_RESULT<> $GITHUB_OUTPUT + echo "### Main Benchmark" >> $GITHUB_OUTPUT echo "$processed" >> $GITHUB_OUTPUT echo "BENCHEOF" >> $GITHUB_OUTPUT shell: bash diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 88cc815d5938..3d78322c8551 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -2,11 +2,10 @@ import fs from 'fs/promises'; import { fileURLToPath } from 'url'; import { execaCommand } from 'execa'; import { markdownTable } from 'markdown-table'; +import { astroBin } from './util.js'; /** @typedef {Record} AstroTimerStat */ -const benchmarkDir = fileURLToPath(new URL('./', import.meta.url)); - /** Default project to run for this benchmark if not specified */ export const defaultProject = 'memory-default'; @@ -19,8 +18,8 @@ export async function run(projectDir, outputFile) { const outputFilePath = fileURLToPath(outputFile); console.log('Building and benchmarking...'); - await execaCommand(`pnpm astro build --root ${root}`, { - cwd: benchmarkDir, + await execaCommand(`node --expose-gc ${astroBin} build`, { + cwd: root, stdio: 'inherit', env: { ASTRO_TIMER_PATH: outputFilePath, diff --git a/benchmark/bench-server-stress.js b/benchmark/bench-server-stress.js index 381a09442fc6..864e317d1fe3 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench-server-stress.js @@ -3,8 +3,8 @@ import { fileURLToPath } from 'url'; import autocannon from 'autocannon'; import { execaCommand } from 'execa'; import { waitUntilBusy } from 'port-authority'; +import { astroBin } from './util.js'; -const benchmarkDir = fileURLToPath(new URL('./', import.meta.url)); const port = 4321; export const defaultProject = 'server-stress-default'; @@ -17,14 +17,14 @@ export async function run(projectDir, outputFile) { const root = fileURLToPath(projectDir); console.log('Building...'); - await execaCommand(`pnpm astro build --root ${root}`, { - cwd: benchmarkDir, + await execaCommand(`${astroBin} build`, { + cwd: root, stdio: 'inherit', }); console.log('Previewing...'); - const previewProcess = execaCommand(`pnpm astro preview --root ${root} --port ${port}`, { - cwd: benchmarkDir, + const previewProcess = execaCommand(`${astroBin} preview --port ${port}`, { + cwd: root, stdio: 'inherit', }); diff --git a/benchmark/util.js b/benchmark/util.js new file mode 100644 index 000000000000..b61c79a7813b --- /dev/null +++ b/benchmark/util.js @@ -0,0 +1,3 @@ +import { createRequire } from 'module'; + +export const astroBin = createRequire(import.meta.url).resolve('astro'); diff --git a/packages/astro/src/core/config/timer.ts b/packages/astro/src/core/config/timer.ts index 3f7a7d69dd7f..7360b55103ee 100644 --- a/packages/astro/src/core/config/timer.ts +++ b/packages/astro/src/core/config/timer.ts @@ -30,6 +30,7 @@ export class AstroTimer { */ start(name: string) { if (!this.enabled) return; + globalThis.gc?.(); this.ongoingTimers.set(name, { startTime: performance.now(), startHeap: process.memoryUsage().heapUsed, @@ -43,6 +44,7 @@ export class AstroTimer { if (!this.enabled) return; const stat = this.ongoingTimers.get(name); if (!stat) return; + globalThis.gc?.(); const endHeap = process.memoryUsage().heapUsed; this.stats[name] = { elapsedTime: performance.now() - stat.startTime, From 3ceb3abb5bffa16aa701520d1915024be695fe9c Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 16:13:46 +0800 Subject: [PATCH 19/35] Add repo guards --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index f3dd1d48b34e..798156e35294 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -11,7 +11,7 @@ env: jobs: benchmark: - if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} runs-on: ubuntu-latest permissions: contents: read @@ -83,7 +83,7 @@ jobs: shell: bash output-benchmark: - if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} needs: [benchmark] runs-on: ubuntu-latest permissions: From 800916c584ab781d16097a43abbcf3e84854a153 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 16:29:09 +0800 Subject: [PATCH 20/35] Fix wrong call --- packages/astro/src/core/build/static-build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 4fde3016cc50..2e39128f533a 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -74,7 +74,7 @@ export async function staticBuild(opts: StaticBuildOptions) { info(opts.logging, 'build', dim(`Completed in ${getTimeStat(ssrTime, performance.now())}.`)); settings.timer.end('SSR build'); - settings.timer.end('Client build'); + settings.timer.start('Client build'); const rendererClientEntrypoints = settings.renderers .map((r) => r.clientEntrypoint) From 83a91dbf46314788e4f86d1781bd25437dd1917b Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 17:48:57 +0800 Subject: [PATCH 21/35] Set max space size --- benchmark/bench-memory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 3d78322c8551..203e3ad0a254 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -18,7 +18,7 @@ export async function run(projectDir, outputFile) { const outputFilePath = fileURLToPath(outputFile); console.log('Building and benchmarking...'); - await execaCommand(`node --expose-gc ${astroBin} build`, { + await execaCommand(`node --expose-gc --max_old_space_size=128 ${astroBin} build`, { cwd: root, stdio: 'inherit', env: { From bc8d4fbc7a2b597680e44e055e4013cfb51a7d57 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 18:01:27 +0800 Subject: [PATCH 22/35] Remove guard --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 798156e35294..f3dd1d48b34e 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -11,7 +11,7 @@ env: jobs: benchmark: - if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} runs-on: ubuntu-latest permissions: contents: read @@ -83,7 +83,7 @@ jobs: shell: bash output-benchmark: - if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} needs: [benchmark] runs-on: ubuntu-latest permissions: From 8c76d7159be204d16716a92414a6fb5f0c56429a Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 19:07:28 +0800 Subject: [PATCH 23/35] Bump memory a bit --- benchmark/bench-memory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/bench-memory.js b/benchmark/bench-memory.js index 203e3ad0a254..2c2bd683bc7c 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench-memory.js @@ -18,7 +18,7 @@ export async function run(projectDir, outputFile) { const outputFilePath = fileURLToPath(outputFile); console.log('Building and benchmarking...'); - await execaCommand(`node --expose-gc --max_old_space_size=128 ${astroBin} build`, { + await execaCommand(`node --expose-gc --max_old_space_size=512 ${astroBin} build`, { cwd: root, stdio: 'inherit', env: { From f6629e4210bf2a1280d287041dde69e600a696ce Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 21:04:37 +0800 Subject: [PATCH 24/35] Organize neatly --- .../_template.js} | 0 benchmark/{util.js => bench/_util.js} | 0 .../{bench-memory.js => bench/memory.js} | 4 +- .../server-stress.js} | 2 +- benchmark/index.js | 19 +++- benchmark/make-project.js | 91 ------------------- benchmark/make-project/_template.js | 4 + benchmark/make-project/_util.js | 2 + benchmark/make-project/memory-default.js | 59 ++++++++++++ .../make-project/server-stress-default.js | 47 ++++++++++ 10 files changed, 131 insertions(+), 97 deletions(-) rename benchmark/{_bench-template.js => bench/_template.js} (100%) rename benchmark/{util.js => bench/_util.js} (100%) rename benchmark/{bench-memory.js => bench/memory.js} (90%) rename benchmark/{bench-server-stress.js => bench/server-stress.js} (98%) delete mode 100644 benchmark/make-project.js create mode 100644 benchmark/make-project/_template.js create mode 100644 benchmark/make-project/_util.js create mode 100644 benchmark/make-project/memory-default.js create mode 100644 benchmark/make-project/server-stress-default.js diff --git a/benchmark/_bench-template.js b/benchmark/bench/_template.js similarity index 100% rename from benchmark/_bench-template.js rename to benchmark/bench/_template.js diff --git a/benchmark/util.js b/benchmark/bench/_util.js similarity index 100% rename from benchmark/util.js rename to benchmark/bench/_util.js diff --git a/benchmark/bench-memory.js b/benchmark/bench/memory.js similarity index 90% rename from benchmark/bench-memory.js rename to benchmark/bench/memory.js index 2c2bd683bc7c..909a61380194 100644 --- a/benchmark/bench-memory.js +++ b/benchmark/bench/memory.js @@ -2,9 +2,9 @@ import fs from 'fs/promises'; import { fileURLToPath } from 'url'; import { execaCommand } from 'execa'; import { markdownTable } from 'markdown-table'; -import { astroBin } from './util.js'; +import { astroBin } from './_util.js'; -/** @typedef {Record} AstroTimerStat */ +/** @typedef {Record} AstroTimerStat */ /** Default project to run for this benchmark if not specified */ export const defaultProject = 'memory-default'; diff --git a/benchmark/bench-server-stress.js b/benchmark/bench/server-stress.js similarity index 98% rename from benchmark/bench-server-stress.js rename to benchmark/bench/server-stress.js index 864e317d1fe3..600e2b9406a8 100644 --- a/benchmark/bench-server-stress.js +++ b/benchmark/bench/server-stress.js @@ -3,7 +3,7 @@ import { fileURLToPath } from 'url'; import autocannon from 'autocannon'; import { execaCommand } from 'execa'; import { waitUntilBusy } from 'port-authority'; -import { astroBin } from './util.js'; +import { astroBin } from './_util.js'; const port = 4321; diff --git a/benchmark/index.js b/benchmark/index.js index 1ed0e5d33017..93096d020c29 100755 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -2,7 +2,6 @@ import fs from 'fs/promises'; import path from 'path'; import { pathToFileURL } from 'url'; import mri from 'mri'; -import { makeProject } from './make-project.js'; const args = mri(process.argv.slice(2)); @@ -24,8 +23,8 @@ Options const commandName = args._[0]; const benchmarks = { - memory: () => import('./bench-memory.js'), - 'server-stress': () => import('./bench-server-stress.js'), + memory: () => import('./bench/memory.js'), + 'server-stress': () => import('./bench/server-stress.js'), }; if (commandName && !(commandName in benchmarks)) { @@ -51,6 +50,20 @@ if (commandName) { } } +async function makeProject(name) { + console.log('Making project:', name); + const projectDir = new URL(`./projects/${name}/`, import.meta.url); + + const makeProjectMod = await import(`./make-project/${name}.js`); + await makeProjectMod.run(projectDir); + + console.log('Finished making project:', name); + return projectDir; +} + +/** + * @param {string} benchmarkName + */ async function getOutputFile(benchmarkName) { let file; if (args.output) { diff --git a/benchmark/make-project.js b/benchmark/make-project.js deleted file mode 100644 index cdd2a739781c..000000000000 --- a/benchmark/make-project.js +++ /dev/null @@ -1,91 +0,0 @@ -import fs from 'fs/promises'; - -/** - * @param {string} name - */ -export async function makeProject(name) { - console.log('Making project:', name); - const projectDir = new URL(`./projects/${name}/`, import.meta.url); - - /** - * NOTE: Here are the list of projects supported to generate programmatically - */ - switch (name) { - case 'memory-default': - await makeProjectMemoryDefault(projectDir); - break; - case 'server-stress-default': - await makeProjectServerStressDefault(projectDir); - break; - default: - throw new Error('Unknown project name: ' + name); - } - - console.log('Finished making project:', name); - return projectDir; -} - -async function makeProjectMemoryDefault(projectDir) { - await fs.rm(projectDir, { recursive: true, force: true }); - await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); - - const promises = []; - - for (let i = 0; i < 500; i++) { - const content = `\ ---- -const i = ${i}; ---- - -{i} -`; - promises.push( - fs.writeFile(new URL(`./src/pages/page-${i}.astro`, projectDir), content, 'utf-8') - ); - } - - await Promise.all(promises); -} - -async function makeProjectServerStressDefault(projectDir) { - await fs.rm(projectDir, { recursive: true, force: true }); - await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); - - await fs.writeFile( - new URL('./src/pages/index.astro', projectDir), - `\ ---- -const content = - "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; ---- - - - - - - - Astro - - -

Astro

-
- ${Array.from({ length: 60 }).map(() => '

{content}

')} -
- -`, - 'utf-8' - ); - - await fs.writeFile( - new URL('./astro.config.js', projectDir), - `\ -import { defineConfig } from 'astro/config'; -import nodejs from '@astrojs/node'; - -export default defineConfig({ - output: 'server', - adapter: nodejs({ mode: 'standalone' }), -});`, - 'utf-8' - ); -} diff --git a/benchmark/make-project/_template.js b/benchmark/make-project/_template.js new file mode 100644 index 000000000000..48041710b8e9 --- /dev/null +++ b/benchmark/make-project/_template.js @@ -0,0 +1,4 @@ +/** + * @param {URL} projectDir + */ +export async function run(projectDir) {} diff --git a/benchmark/make-project/_util.js b/benchmark/make-project/_util.js new file mode 100644 index 000000000000..c0e17965b022 --- /dev/null +++ b/benchmark/make-project/_util.js @@ -0,0 +1,2 @@ +export const loremIpsum = + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; diff --git a/benchmark/make-project/memory-default.js b/benchmark/make-project/memory-default.js new file mode 100644 index 000000000000..021a42b0f21b --- /dev/null +++ b/benchmark/make-project/memory-default.js @@ -0,0 +1,59 @@ +import fs from 'fs/promises'; +import { loremIpsum } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/content/blog', projectDir), { recursive: true }); + + const promises = []; + + for (let i = 0; i < 100; i++) { + const content = `\ +--- +const i = ${i}; +--- + +{i} +`; + promises.push( + fs.writeFile(new URL(`./src/pages/page-${i}.astro`, projectDir), content, 'utf-8') + ); + } + + for (let i = 0; i < 100; i++) { + const content = `\ +# Article ${i} + +${loremIpsum} +`; + promises.push( + fs.writeFile(new URL(`./src/content/blog/article-${i}.md`, projectDir), content, 'utf-8') + ); + } + + await fs.writeFile( + new URL(`./src/pages/blog/[...slug].astro`, projectDir), + `\ +--- +import { getCollection } from 'astro:content'; +export async function getStaticPaths() { + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ + params: { slug: entry.slug }, props: { entry }, + })); +} +const { entry } = Astro.props; +const { Content } = await entry.render(); +--- +

{entry.data.title}

+ +`, + 'utf-8' + ); + + await Promise.all(promises); +} diff --git a/benchmark/make-project/server-stress-default.js b/benchmark/make-project/server-stress-default.js new file mode 100644 index 000000000000..c7ff6b2b4ee3 --- /dev/null +++ b/benchmark/make-project/server-stress-default.js @@ -0,0 +1,47 @@ +import fs from 'fs/promises'; +import { loremIpsum } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); + + await fs.writeFile( + new URL('./src/pages/index.astro', projectDir), + `\ +--- +const content = "${loremIpsum}" +--- + + + + + + + Astro + + +

Astro

+
+ ${Array.from({ length: 60 }).map(() => '

{content}

')} +
+ +`, + 'utf-8' + ); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; +import nodejs from '@astrojs/node'; + +export default defineConfig({ + output: 'server', + adapter: nodejs({ mode: 'standalone' }), +});`, + 'utf-8' + ); +} From f6261f8398aa0a6d46064a8e3c75a08af5265029 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 21:18:33 +0800 Subject: [PATCH 25/35] Update readme --- benchmark/README.md | 5 +++++ benchmark/bench/README.md | 7 +++++++ benchmark/index.js | 2 +- benchmark/make-project/README.md | 7 +++++++ benchmark/make-project/_template.js | 2 ++ 5 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 benchmark/README.md create mode 100644 benchmark/bench/README.md create mode 100644 benchmark/make-project/README.md diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 000000000000..79d63f4da26c --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,5 @@ +# benchmark + +Astro's main benchmark suite. It exposes the `astro-benchmark` CLI command. Run `astro-benchmark --help` to see all available commands! + +If you'd like to understand how the benchmark works, check out the other READMEs in the subfolders. diff --git a/benchmark/bench/README.md b/benchmark/bench/README.md new file mode 100644 index 000000000000..42d44f5b7ebd --- /dev/null +++ b/benchmark/bench/README.md @@ -0,0 +1,7 @@ +# bench + +This `bench` folder contains different benchmarking files that you can run via `astro-benchmark `, e.g. `astro-benchmark memory`. Files that start with an underscore are not benchmarking files. + +Benchmarking files will run against a project to measure it's performance, and write the results down as JSON in the `results` folder. The `results` folder is gitignored and it's result files can be safely deleted if you're not using it. + +You can duplicate `_template.js` to start a new benchmark test. All shared utilities are kept in `_util.js`. diff --git a/benchmark/index.js b/benchmark/index.js index 93096d020c29..6ac76759c029 100755 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -15,7 +15,7 @@ Command server-stress Run server stress test Options - --project Project to use for benchmark, see make-project.js + --project Project to use for benchmark, see benchmark/make-project/ for available names --output Output file to write results to `); process.exit(0); diff --git a/benchmark/make-project/README.md b/benchmark/make-project/README.md new file mode 100644 index 000000000000..99eb2c4672ff --- /dev/null +++ b/benchmark/make-project/README.md @@ -0,0 +1,7 @@ +# make-project + +This `make-project` folder contains different files to programmatically create a new Astro project. They are created inside the `projects` folder and are gitignored. These projects are used by benchmarks for testing. + +Each benchmark can specify the default project to run in it's `defaultProject` export, but it can be overriden if `--project ` is passed through the CLI. + +You can duplicate `_template.js` to start a new project script. All shared utilities are kept in `_util.js`. diff --git a/benchmark/make-project/_template.js b/benchmark/make-project/_template.js index 48041710b8e9..00ebdc4737b7 100644 --- a/benchmark/make-project/_template.js +++ b/benchmark/make-project/_template.js @@ -1,4 +1,6 @@ /** + * Create a new project in the `projectDir` directory. Make sure to clean up the + * previous artifacts here before generating files. * @param {URL} projectDir */ export async function run(projectDir) {} From b269511006948e915058aa885c5260b436150cf6 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 21:59:36 +0800 Subject: [PATCH 26/35] Try large md --- benchmark/make-project/_markdown-test.md | 896 +++++++++++++++++++++++ benchmark/make-project/_util.js | 10 + benchmark/make-project/memory-default.js | 16 +- benchmark/package.json | 1 + pnpm-lock.yaml | 2 + 5 files changed, 923 insertions(+), 2 deletions(-) create mode 100644 benchmark/make-project/_markdown-test.md diff --git a/benchmark/make-project/_markdown-test.md b/benchmark/make-project/_markdown-test.md new file mode 100644 index 000000000000..a8c129f60210 --- /dev/null +++ b/benchmark/make-project/_markdown-test.md @@ -0,0 +1,896 @@ +Markdown: Syntax +================ + + + + +* [Overview](#overview) + * [Philosophy](#philosophy) + * [Inline HTML](#html) + * [Automatic Escaping for Special Characters](#autoescape) +* [Block Elements](#block) + * [Paragraphs and Line Breaks](#p) + * [Headers](#header) + * [Blockquotes](#blockquote) + * [Lists](#list) + * [Code Blocks](#precode) + * [Horizontal Rules](#hr) +* [Span Elements](#span) + * [Links](#link) + * [Emphasis](#em) + * [Code](#code) + * [Images](#img) +* [Miscellaneous](#misc) + * [Backslash Escapes](#backslash) + * [Automatic Links](#autolink) + + +**Note:** This document is itself written using Markdown; you +can [see the source for it by adding '.text' to the URL][src]. + + [src]: /projects/markdown/syntax.text + +* * * + +

Overview

+ +

Philosophy

+ +Markdown is intended to be as easy-to-read and easy-to-write as is feasible. + +Readability, however, is emphasized above all else. A Markdown-formatted +document should be publishable as-is, as plain text, without looking +like it's been marked up with tags or formatting instructions. While +Markdown's syntax has been influenced by several existing text-to-HTML +filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4], +[Grutatext] [5], and [EtText] [6] -- the single biggest source of +inspiration for Markdown's syntax is the format of plain text email. + + [1]: http://docutils.sourceforge.net/mirror/setext.html + [2]: http://www.aaronsw.com/2002/atx/ + [3]: http://textism.com/tools/textile/ + [4]: http://docutils.sourceforge.net/rst.html + [5]: http://www.triptico.com/software/grutatxt.html + [6]: http://ettext.taint.org/doc/ + +To this end, Markdown's syntax is comprised entirely of punctuation +characters, which punctuation characters have been carefully chosen so +as to look like what they mean. E.g., asterisks around a word actually +look like \*emphasis\*. Markdown lists look like, well, lists. Even +blockquotes look like quoted passages of text, assuming you've ever +used email. + + + +

Inline HTML

+ +Markdown's syntax is intended for one purpose: to be used as a +format for *writing* for the web. + +Markdown is not a replacement for HTML, or even close to it. Its +syntax is very small, corresponding only to a very small subset of +HTML tags. The idea is *not* to create a syntax that makes it easier +to insert HTML tags. In my opinion, HTML tags are already easy to +insert. The idea for Markdown is to make it easy to read, write, and +edit prose. HTML is a *publishing* format; Markdown is a *writing* +format. Thus, Markdown's formatting syntax only addresses issues that +can be conveyed in plain text. + +For any markup that is not covered by Markdown's syntax, you simply +use HTML itself. There's no need to preface it or delimit it to +indicate that you're switching from Markdown to HTML; you just use +the tags. + +The only restrictions are that block-level HTML elements -- e.g. `
`, +``, `
`, `

`, etc. -- must be separated from surrounding +content by blank lines, and the start and end tags of the block should +not be indented with tabs or spaces. Markdown is smart enough not +to add extra (unwanted) `

` tags around HTML block-level tags. + +For example, to add an HTML table to a Markdown article: + + This is a regular paragraph. + +

+ + + +
Foo
+ + This is another regular paragraph. + +Note that Markdown formatting syntax is not processed within block-level +HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an +HTML block. + +Span-level HTML tags -- e.g. ``, ``, or `` -- can be +used anywhere in a Markdown paragraph, list item, or header. If you +want, you can even use HTML tags instead of Markdown formatting; e.g. if +you'd prefer to use HTML `` or `` tags instead of Markdown's +link or image syntax, go right ahead. + +Unlike block-level HTML tags, Markdown syntax *is* processed within +span-level tags. + + +

Automatic Escaping for Special Characters

+ +In HTML, there are two characters that demand special treatment: `<` +and `&`. Left angle brackets are used to start tags; ampersands are +used to denote HTML entities. If you want to use them as literal +characters, you must escape them as entities, e.g. `<`, and +`&`. + +Ampersands in particular are bedeviling for web writers. If you want to +write about 'AT&T', you need to write '`AT&T`'. You even need to +escape ampersands within URLs. Thus, if you want to link to: + + http://images.google.com/images?num=30&q=larry+bird + +you need to encode the URL as: + + http://images.google.com/images?num=30&q=larry+bird + +in your anchor tag `href` attribute. Needless to say, this is easy to +forget, and is probably the single most common source of HTML validation +errors in otherwise well-marked-up web sites. + +Markdown allows you to use these characters naturally, taking care of +all the necessary escaping for you. If you use an ampersand as part of +an HTML entity, it remains unchanged; otherwise it will be translated +into `&`. + +So, if you want to include a copyright symbol in your article, you can write: + + © + +and Markdown will leave it alone. But if you write: + + AT&T + +Markdown will translate it to: + + AT&T + +Similarly, because Markdown supports [inline HTML](#html), if you use +angle brackets as delimiters for HTML tags, Markdown will treat them as +such. But if you write: + + 4 < 5 + +Markdown will translate it to: + + 4 < 5 + +However, inside Markdown code spans and blocks, angle brackets and +ampersands are *always* encoded automatically. This makes it easy to use +Markdown to write about HTML code. (As opposed to raw HTML, which is a +terrible format for writing about HTML syntax, because every single `<` +and `&` in your example code needs to be escaped.) + + +* * * + + +

Block Elements

+ + +

Paragraphs and Line Breaks

+ +A paragraph is simply one or more consecutive lines of text, separated +by one or more blank lines. (A blank line is any line that looks like a +blank line -- a line containing nothing but spaces or tabs is considered +blank.) Normal paragraphs should not be indented with spaces or tabs. + +The implication of the "one or more consecutive lines of text" rule is +that Markdown supports "hard-wrapped" text paragraphs. This differs +significantly from most other text-to-HTML formatters (including Movable +Type's "Convert Line Breaks" option) which translate every line break +character in a paragraph into a `
` tag. + +When you *do* want to insert a `
` break tag using Markdown, you +end a line with two or more spaces, then type return. + +Yes, this takes a tad more effort to create a `
`, but a simplistic +"every line break is a `
`" rule wouldn't work for Markdown. +Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l] +work best -- and look better -- when you format them with hard breaks. + + [bq]: #blockquote + [l]: #list + + + + + +Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. + +Setext-style headers are "underlined" using equal signs (for first-level +headers) and dashes (for second-level headers). For example: + + This is an H1 + ============= + + This is an H2 + ------------- + +Any number of underlining `=`'s or `-`'s will work. + +Atx-style headers use 1-6 hash characters at the start of the line, +corresponding to header levels 1-6. For example: + + # This is an H1 + + ## This is an H2 + + ###### This is an H6 + +Optionally, you may "close" atx-style headers. This is purely +cosmetic -- you can use this if you think it looks better. The +closing hashes don't even need to match the number of hashes +used to open the header. (The number of opening hashes +determines the header level.) : + + # This is an H1 # + + ## This is an H2 ## + + ### This is an H3 ###### + + +

Blockquotes

+ +Markdown uses email-style `>` characters for blockquoting. If you're +familiar with quoting passages of text in an email message, then you +know how to create a blockquote in Markdown. It looks best if you hard +wrap the text and put a `>` before every line: + + > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, + > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. + > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + > + > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse + > id sem consectetuer libero luctus adipiscing. + +Markdown allows you to be lazy and only put the `>` before the first +line of a hard-wrapped paragraph: + + > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, + consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. + Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + + > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse + id sem consectetuer libero luctus adipiscing. + +Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by +adding additional levels of `>`: + + > This is the first level of quoting. + > + > > This is nested blockquote. + > + > Back to the first level. + +Blockquotes can contain other Markdown elements, including headers, lists, +and code blocks: + + > ## This is a header. + > + > 1. This is the first list item. + > 2. This is the second list item. + > + > Here's some example code: + > + > return shell_exec("echo $input | $markdown_script"); + +Any decent text editor should make email-style quoting easy. For +example, with BBEdit, you can make a selection and choose Increase +Quote Level from the Text menu. + + +

Lists

+ +Markdown supports ordered (numbered) and unordered (bulleted) lists. + +Unordered lists use asterisks, pluses, and hyphens -- interchangably +-- as list markers: + + * Red + * Green + * Blue + +is equivalent to: + + + Red + + Green + + Blue + +and: + + - Red + - Green + - Blue + +Ordered lists use numbers followed by periods: + + 1. Bird + 2. McHale + 3. Parish + +It's important to note that the actual numbers you use to mark the +list have no effect on the HTML output Markdown produces. The HTML +Markdown produces from the above list is: + +
    +
  1. Bird
  2. +
  3. McHale
  4. +
  5. Parish
  6. +
+ +If you instead wrote the list in Markdown like this: + + 1. Bird + 1. McHale + 1. Parish + +or even: + + 3. Bird + 1. McHale + 8. Parish + +you'd get the exact same HTML output. The point is, if you want to, +you can use ordinal numbers in your ordered Markdown lists, so that +the numbers in your source match the numbers in your published HTML. +But if you want to be lazy, you don't have to. + +If you do use lazy list numbering, however, you should still start the +list with the number 1. At some point in the future, Markdown may support +starting ordered lists at an arbitrary number. + +List markers typically start at the left margin, but may be indented by +up to three spaces. List markers must be followed by one or more spaces +or a tab. + +To make lists look nice, you can wrap items with hanging indents: + + * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, + viverra nec, fringilla in, laoreet vitae, risus. + * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. + Suspendisse id sem consectetuer libero luctus adipiscing. + +But if you want to be lazy, you don't have to: + + * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, + viverra nec, fringilla in, laoreet vitae, risus. + * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. + Suspendisse id sem consectetuer libero luctus adipiscing. + +If list items are separated by blank lines, Markdown will wrap the +items in `

` tags in the HTML output. For example, this input: + + * Bird + * Magic + +will turn into: + +

    +
  • Bird
  • +
  • Magic
  • +
+ +But this: + + * Bird + + * Magic + +will turn into: + +
    +
  • Bird

  • +
  • Magic

  • +
+ +List items may consist of multiple paragraphs. Each subsequent +paragraph in a list item must be indented by either 4 spaces +or one tab: + + 1. This is a list item with two paragraphs. Lorem ipsum dolor + sit amet, consectetuer adipiscing elit. Aliquam hendrerit + mi posuere lectus. + + Vestibulum enim wisi, viverra nec, fringilla in, laoreet + vitae, risus. Donec sit amet nisl. Aliquam semper ipsum + sit amet velit. + + 2. Suspendisse id sem consectetuer libero luctus adipiscing. + +It looks nice if you indent every line of the subsequent +paragraphs, but here again, Markdown will allow you to be +lazy: + + * This is a list item with two paragraphs. + + This is the second paragraph in the list item. You're + only required to indent the first line. Lorem ipsum dolor + sit amet, consectetuer adipiscing elit. + + * Another item in the same list. + +To put a blockquote within a list item, the blockquote's `>` +delimiters need to be indented: + + * A list item with a blockquote: + + > This is a blockquote + > inside a list item. + +To put a code block within a list item, the code block needs +to be indented *twice* -- 8 spaces or two tabs: + + * A list item with a code block: + + + + +It's worth noting that it's possible to trigger an ordered list by +accident, by writing something like this: + + 1986. What a great season. + +In other words, a *number-period-space* sequence at the beginning of a +line. To avoid this, you can backslash-escape the period: + + 1986\. What a great season. + + + +

Code Blocks

+ +Pre-formatted code blocks are used for writing about programming or +markup source code. Rather than forming normal paragraphs, the lines +of a code block are interpreted literally. Markdown wraps a code block +in both `
` and `` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab. For example, given this input:
+
+    This is a normal paragraph:
+
+        This is a code block.
+
+Markdown will generate:
+
+    

This is a normal paragraph:

+ +
This is a code block.
+    
+ +One level of indentation -- 4 spaces or 1 tab -- is removed from each +line of the code block. For example, this: + + Here is an example of AppleScript: + + tell application "Foo" + beep + end tell + +will turn into: + +

Here is an example of AppleScript:

+ +
tell application "Foo"
+        beep
+    end tell
+    
+ +A code block continues until it reaches a line that is not indented +(or the end of the article). + +Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) +are automatically converted into HTML entities. This makes it very +easy to include example HTML source code using Markdown -- just paste +it and indent it, and Markdown will handle the hassle of encoding the +ampersands and angle brackets. For example, this: + + + +will turn into: + +
<div class="footer">
+        &copy; 2004 Foo Corporation
+    </div>
+    
+ +Regular Markdown syntax is not processed within code blocks. E.g., +asterisks are just literal asterisks within a code block. This means +it's also easy to use Markdown to write about Markdown's own syntax. + + + +

Horizontal Rules

+ +You can produce a horizontal rule tag (`
`) by placing three or +more hyphens, asterisks, or underscores on a line by themselves. If you +wish, you may use spaces between the hyphens or asterisks. Each of the +following lines will produce a horizontal rule: + + * * * + + *** + + ***** + + - - - + + --------------------------------------- + + +* * * + +

Span Elements

+ + + +Markdown supports two style of links: *inline* and *reference*. + +In both styles, the link text is delimited by [square brackets]. + +To create an inline link, use a set of regular parentheses immediately +after the link text's closing square bracket. Inside the parentheses, +put the URL where you want the link to point, along with an *optional* +title for the link, surrounded in quotes. For example: + + This is [an example](http://example.com/ "Title") inline link. + + [This link](http://example.net/) has no title attribute. + +Will produce: + +

This is + an example inline link.

+ +

This link has no + title attribute.

+ +If you're referring to a local resource on the same server, you can +use relative paths: + + See my [About](/about/) page for details. + +Reference-style links use a second set of square brackets, inside +which you place a label of your choosing to identify the link: + + This is [an example][id] reference-style link. + +You can optionally use a space to separate the sets of brackets: + + This is [an example] [id] reference-style link. + +Then, anywhere in the document, you define your link label like this, +on a line by itself: + + [id]: http://example.com/ "Optional Title Here" + +That is: + +* Square brackets containing the link identifier (optionally + indented from the left margin using up to three spaces); +* followed by a colon; +* followed by one or more spaces (or tabs); +* followed by the URL for the link; +* optionally followed by a title attribute for the link, enclosed + in double or single quotes, or enclosed in parentheses. + +The following three link definitions are equivalent: + + [foo]: http://example.com/ "Optional Title Here" + [foo]: http://example.com/ 'Optional Title Here' + [foo]: http://example.com/ (Optional Title Here) + +**Note:** There is a known bug in Markdown.pl 1.0.1 which prevents +single quotes from being used to delimit link titles. + +The link URL may, optionally, be surrounded by angle brackets: + + [id]: "Optional Title Here" + +You can put the title attribute on the next line and use extra spaces +or tabs for padding, which tends to look better with longer URLs: + + [id]: http://example.com/longish/path/to/resource/here + "Optional Title Here" + +Link definitions are only used for creating links during Markdown +processing, and are stripped from your document in the HTML output. + +Link definition names may consist of letters, numbers, spaces, and +punctuation -- but they are *not* case sensitive. E.g. these two +links: + + [link text][a] + [link text][A] + +are equivalent. + +The *implicit link name* shortcut allows you to omit the name of the +link, in which case the link text itself is used as the name. +Just use an empty set of square brackets -- e.g., to link the word +"Google" to the google.com web site, you could simply write: + + [Google][] + +And then define the link: + + [Google]: http://google.com/ + +Because link names may contain spaces, this shortcut even works for +multiple words in the link text: + + Visit [Daring Fireball][] for more information. + +And then define the link: + + [Daring Fireball]: http://daringfireball.net/ + +Link definitions can be placed anywhere in your Markdown document. I +tend to put them immediately after each paragraph in which they're +used, but if you want, you can put them all at the end of your +document, sort of like footnotes. + +Here's an example of reference links in action: + + I get 10 times more traffic from [Google] [1] than from + [Yahoo] [2] or [MSN] [3]. + + [1]: http://google.com/ "Google" + [2]: http://search.yahoo.com/ "Yahoo Search" + [3]: http://search.msn.com/ "MSN Search" + +Using the implicit link name shortcut, you could instead write: + + I get 10 times more traffic from [Google][] than from + [Yahoo][] or [MSN][]. + + [google]: http://google.com/ "Google" + [yahoo]: http://search.yahoo.com/ "Yahoo Search" + [msn]: http://search.msn.com/ "MSN Search" + +Both of the above examples will produce the following HTML output: + +

I get 10 times more traffic from Google than from + Yahoo + or MSN.

+ +For comparison, here is the same paragraph written using +Markdown's inline link style: + + I get 10 times more traffic from [Google](http://google.com/ "Google") + than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or + [MSN](http://search.msn.com/ "MSN Search"). + +The point of reference-style links is not that they're easier to +write. The point is that with reference-style links, your document +source is vastly more readable. Compare the above examples: using +reference-style links, the paragraph itself is only 81 characters +long; with inline-style links, it's 176 characters; and as raw HTML, +it's 234 characters. In the raw HTML, there's more markup than there +is text. + +With Markdown's reference-style links, a source document much more +closely resembles the final output, as rendered in a browser. By +allowing you to move the markup-related metadata out of the paragraph, +you can add links without interrupting the narrative flow of your +prose. + + +

Emphasis

+ +Markdown treats asterisks (`*`) and underscores (`_`) as indicators of +emphasis. Text wrapped with one `*` or `_` will be wrapped with an +HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML +`` tag. E.g., this input: + + *single asterisks* + + _single underscores_ + + **double asterisks** + + __double underscores__ + +will produce: + + single asterisks + + single underscores + + double asterisks + + double underscores + +You can use whichever style you prefer; the lone restriction is that +the same character must be used to open and close an emphasis span. + +Emphasis can be used in the middle of a word: + + un*frigging*believable + +But if you surround an `*` or `_` with spaces, it'll be treated as a +literal asterisk or underscore. + +To produce a literal asterisk or underscore at a position where it +would otherwise be used as an emphasis delimiter, you can backslash +escape it: + + \*this text is surrounded by literal asterisks\* + + + +

Code

+ +To indicate a span of code, wrap it with backtick quotes (`` ` ``). +Unlike a pre-formatted code block, a code span indicates code within a +normal paragraph. For example: + + Use the `printf()` function. + +will produce: + +

Use the printf() function.

+ +To include a literal backtick character within a code span, you can use +multiple backticks as the opening and closing delimiters: + + ``There is a literal backtick (`) here.`` + +which will produce this: + +

There is a literal backtick (`) here.

+ +The backtick delimiters surrounding a code span may include spaces -- +one after the opening, one before the closing. This allows you to place +literal backtick characters at the beginning or end of a code span: + + A single backtick in a code span: `` ` `` + + A backtick-delimited string in a code span: `` `foo` `` + +will produce: + +

A single backtick in a code span: `

+ +

A backtick-delimited string in a code span: `foo`

+ +With a code span, ampersands and angle brackets are encoded as HTML +entities automatically, which makes it easy to include example HTML +tags. Markdown will turn this: + + Please don't use any `` tags. + +into: + +

Please don't use any <blink> tags.

+ +You can write this: + + `—` is the decimal-encoded equivalent of `—`. + +to produce: + +

&#8212; is the decimal-encoded + equivalent of &mdash;.

+ + + +

Images

+ +Admittedly, it's fairly difficult to devise a "natural" syntax for +placing images into a plain text document format. + +Markdown uses an image syntax that is intended to resemble the syntax +for links, allowing for two styles: *inline* and *reference*. + +Inline image syntax looks like this: + + ![Alt text](/path/to/img.jpg) + + ![Alt text](/path/to/img.jpg "Optional title") + +That is: + +* An exclamation mark: `!`; +* followed by a set of square brackets, containing the `alt` + attribute text for the image; +* followed by a set of parentheses, containing the URL or path to + the image, and an optional `title` attribute enclosed in double + or single quotes. + +Reference-style image syntax looks like this: + + ![Alt text][id] + +Where "id" is the name of a defined image reference. Image references +are defined using syntax identical to link references: + + [id]: url/to/image "Optional title attribute" + +As of this writing, Markdown has no syntax for specifying the +dimensions of an image; if this is important to you, you can simply +use regular HTML `` tags. + + +* * * + + +

Miscellaneous

+ + + +Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this: + + + +Markdown will turn this into: + + http://example.com/ + +Automatic links for email addresses work similarly, except that +Markdown will also perform a bit of randomized decimal and hex +entity-encoding to help obscure your address from address-harvesting +spambots. For example, Markdown will turn this: + + + +into something like this: + + address@exa + mple.com + +which will render in a browser as a clickable link to "address@example.com". + +(This sort of entity-encoding trick will indeed fool many, if not +most, address-harvesting bots, but it definitely won't fool all of +them. It's better than nothing, but an address published in this way +will probably eventually start receiving spam.) + + + +

Backslash Escapes

+ +Markdown allows you to use backslash escapes to generate literal +characters which would otherwise have special meaning in Markdown's +formatting syntax. For example, if you wanted to surround a word +with literal asterisks (instead of an HTML `` tag), you can use +backslashes before the asterisks, like this: + + \*literal asterisks\* + +Markdown provides backslash escapes for the following characters: + + \ backslash + ` backtick + * asterisk + _ underscore + {} curly braces + [] square brackets + () parentheses + # hash mark + + plus sign + - minus sign (hyphen) + . dot + ! exclamation mark diff --git a/benchmark/make-project/_util.js b/benchmark/make-project/_util.js index c0e17965b022..257f06ea5238 100644 --- a/benchmark/make-project/_util.js +++ b/benchmark/make-project/_util.js @@ -1,2 +1,12 @@ +import fs from 'fs'; + export const loremIpsum = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; + +// https://daringfireball.net/projects/markdown/syntax.text +let _markdownStressText; +export const markdownStressText = () => { + if (_markdownStressText) return _markdownStressText; + _markdownStressText = fs.readFileSync(new URL('./_markdown-test.md', import.meta.url), 'utf-8'); + return _markdownStressText; +}; diff --git a/benchmark/make-project/memory-default.js b/benchmark/make-project/memory-default.js index 021a42b0f21b..81902efe920d 100644 --- a/benchmark/make-project/memory-default.js +++ b/benchmark/make-project/memory-default.js @@ -1,5 +1,5 @@ import fs from 'fs/promises'; -import { loremIpsum } from './_util.js'; +import { markdownStressText } from './_util.js'; /** * @param {URL} projectDir @@ -28,7 +28,7 @@ const i = ${i}; const content = `\ # Article ${i} -${loremIpsum} +${markdownStressText()} `; promises.push( fs.writeFile(new URL(`./src/content/blog/article-${i}.md`, projectDir), content, 'utf-8') @@ -55,5 +55,17 @@ const { Content } = await entry.render(); 'utf-8' ); + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; + +export default defineConfig({ + integrations: [mdx()] +});`, + 'utf-8' + ); + await Promise.all(promises); } diff --git a/benchmark/package.json b/benchmark/package.json index 064cd601f642..95a4e5e7b619 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@astrojs/node": "workspace:*", + "@astrojs/mdx": "workspace:*", "astro": "workspace:*", "autocannon": "^7.10.0", "execa": "^6.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd498df0f5c5..ef8c67117c74 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,7 @@ importers: benchmark: specifiers: + '@astrojs/mdx': workspace:* '@astrojs/node': workspace:* astro: workspace:* autocannon: ^7.10.0 @@ -73,6 +74,7 @@ importers: mri: ^1.2.0 port-authority: ^2.0.1 dependencies: + '@astrojs/mdx': link:../packages/integrations/mdx '@astrojs/node': link:../packages/integrations/node astro: link:../packages/astro autocannon: 7.10.0 From 3df0c0566850ed6ff5ee0b11d4727c585ed0c1ee Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 22:05:55 +0800 Subject: [PATCH 27/35] Try no gc --- packages/astro/src/core/config/timer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/config/timer.ts b/packages/astro/src/core/config/timer.ts index 7360b55103ee..c3fc6e2c3b35 100644 --- a/packages/astro/src/core/config/timer.ts +++ b/packages/astro/src/core/config/timer.ts @@ -30,7 +30,7 @@ export class AstroTimer { */ start(name: string) { if (!this.enabled) return; - globalThis.gc?.(); + // globalThis.gc?.(); this.ongoingTimers.set(name, { startTime: performance.now(), startHeap: process.memoryUsage().heapUsed, @@ -44,7 +44,7 @@ export class AstroTimer { if (!this.enabled) return; const stat = this.ongoingTimers.get(name); if (!stat) return; - globalThis.gc?.(); + // globalThis.gc?.(); const endHeap = process.memoryUsage().heapUsed; this.stats[name] = { elapsedTime: performance.now() - stat.startTime, From e2d6311c3fb6714d4d272ac0fdcba953000361e3 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 22:21:31 +0800 Subject: [PATCH 28/35] Revert markdown and gc changes --- benchmark/make-project/_markdown-test.md | 896 ----------------------- benchmark/make-project/_util.js | 10 - benchmark/make-project/memory-default.js | 16 +- benchmark/package.json | 1 - packages/astro/src/core/config/timer.ts | 4 +- pnpm-lock.yaml | 2 - 6 files changed, 4 insertions(+), 925 deletions(-) delete mode 100644 benchmark/make-project/_markdown-test.md diff --git a/benchmark/make-project/_markdown-test.md b/benchmark/make-project/_markdown-test.md deleted file mode 100644 index a8c129f60210..000000000000 --- a/benchmark/make-project/_markdown-test.md +++ /dev/null @@ -1,896 +0,0 @@ -Markdown: Syntax -================ - - - - -* [Overview](#overview) - * [Philosophy](#philosophy) - * [Inline HTML](#html) - * [Automatic Escaping for Special Characters](#autoescape) -* [Block Elements](#block) - * [Paragraphs and Line Breaks](#p) - * [Headers](#header) - * [Blockquotes](#blockquote) - * [Lists](#list) - * [Code Blocks](#precode) - * [Horizontal Rules](#hr) -* [Span Elements](#span) - * [Links](#link) - * [Emphasis](#em) - * [Code](#code) - * [Images](#img) -* [Miscellaneous](#misc) - * [Backslash Escapes](#backslash) - * [Automatic Links](#autolink) - - -**Note:** This document is itself written using Markdown; you -can [see the source for it by adding '.text' to the URL][src]. - - [src]: /projects/markdown/syntax.text - -* * * - -

Overview

- -

Philosophy

- -Markdown is intended to be as easy-to-read and easy-to-write as is feasible. - -Readability, however, is emphasized above all else. A Markdown-formatted -document should be publishable as-is, as plain text, without looking -like it's been marked up with tags or formatting instructions. While -Markdown's syntax has been influenced by several existing text-to-HTML -filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4], -[Grutatext] [5], and [EtText] [6] -- the single biggest source of -inspiration for Markdown's syntax is the format of plain text email. - - [1]: http://docutils.sourceforge.net/mirror/setext.html - [2]: http://www.aaronsw.com/2002/atx/ - [3]: http://textism.com/tools/textile/ - [4]: http://docutils.sourceforge.net/rst.html - [5]: http://www.triptico.com/software/grutatxt.html - [6]: http://ettext.taint.org/doc/ - -To this end, Markdown's syntax is comprised entirely of punctuation -characters, which punctuation characters have been carefully chosen so -as to look like what they mean. E.g., asterisks around a word actually -look like \*emphasis\*. Markdown lists look like, well, lists. Even -blockquotes look like quoted passages of text, assuming you've ever -used email. - - - -

Inline HTML

- -Markdown's syntax is intended for one purpose: to be used as a -format for *writing* for the web. - -Markdown is not a replacement for HTML, or even close to it. Its -syntax is very small, corresponding only to a very small subset of -HTML tags. The idea is *not* to create a syntax that makes it easier -to insert HTML tags. In my opinion, HTML tags are already easy to -insert. The idea for Markdown is to make it easy to read, write, and -edit prose. HTML is a *publishing* format; Markdown is a *writing* -format. Thus, Markdown's formatting syntax only addresses issues that -can be conveyed in plain text. - -For any markup that is not covered by Markdown's syntax, you simply -use HTML itself. There's no need to preface it or delimit it to -indicate that you're switching from Markdown to HTML; you just use -the tags. - -The only restrictions are that block-level HTML elements -- e.g. `
`, -``, `
`, `

`, etc. -- must be separated from surrounding -content by blank lines, and the start and end tags of the block should -not be indented with tabs or spaces. Markdown is smart enough not -to add extra (unwanted) `

` tags around HTML block-level tags. - -For example, to add an HTML table to a Markdown article: - - This is a regular paragraph. - -

- - - -
Foo
- - This is another regular paragraph. - -Note that Markdown formatting syntax is not processed within block-level -HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an -HTML block. - -Span-level HTML tags -- e.g. ``, ``, or `` -- can be -used anywhere in a Markdown paragraph, list item, or header. If you -want, you can even use HTML tags instead of Markdown formatting; e.g. if -you'd prefer to use HTML `` or `` tags instead of Markdown's -link or image syntax, go right ahead. - -Unlike block-level HTML tags, Markdown syntax *is* processed within -span-level tags. - - -

Automatic Escaping for Special Characters

- -In HTML, there are two characters that demand special treatment: `<` -and `&`. Left angle brackets are used to start tags; ampersands are -used to denote HTML entities. If you want to use them as literal -characters, you must escape them as entities, e.g. `<`, and -`&`. - -Ampersands in particular are bedeviling for web writers. If you want to -write about 'AT&T', you need to write '`AT&T`'. You even need to -escape ampersands within URLs. Thus, if you want to link to: - - http://images.google.com/images?num=30&q=larry+bird - -you need to encode the URL as: - - http://images.google.com/images?num=30&q=larry+bird - -in your anchor tag `href` attribute. Needless to say, this is easy to -forget, and is probably the single most common source of HTML validation -errors in otherwise well-marked-up web sites. - -Markdown allows you to use these characters naturally, taking care of -all the necessary escaping for you. If you use an ampersand as part of -an HTML entity, it remains unchanged; otherwise it will be translated -into `&`. - -So, if you want to include a copyright symbol in your article, you can write: - - © - -and Markdown will leave it alone. But if you write: - - AT&T - -Markdown will translate it to: - - AT&T - -Similarly, because Markdown supports [inline HTML](#html), if you use -angle brackets as delimiters for HTML tags, Markdown will treat them as -such. But if you write: - - 4 < 5 - -Markdown will translate it to: - - 4 < 5 - -However, inside Markdown code spans and blocks, angle brackets and -ampersands are *always* encoded automatically. This makes it easy to use -Markdown to write about HTML code. (As opposed to raw HTML, which is a -terrible format for writing about HTML syntax, because every single `<` -and `&` in your example code needs to be escaped.) - - -* * * - - -

Block Elements

- - -

Paragraphs and Line Breaks

- -A paragraph is simply one or more consecutive lines of text, separated -by one or more blank lines. (A blank line is any line that looks like a -blank line -- a line containing nothing but spaces or tabs is considered -blank.) Normal paragraphs should not be indented with spaces or tabs. - -The implication of the "one or more consecutive lines of text" rule is -that Markdown supports "hard-wrapped" text paragraphs. This differs -significantly from most other text-to-HTML formatters (including Movable -Type's "Convert Line Breaks" option) which translate every line break -character in a paragraph into a `
` tag. - -When you *do* want to insert a `
` break tag using Markdown, you -end a line with two or more spaces, then type return. - -Yes, this takes a tad more effort to create a `
`, but a simplistic -"every line break is a `
`" rule wouldn't work for Markdown. -Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l] -work best -- and look better -- when you format them with hard breaks. - - [bq]: #blockquote - [l]: #list - - - - - -Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. - -Setext-style headers are "underlined" using equal signs (for first-level -headers) and dashes (for second-level headers). For example: - - This is an H1 - ============= - - This is an H2 - ------------- - -Any number of underlining `=`'s or `-`'s will work. - -Atx-style headers use 1-6 hash characters at the start of the line, -corresponding to header levels 1-6. For example: - - # This is an H1 - - ## This is an H2 - - ###### This is an H6 - -Optionally, you may "close" atx-style headers. This is purely -cosmetic -- you can use this if you think it looks better. The -closing hashes don't even need to match the number of hashes -used to open the header. (The number of opening hashes -determines the header level.) : - - # This is an H1 # - - ## This is an H2 ## - - ### This is an H3 ###### - - -

Blockquotes

- -Markdown uses email-style `>` characters for blockquoting. If you're -familiar with quoting passages of text in an email message, then you -know how to create a blockquote in Markdown. It looks best if you hard -wrap the text and put a `>` before every line: - - > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, - > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. - > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. - > - > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse - > id sem consectetuer libero luctus adipiscing. - -Markdown allows you to be lazy and only put the `>` before the first -line of a hard-wrapped paragraph: - - > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, - consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. - Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. - - > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse - id sem consectetuer libero luctus adipiscing. - -Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by -adding additional levels of `>`: - - > This is the first level of quoting. - > - > > This is nested blockquote. - > - > Back to the first level. - -Blockquotes can contain other Markdown elements, including headers, lists, -and code blocks: - - > ## This is a header. - > - > 1. This is the first list item. - > 2. This is the second list item. - > - > Here's some example code: - > - > return shell_exec("echo $input | $markdown_script"); - -Any decent text editor should make email-style quoting easy. For -example, with BBEdit, you can make a selection and choose Increase -Quote Level from the Text menu. - - -

Lists

- -Markdown supports ordered (numbered) and unordered (bulleted) lists. - -Unordered lists use asterisks, pluses, and hyphens -- interchangably --- as list markers: - - * Red - * Green - * Blue - -is equivalent to: - - + Red - + Green - + Blue - -and: - - - Red - - Green - - Blue - -Ordered lists use numbers followed by periods: - - 1. Bird - 2. McHale - 3. Parish - -It's important to note that the actual numbers you use to mark the -list have no effect on the HTML output Markdown produces. The HTML -Markdown produces from the above list is: - -
    -
  1. Bird
  2. -
  3. McHale
  4. -
  5. Parish
  6. -
- -If you instead wrote the list in Markdown like this: - - 1. Bird - 1. McHale - 1. Parish - -or even: - - 3. Bird - 1. McHale - 8. Parish - -you'd get the exact same HTML output. The point is, if you want to, -you can use ordinal numbers in your ordered Markdown lists, so that -the numbers in your source match the numbers in your published HTML. -But if you want to be lazy, you don't have to. - -If you do use lazy list numbering, however, you should still start the -list with the number 1. At some point in the future, Markdown may support -starting ordered lists at an arbitrary number. - -List markers typically start at the left margin, but may be indented by -up to three spaces. List markers must be followed by one or more spaces -or a tab. - -To make lists look nice, you can wrap items with hanging indents: - - * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. - Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, - viverra nec, fringilla in, laoreet vitae, risus. - * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. - Suspendisse id sem consectetuer libero luctus adipiscing. - -But if you want to be lazy, you don't have to: - - * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. - Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, - viverra nec, fringilla in, laoreet vitae, risus. - * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. - Suspendisse id sem consectetuer libero luctus adipiscing. - -If list items are separated by blank lines, Markdown will wrap the -items in `

` tags in the HTML output. For example, this input: - - * Bird - * Magic - -will turn into: - -

    -
  • Bird
  • -
  • Magic
  • -
- -But this: - - * Bird - - * Magic - -will turn into: - -
    -
  • Bird

  • -
  • Magic

  • -
- -List items may consist of multiple paragraphs. Each subsequent -paragraph in a list item must be indented by either 4 spaces -or one tab: - - 1. This is a list item with two paragraphs. Lorem ipsum dolor - sit amet, consectetuer adipiscing elit. Aliquam hendrerit - mi posuere lectus. - - Vestibulum enim wisi, viverra nec, fringilla in, laoreet - vitae, risus. Donec sit amet nisl. Aliquam semper ipsum - sit amet velit. - - 2. Suspendisse id sem consectetuer libero luctus adipiscing. - -It looks nice if you indent every line of the subsequent -paragraphs, but here again, Markdown will allow you to be -lazy: - - * This is a list item with two paragraphs. - - This is the second paragraph in the list item. You're - only required to indent the first line. Lorem ipsum dolor - sit amet, consectetuer adipiscing elit. - - * Another item in the same list. - -To put a blockquote within a list item, the blockquote's `>` -delimiters need to be indented: - - * A list item with a blockquote: - - > This is a blockquote - > inside a list item. - -To put a code block within a list item, the code block needs -to be indented *twice* -- 8 spaces or two tabs: - - * A list item with a code block: - - - - -It's worth noting that it's possible to trigger an ordered list by -accident, by writing something like this: - - 1986. What a great season. - -In other words, a *number-period-space* sequence at the beginning of a -line. To avoid this, you can backslash-escape the period: - - 1986\. What a great season. - - - -

Code Blocks

- -Pre-formatted code blocks are used for writing about programming or -markup source code. Rather than forming normal paragraphs, the lines -of a code block are interpreted literally. Markdown wraps a code block -in both `
` and `` tags.
-
-To produce a code block in Markdown, simply indent every line of the
-block by at least 4 spaces or 1 tab. For example, given this input:
-
-    This is a normal paragraph:
-
-        This is a code block.
-
-Markdown will generate:
-
-    

This is a normal paragraph:

- -
This is a code block.
-    
- -One level of indentation -- 4 spaces or 1 tab -- is removed from each -line of the code block. For example, this: - - Here is an example of AppleScript: - - tell application "Foo" - beep - end tell - -will turn into: - -

Here is an example of AppleScript:

- -
tell application "Foo"
-        beep
-    end tell
-    
- -A code block continues until it reaches a line that is not indented -(or the end of the article). - -Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) -are automatically converted into HTML entities. This makes it very -easy to include example HTML source code using Markdown -- just paste -it and indent it, and Markdown will handle the hassle of encoding the -ampersands and angle brackets. For example, this: - - - -will turn into: - -
<div class="footer">
-        &copy; 2004 Foo Corporation
-    </div>
-    
- -Regular Markdown syntax is not processed within code blocks. E.g., -asterisks are just literal asterisks within a code block. This means -it's also easy to use Markdown to write about Markdown's own syntax. - - - -

Horizontal Rules

- -You can produce a horizontal rule tag (`
`) by placing three or -more hyphens, asterisks, or underscores on a line by themselves. If you -wish, you may use spaces between the hyphens or asterisks. Each of the -following lines will produce a horizontal rule: - - * * * - - *** - - ***** - - - - - - - --------------------------------------- - - -* * * - -

Span Elements

- - - -Markdown supports two style of links: *inline* and *reference*. - -In both styles, the link text is delimited by [square brackets]. - -To create an inline link, use a set of regular parentheses immediately -after the link text's closing square bracket. Inside the parentheses, -put the URL where you want the link to point, along with an *optional* -title for the link, surrounded in quotes. For example: - - This is [an example](http://example.com/ "Title") inline link. - - [This link](http://example.net/) has no title attribute. - -Will produce: - -

This is - an example inline link.

- -

This link has no - title attribute.

- -If you're referring to a local resource on the same server, you can -use relative paths: - - See my [About](/about/) page for details. - -Reference-style links use a second set of square brackets, inside -which you place a label of your choosing to identify the link: - - This is [an example][id] reference-style link. - -You can optionally use a space to separate the sets of brackets: - - This is [an example] [id] reference-style link. - -Then, anywhere in the document, you define your link label like this, -on a line by itself: - - [id]: http://example.com/ "Optional Title Here" - -That is: - -* Square brackets containing the link identifier (optionally - indented from the left margin using up to three spaces); -* followed by a colon; -* followed by one or more spaces (or tabs); -* followed by the URL for the link; -* optionally followed by a title attribute for the link, enclosed - in double or single quotes, or enclosed in parentheses. - -The following three link definitions are equivalent: - - [foo]: http://example.com/ "Optional Title Here" - [foo]: http://example.com/ 'Optional Title Here' - [foo]: http://example.com/ (Optional Title Here) - -**Note:** There is a known bug in Markdown.pl 1.0.1 which prevents -single quotes from being used to delimit link titles. - -The link URL may, optionally, be surrounded by angle brackets: - - [id]: "Optional Title Here" - -You can put the title attribute on the next line and use extra spaces -or tabs for padding, which tends to look better with longer URLs: - - [id]: http://example.com/longish/path/to/resource/here - "Optional Title Here" - -Link definitions are only used for creating links during Markdown -processing, and are stripped from your document in the HTML output. - -Link definition names may consist of letters, numbers, spaces, and -punctuation -- but they are *not* case sensitive. E.g. these two -links: - - [link text][a] - [link text][A] - -are equivalent. - -The *implicit link name* shortcut allows you to omit the name of the -link, in which case the link text itself is used as the name. -Just use an empty set of square brackets -- e.g., to link the word -"Google" to the google.com web site, you could simply write: - - [Google][] - -And then define the link: - - [Google]: http://google.com/ - -Because link names may contain spaces, this shortcut even works for -multiple words in the link text: - - Visit [Daring Fireball][] for more information. - -And then define the link: - - [Daring Fireball]: http://daringfireball.net/ - -Link definitions can be placed anywhere in your Markdown document. I -tend to put them immediately after each paragraph in which they're -used, but if you want, you can put them all at the end of your -document, sort of like footnotes. - -Here's an example of reference links in action: - - I get 10 times more traffic from [Google] [1] than from - [Yahoo] [2] or [MSN] [3]. - - [1]: http://google.com/ "Google" - [2]: http://search.yahoo.com/ "Yahoo Search" - [3]: http://search.msn.com/ "MSN Search" - -Using the implicit link name shortcut, you could instead write: - - I get 10 times more traffic from [Google][] than from - [Yahoo][] or [MSN][]. - - [google]: http://google.com/ "Google" - [yahoo]: http://search.yahoo.com/ "Yahoo Search" - [msn]: http://search.msn.com/ "MSN Search" - -Both of the above examples will produce the following HTML output: - -

I get 10 times more traffic from Google than from - Yahoo - or MSN.

- -For comparison, here is the same paragraph written using -Markdown's inline link style: - - I get 10 times more traffic from [Google](http://google.com/ "Google") - than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or - [MSN](http://search.msn.com/ "MSN Search"). - -The point of reference-style links is not that they're easier to -write. The point is that with reference-style links, your document -source is vastly more readable. Compare the above examples: using -reference-style links, the paragraph itself is only 81 characters -long; with inline-style links, it's 176 characters; and as raw HTML, -it's 234 characters. In the raw HTML, there's more markup than there -is text. - -With Markdown's reference-style links, a source document much more -closely resembles the final output, as rendered in a browser. By -allowing you to move the markup-related metadata out of the paragraph, -you can add links without interrupting the narrative flow of your -prose. - - -

Emphasis

- -Markdown treats asterisks (`*`) and underscores (`_`) as indicators of -emphasis. Text wrapped with one `*` or `_` will be wrapped with an -HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML -`` tag. E.g., this input: - - *single asterisks* - - _single underscores_ - - **double asterisks** - - __double underscores__ - -will produce: - - single asterisks - - single underscores - - double asterisks - - double underscores - -You can use whichever style you prefer; the lone restriction is that -the same character must be used to open and close an emphasis span. - -Emphasis can be used in the middle of a word: - - un*frigging*believable - -But if you surround an `*` or `_` with spaces, it'll be treated as a -literal asterisk or underscore. - -To produce a literal asterisk or underscore at a position where it -would otherwise be used as an emphasis delimiter, you can backslash -escape it: - - \*this text is surrounded by literal asterisks\* - - - -

Code

- -To indicate a span of code, wrap it with backtick quotes (`` ` ``). -Unlike a pre-formatted code block, a code span indicates code within a -normal paragraph. For example: - - Use the `printf()` function. - -will produce: - -

Use the printf() function.

- -To include a literal backtick character within a code span, you can use -multiple backticks as the opening and closing delimiters: - - ``There is a literal backtick (`) here.`` - -which will produce this: - -

There is a literal backtick (`) here.

- -The backtick delimiters surrounding a code span may include spaces -- -one after the opening, one before the closing. This allows you to place -literal backtick characters at the beginning or end of a code span: - - A single backtick in a code span: `` ` `` - - A backtick-delimited string in a code span: `` `foo` `` - -will produce: - -

A single backtick in a code span: `

- -

A backtick-delimited string in a code span: `foo`

- -With a code span, ampersands and angle brackets are encoded as HTML -entities automatically, which makes it easy to include example HTML -tags. Markdown will turn this: - - Please don't use any `` tags. - -into: - -

Please don't use any <blink> tags.

- -You can write this: - - `—` is the decimal-encoded equivalent of `—`. - -to produce: - -

&#8212; is the decimal-encoded - equivalent of &mdash;.

- - - -

Images

- -Admittedly, it's fairly difficult to devise a "natural" syntax for -placing images into a plain text document format. - -Markdown uses an image syntax that is intended to resemble the syntax -for links, allowing for two styles: *inline* and *reference*. - -Inline image syntax looks like this: - - ![Alt text](/path/to/img.jpg) - - ![Alt text](/path/to/img.jpg "Optional title") - -That is: - -* An exclamation mark: `!`; -* followed by a set of square brackets, containing the `alt` - attribute text for the image; -* followed by a set of parentheses, containing the URL or path to - the image, and an optional `title` attribute enclosed in double - or single quotes. - -Reference-style image syntax looks like this: - - ![Alt text][id] - -Where "id" is the name of a defined image reference. Image references -are defined using syntax identical to link references: - - [id]: url/to/image "Optional title attribute" - -As of this writing, Markdown has no syntax for specifying the -dimensions of an image; if this is important to you, you can simply -use regular HTML `` tags. - - -* * * - - -

Miscellaneous

- - - -Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this: - - - -Markdown will turn this into: - - http://example.com/ - -Automatic links for email addresses work similarly, except that -Markdown will also perform a bit of randomized decimal and hex -entity-encoding to help obscure your address from address-harvesting -spambots. For example, Markdown will turn this: - - - -into something like this: - - address@exa - mple.com - -which will render in a browser as a clickable link to "address@example.com". - -(This sort of entity-encoding trick will indeed fool many, if not -most, address-harvesting bots, but it definitely won't fool all of -them. It's better than nothing, but an address published in this way -will probably eventually start receiving spam.) - - - -

Backslash Escapes

- -Markdown allows you to use backslash escapes to generate literal -characters which would otherwise have special meaning in Markdown's -formatting syntax. For example, if you wanted to surround a word -with literal asterisks (instead of an HTML `` tag), you can use -backslashes before the asterisks, like this: - - \*literal asterisks\* - -Markdown provides backslash escapes for the following characters: - - \ backslash - ` backtick - * asterisk - _ underscore - {} curly braces - [] square brackets - () parentheses - # hash mark - + plus sign - - minus sign (hyphen) - . dot - ! exclamation mark diff --git a/benchmark/make-project/_util.js b/benchmark/make-project/_util.js index 257f06ea5238..c0e17965b022 100644 --- a/benchmark/make-project/_util.js +++ b/benchmark/make-project/_util.js @@ -1,12 +1,2 @@ -import fs from 'fs'; - export const loremIpsum = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; - -// https://daringfireball.net/projects/markdown/syntax.text -let _markdownStressText; -export const markdownStressText = () => { - if (_markdownStressText) return _markdownStressText; - _markdownStressText = fs.readFileSync(new URL('./_markdown-test.md', import.meta.url), 'utf-8'); - return _markdownStressText; -}; diff --git a/benchmark/make-project/memory-default.js b/benchmark/make-project/memory-default.js index 81902efe920d..021a42b0f21b 100644 --- a/benchmark/make-project/memory-default.js +++ b/benchmark/make-project/memory-default.js @@ -1,5 +1,5 @@ import fs from 'fs/promises'; -import { markdownStressText } from './_util.js'; +import { loremIpsum } from './_util.js'; /** * @param {URL} projectDir @@ -28,7 +28,7 @@ const i = ${i}; const content = `\ # Article ${i} -${markdownStressText()} +${loremIpsum} `; promises.push( fs.writeFile(new URL(`./src/content/blog/article-${i}.md`, projectDir), content, 'utf-8') @@ -55,17 +55,5 @@ const { Content } = await entry.render(); 'utf-8' ); - await fs.writeFile( - new URL('./astro.config.js', projectDir), - `\ -import { defineConfig } from 'astro/config'; -import mdx from '@astrojs/mdx'; - -export default defineConfig({ - integrations: [mdx()] -});`, - 'utf-8' - ); - await Promise.all(promises); } diff --git a/benchmark/package.json b/benchmark/package.json index 95a4e5e7b619..064cd601f642 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -8,7 +8,6 @@ }, "dependencies": { "@astrojs/node": "workspace:*", - "@astrojs/mdx": "workspace:*", "astro": "workspace:*", "autocannon": "^7.10.0", "execa": "^6.1.0", diff --git a/packages/astro/src/core/config/timer.ts b/packages/astro/src/core/config/timer.ts index c3fc6e2c3b35..7360b55103ee 100644 --- a/packages/astro/src/core/config/timer.ts +++ b/packages/astro/src/core/config/timer.ts @@ -30,7 +30,7 @@ export class AstroTimer { */ start(name: string) { if (!this.enabled) return; - // globalThis.gc?.(); + globalThis.gc?.(); this.ongoingTimers.set(name, { startTime: performance.now(), startHeap: process.memoryUsage().heapUsed, @@ -44,7 +44,7 @@ export class AstroTimer { if (!this.enabled) return; const stat = this.ongoingTimers.get(name); if (!stat) return; - // globalThis.gc?.(); + globalThis.gc?.(); const endHeap = process.memoryUsage().heapUsed; this.stats[name] = { elapsedTime: performance.now() - stat.startTime, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef8c67117c74..cd498df0f5c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,7 +65,6 @@ importers: benchmark: specifiers: - '@astrojs/mdx': workspace:* '@astrojs/node': workspace:* astro: workspace:* autocannon: ^7.10.0 @@ -74,7 +73,6 @@ importers: mri: ^1.2.0 port-authority: ^2.0.1 dependencies: - '@astrojs/mdx': link:../packages/integrations/mdx '@astrojs/node': link:../packages/integrations/node astro: link:../packages/astro autocannon: 7.10.0 From 692e9a24e12bf62ea619a6c00d97f90223ff26c6 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 22:41:29 +0800 Subject: [PATCH 29/35] Test sha --- .github/workflows/benchmark.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index f3dd1d48b34e..2d01be2c1bac 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -19,10 +19,13 @@ jobs: PR-BENCH: ${{ steps.benchmark-pr.outputs.BENCH_RESULT }} MAIN-BENCH: ${{ steps.benchmark-main.outputs.BENCH_RESULT }} steps: + - name: Check + run: echo "${{github.event.pull_request.head.sha}}" + - uses: actions/checkout@v3 with: persist-credentials: false - ref: ${{github.event.pull_request.head.sha}} + ref: "${{github.event.pull_request.head.sha}}" repository: ${{github.event.pull_request.head.repo.full_name}} - name: Setup PNPM From 7a0768fb40183e20879f20f1e4a2fc3750f73dff Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 22:59:41 +0800 Subject: [PATCH 30/35] Try ref --- .github/workflows/benchmark.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 2d01be2c1bac..25c747f6c65b 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -19,14 +19,11 @@ jobs: PR-BENCH: ${{ steps.benchmark-pr.outputs.BENCH_RESULT }} MAIN-BENCH: ${{ steps.benchmark-main.outputs.BENCH_RESULT }} steps: - - name: Check - run: echo "${{github.event.pull_request.head.sha}}" - + # https://github.com/actions/checkout/issues/331#issuecomment-1438220926 - uses: actions/checkout@v3 with: persist-credentials: false - ref: "${{github.event.pull_request.head.sha}}" - repository: ${{github.event.pull_request.head.repo.full_name}} + ref: refs/pull/${{ github.event.issue.number }}/head - name: Setup PNPM uses: pnpm/action-setup@v2 From 3cecf698f1fa2d5177952b136aad9cf658a02ce7 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 23:33:40 +0800 Subject: [PATCH 31/35] Try 128mb --- benchmark/bench/memory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/bench/memory.js b/benchmark/bench/memory.js index 909a61380194..924fa39840f1 100644 --- a/benchmark/bench/memory.js +++ b/benchmark/bench/memory.js @@ -18,7 +18,7 @@ export async function run(projectDir, outputFile) { const outputFilePath = fileURLToPath(outputFile); console.log('Building and benchmarking...'); - await execaCommand(`node --expose-gc --max_old_space_size=512 ${astroBin} build`, { + await execaCommand(`node --expose-gc --max_old_space_size=128 ${astroBin} build`, { cwd: root, stdio: 'inherit', env: { From dbd437838969ce02ca542948e32ab2d9947d34f1 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 23:46:45 +0800 Subject: [PATCH 32/35] Set 256 --- benchmark/bench/memory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/bench/memory.js b/benchmark/bench/memory.js index 924fa39840f1..1f0d8ab8bc2d 100644 --- a/benchmark/bench/memory.js +++ b/benchmark/bench/memory.js @@ -18,7 +18,7 @@ export async function run(projectDir, outputFile) { const outputFilePath = fileURLToPath(outputFile); console.log('Building and benchmarking...'); - await execaCommand(`node --expose-gc --max_old_space_size=128 ${astroBin} build`, { + await execaCommand(`node --expose-gc --max_old_space_size=256 ${astroBin} build`, { cwd: root, stdio: 'inherit', env: { From 314c06d2cb7bcdfb3df4bcdb6eedeb0f0908d626 Mon Sep 17 00:00:00 2001 From: bluwy Date: Tue, 28 Feb 2023 23:52:30 +0800 Subject: [PATCH 33/35] Add guard --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 25c747f6c65b..0dcfd6105b50 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -11,7 +11,7 @@ env: jobs: benchmark: - if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} runs-on: ubuntu-latest permissions: contents: read @@ -83,7 +83,7 @@ jobs: shell: bash output-benchmark: - if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} + if: ${{ github.repository_owner == 'withastro' && github.event.issue.pull_request && startsWith(github.event.comment.body, '!bench') }} needs: [benchmark] runs-on: ubuntu-latest permissions: From 636925f692be8f9ec6f417d8eea06f46d5b327f6 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 1 Mar 2023 16:10:16 +0800 Subject: [PATCH 34/35] Apply suggestions from code review Co-authored-by: Sarah Rainsberger --- benchmark/bench/README.md | 2 +- benchmark/make-project/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/bench/README.md b/benchmark/bench/README.md index 42d44f5b7ebd..9d3312880bb0 100644 --- a/benchmark/bench/README.md +++ b/benchmark/bench/README.md @@ -2,6 +2,6 @@ This `bench` folder contains different benchmarking files that you can run via `astro-benchmark `, e.g. `astro-benchmark memory`. Files that start with an underscore are not benchmarking files. -Benchmarking files will run against a project to measure it's performance, and write the results down as JSON in the `results` folder. The `results` folder is gitignored and it's result files can be safely deleted if you're not using it. +Benchmarking files will run against a project to measure its performance, and write the results down as JSON in the `results` folder. The `results` folder is gitignored and its result files can be safely deleted if you're not using them. You can duplicate `_template.js` to start a new benchmark test. All shared utilities are kept in `_util.js`. diff --git a/benchmark/make-project/README.md b/benchmark/make-project/README.md index 99eb2c4672ff..3199d1b7a747 100644 --- a/benchmark/make-project/README.md +++ b/benchmark/make-project/README.md @@ -2,6 +2,6 @@ This `make-project` folder contains different files to programmatically create a new Astro project. They are created inside the `projects` folder and are gitignored. These projects are used by benchmarks for testing. -Each benchmark can specify the default project to run in it's `defaultProject` export, but it can be overriden if `--project ` is passed through the CLI. +Each benchmark can specify the default project to run in its `defaultProject` export, but it can be overriden if `--project ` is passed through the CLI. You can duplicate `_template.js` to start a new project script. All shared utilities are kept in `_util.js`. From 9a943d3e68db65a5d29f58809997c5f234a483d4 Mon Sep 17 00:00:00 2001 From: bluwy Date: Wed, 1 Mar 2023 16:21:35 +0800 Subject: [PATCH 35/35] Add docs comment --- benchmark/bench/server-stress.js | 3 +++ benchmark/ci-helper.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/benchmark/bench/server-stress.js b/benchmark/bench/server-stress.js index 600e2b9406a8..6237b2e5f59b 100644 --- a/benchmark/bench/server-stress.js +++ b/benchmark/bench/server-stress.js @@ -46,6 +46,9 @@ export async function run(projectDir, outputFile) { console.log('='.repeat(10)); console.log(`#### Server stress\n\n`); let text = autocannon.printResult(result); + // Truncate the logs in CI so that the generated comment from the `!bench` command + // is shortened. Also we only need this information when comparing runs. + // Full log example: https://github.com/mcollina/autocannon#command-line if (process.env.CI) { text = text.match(/^.*?requests in.*?read$/m)?.[0]; } diff --git a/benchmark/ci-helper.js b/benchmark/ci-helper.js index fce738841441..2dbdf5acf195 100644 --- a/benchmark/ci-helper.js +++ b/benchmark/ci-helper.js @@ -1,3 +1,6 @@ +// This script helps extract the benchmark logs that are between the `==========` lines. +// They are a convention defined in the `./bench/_template.js` file, which are used to log +// out with the `!bench` command. See `/.github/workflows/benchmark.yml` to see how it's used. const benchLogs = process.argv[2]; const resultRegex = /==========(.*?)==========/gs;