193fb6ee3Sopenharmony_ciimport { readFileSync, createReadStream, readdirSync } from 'node:fs'; 293fb6ee3Sopenharmony_ciimport Benchmark from 'benchmark'; 393fb6ee3Sopenharmony_ciimport { loadTreeConstructionTestData } from 'parse5-test-utils/dist/generate-parsing-tests.js'; 493fb6ee3Sopenharmony_ciimport { loadSAXParserTestData } from 'parse5-test-utils/dist/load-sax-parser-test-data.js'; 593fb6ee3Sopenharmony_ciimport { treeAdapters, WritableStreamStub, finished } from 'parse5-test-utils/dist/common.js'; 693fb6ee3Sopenharmony_ciimport * as parse5 from '../../packages/parse5/dist/index.js'; 793fb6ee3Sopenharmony_ciimport { ParserStream as parse5Stream } from '../../packages/parse5-parser-stream/dist/index.js'; 893fb6ee3Sopenharmony_ciimport * as parse5Upstream from 'parse5'; 993fb6ee3Sopenharmony_ci 1093fb6ee3Sopenharmony_ciconst hugePagePath = new URL('../../test/data/huge-page/huge-page.html', import.meta.url); 1193fb6ee3Sopenharmony_ciconst treeConstructionPath = new URL('../../test/data/html5lib-tests/tree-construction', import.meta.url); 1293fb6ee3Sopenharmony_ciconst saxPath = new URL('../../test/data/sax/', import.meta.url); 1393fb6ee3Sopenharmony_ci 1493fb6ee3Sopenharmony_ci//HACK: https://github.com/bestiejs/benchmark.js/issues/51 1593fb6ee3Sopenharmony_ci/* global workingCopy, WorkingCopyParserStream, upstreamParser, hugePage, microTests, runMicro, runPages, files */ 1693fb6ee3Sopenharmony_ciglobal.workingCopy = parse5; 1793fb6ee3Sopenharmony_ciglobal.WorkingCopyParserStream = parse5Stream; 1893fb6ee3Sopenharmony_ciglobal.upstreamParser = parse5Upstream; 1993fb6ee3Sopenharmony_ci 2093fb6ee3Sopenharmony_ci// Huge page data 2193fb6ee3Sopenharmony_ciglobal.hugePage = readFileSync(hugePagePath).toString(); 2293fb6ee3Sopenharmony_ci 2393fb6ee3Sopenharmony_ci// Micro data 2493fb6ee3Sopenharmony_ciglobal.microTests = loadTreeConstructionTestData(treeConstructionPath, treeAdapters.default) 2593fb6ee3Sopenharmony_ci .filter( 2693fb6ee3Sopenharmony_ci (test) => 2793fb6ee3Sopenharmony_ci //NOTE: this test caused a stack overflow in parse5 v1.x 2893fb6ee3Sopenharmony_ci test.input !== '<button><p><button>' 2993fb6ee3Sopenharmony_ci ) 3093fb6ee3Sopenharmony_ci .map((test) => ({ 3193fb6ee3Sopenharmony_ci html: test.input, 3293fb6ee3Sopenharmony_ci fragmentContext: test.fragmentContext, 3393fb6ee3Sopenharmony_ci })); 3493fb6ee3Sopenharmony_ci 3593fb6ee3Sopenharmony_ciglobal.runMicro = function (parser) { 3693fb6ee3Sopenharmony_ci for (const test of microTests) { 3793fb6ee3Sopenharmony_ci if (test.fragmentContext) { 3893fb6ee3Sopenharmony_ci parser.parseFragment(test.fragmentContext, test.html); 3993fb6ee3Sopenharmony_ci } else { 4093fb6ee3Sopenharmony_ci parser.parse(test.html); 4193fb6ee3Sopenharmony_ci } 4293fb6ee3Sopenharmony_ci } 4393fb6ee3Sopenharmony_ci}; 4493fb6ee3Sopenharmony_ci 4593fb6ee3Sopenharmony_ci// Pages data 4693fb6ee3Sopenharmony_ciconst pages = loadSAXParserTestData().map((test) => test.src); 4793fb6ee3Sopenharmony_ci 4893fb6ee3Sopenharmony_ciglobal.runPages = function (parser) { 4993fb6ee3Sopenharmony_ci for (const page of pages) { 5093fb6ee3Sopenharmony_ci parser.parse(page); 5193fb6ee3Sopenharmony_ci } 5293fb6ee3Sopenharmony_ci}; 5393fb6ee3Sopenharmony_ci 5493fb6ee3Sopenharmony_ci// Stream data 5593fb6ee3Sopenharmony_ciglobal.files = readdirSync(saxPath).map((dirName) => new URL(`${dirName}/src.html`, saxPath).pathname); 5693fb6ee3Sopenharmony_ci 5793fb6ee3Sopenharmony_ci// Utils 5893fb6ee3Sopenharmony_cifunction getHz(suite, testName) { 5993fb6ee3Sopenharmony_ci for (let i = 0; i < suite.length; i++) { 6093fb6ee3Sopenharmony_ci if (suite[i].name === testName) { 6193fb6ee3Sopenharmony_ci return suite[i].hz; 6293fb6ee3Sopenharmony_ci } 6393fb6ee3Sopenharmony_ci } 6493fb6ee3Sopenharmony_ci} 6593fb6ee3Sopenharmony_ci 6693fb6ee3Sopenharmony_cifunction runBench({ name, workingCopyFn, upstreamFn, defer = false }) { 6793fb6ee3Sopenharmony_ci const suite = new Benchmark.Suite(name); 6893fb6ee3Sopenharmony_ci 6993fb6ee3Sopenharmony_ci suite 7093fb6ee3Sopenharmony_ci .add('Working copy', workingCopyFn, { defer }) 7193fb6ee3Sopenharmony_ci .add('Upstream', upstreamFn, { defer }) 7293fb6ee3Sopenharmony_ci .on('start', () => console.log(name)) 7393fb6ee3Sopenharmony_ci .on('cycle', (event) => console.log(String(event.target))) 7493fb6ee3Sopenharmony_ci .on('complete', () => { 7593fb6ee3Sopenharmony_ci const workingCopyHz = getHz(suite, 'Working copy'); 7693fb6ee3Sopenharmony_ci const upstreamHz = getHz(suite, 'Upstream'); 7793fb6ee3Sopenharmony_ci 7893fb6ee3Sopenharmony_ci if (workingCopyHz > upstreamHz) { 7993fb6ee3Sopenharmony_ci console.log(`Working copy is ${(workingCopyHz / upstreamHz).toFixed(2)}x faster.\n`); 8093fb6ee3Sopenharmony_ci } else { 8193fb6ee3Sopenharmony_ci console.log(`Working copy is ${(upstreamHz / workingCopyHz).toFixed(2)}x slower.\n`); 8293fb6ee3Sopenharmony_ci } 8393fb6ee3Sopenharmony_ci }) 8493fb6ee3Sopenharmony_ci .run(); 8593fb6ee3Sopenharmony_ci} 8693fb6ee3Sopenharmony_ci 8793fb6ee3Sopenharmony_ci// Benchmarks 8893fb6ee3Sopenharmony_cirunBench({ 8993fb6ee3Sopenharmony_ci name: 'parse5 regression benchmark - MICRO', 9093fb6ee3Sopenharmony_ci workingCopyFn: () => runMicro(workingCopy), 9193fb6ee3Sopenharmony_ci upstreamFn: () => runMicro(upstreamParser), 9293fb6ee3Sopenharmony_ci}); 9393fb6ee3Sopenharmony_ci 9493fb6ee3Sopenharmony_cirunBench({ 9593fb6ee3Sopenharmony_ci name: 'parse5 regression benchmark - HUGE', 9693fb6ee3Sopenharmony_ci workingCopyFn: () => workingCopy.parse(hugePage), 9793fb6ee3Sopenharmony_ci upstreamFn: () => upstreamParser.parse(hugePage), 9893fb6ee3Sopenharmony_ci}); 9993fb6ee3Sopenharmony_ci 10093fb6ee3Sopenharmony_cirunBench({ 10193fb6ee3Sopenharmony_ci name: 'parse5 regression benchmark - PAGES', 10293fb6ee3Sopenharmony_ci workingCopyFn: () => runPages(workingCopy), 10393fb6ee3Sopenharmony_ci upstreamFn: () => runPages(upstreamParser), 10493fb6ee3Sopenharmony_ci}); 10593fb6ee3Sopenharmony_ci 10693fb6ee3Sopenharmony_cirunBench({ 10793fb6ee3Sopenharmony_ci name: 'parse5 regression benchmark - STREAM', 10893fb6ee3Sopenharmony_ci defer: true, 10993fb6ee3Sopenharmony_ci workingCopyFn: async (deferred) => { 11093fb6ee3Sopenharmony_ci const parsePromises = files.map((fileName) => { 11193fb6ee3Sopenharmony_ci const stream = createReadStream(fileName, 'utf8'); 11293fb6ee3Sopenharmony_ci const parserStream = new WorkingCopyParserStream(); 11393fb6ee3Sopenharmony_ci 11493fb6ee3Sopenharmony_ci stream.pipe(parserStream); 11593fb6ee3Sopenharmony_ci return finished(parserStream); 11693fb6ee3Sopenharmony_ci }); 11793fb6ee3Sopenharmony_ci 11893fb6ee3Sopenharmony_ci await Promise.all(parsePromises); 11993fb6ee3Sopenharmony_ci deferred.resolve(); 12093fb6ee3Sopenharmony_ci }, 12193fb6ee3Sopenharmony_ci upstreamFn: async (deferred) => { 12293fb6ee3Sopenharmony_ci const parsePromises = files.map(async (fileName) => { 12393fb6ee3Sopenharmony_ci const stream = createReadStream(fileName, 'utf8'); 12493fb6ee3Sopenharmony_ci const writable = new WritableStreamStub(); 12593fb6ee3Sopenharmony_ci 12693fb6ee3Sopenharmony_ci stream.pipe(writable); 12793fb6ee3Sopenharmony_ci 12893fb6ee3Sopenharmony_ci await finished(writable); 12993fb6ee3Sopenharmony_ci 13093fb6ee3Sopenharmony_ci upstreamParser.parse(writable.writtenData); 13193fb6ee3Sopenharmony_ci }); 13293fb6ee3Sopenharmony_ci 13393fb6ee3Sopenharmony_ci await Promise.all(parsePromises); 13493fb6ee3Sopenharmony_ci deferred.resolve(); 13593fb6ee3Sopenharmony_ci }, 13693fb6ee3Sopenharmony_ci}); 137