Files
oxc-project__oxc/napi/parser/bench.bench.js
camc314 949d7a9302 chore(deps): update vitest to v5 (#26299)
## Summary

- update workspace test dependencies to Vitest 5.0.0
- pin Vitest and related tooling to Vite 8.2.2
- migrate removed sequential and benchmark APIs
- update the pnpm lockfile
2026-09-03 19:05:05 +00:00

213 lines
7.1 KiB
JavaScript

import { writeFile } from "node:fs/promises";
import { join as pathJoin } from "node:path";
import { describe, test } from "vitest";
import { parseRawSync } from "./src-js/bindings.js";
import { parse as parseAsync, parseSync } from "./src-js/index.js";
// Internals
import { DATA_POINTER_POS_32, PROGRAM_OFFSET } from "./src-js/generated/constants.js";
import { deserialize as deserializeJS } from "./src-js/generated/deserialize/js.js";
import { deserialize as deserializeTS } from "./src-js/generated/deserialize/ts.js";
import { walkProgram } from "./src-js/generated/lazy/walk.js";
import { isJsAst, prepareRaw, returnBufferToCache } from "./src-js/raw-transfer/common.js";
import { TOKEN } from "./src-js/raw-transfer/lazy-common.js";
import { getVisitorsArr, Visitor } from "./src-js/raw-transfer/visitor.js";
// Same fixtures as used in Rust parser benchmarks
let fixtureUrls = [
"https://cdn.jsdelivr.net/gh/microsoft/TypeScript@v5.3.3/src/compiler/checker.ts",
"https://cdn.jsdelivr.net/gh/excalidraw/excalidraw@f6d85bc80fe328e8f472636eb0d541f7bb891aa0/packages/excalidraw/components/App.tsx",
"https://cdn.jsdelivr.net/gh/oxc-project/benchmark-files@cd3bc3d431452b640f5dfcabbc22a8d8a388f393/RadixUIAdoptionSection.jsx",
"https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.269/build/pdf.mjs",
"https://cdn.jsdelivr.net/npm/antd@4.16.1/dist/antd.js",
];
// For sharding in CI - specify single fixture to run benchmarks on
let testStandard = test,
testRaw = test;
let shard = process.env.SHARD;
if (shard) {
shard *= 1;
if (shard % 2 === 0) {
testRaw = test.skip;
} else {
testStandard = test.skip;
shard--;
}
fixtureUrls = [fixtureUrls[shard / 2]];
}
// Same directory as Rust benchmarks use for downloaded files
// to avoid re-downloading if Rust benchmarks already downloaded
const cacheDirPath = pathJoin(import.meta.dirname, "../../target");
// Load fixtures
const fixtures = await Promise.all(
fixtureUrls.map(async (url) => {
const filename = url.split("/").at(-1),
path = pathJoin(cacheDirPath, filename);
let code;
try {
code = await readFile(path, "utf8");
} catch {
const res = await fetch(url);
code = await res.text();
await writeFile(path, code);
}
return { filename, code };
}),
);
const defineBenchmark = (testFunction, name, benchmarkFunction) => {
testFunction(name, async ({ bench }) => {
await bench(name, benchmarkFunction).run();
});
};
// Run benchmarks
for (const { filename, code } of fixtures) {
// oxlint-disable-next-line jest/valid-title
describe(filename, () => {
defineBenchmark(testStandard, "parser_napi", () => {
const ret = parseSync(filename, code);
// Read returned object's properties to execute getters which deserialize
// oxlint-disable-next-line no-unused-vars
const { program, comments, module, errors } = ret;
});
defineBenchmark(testRaw, "parser_napi_raw", () => {
const ret = parseSync(filename, code, { experimentalRawTransfer: true });
// Read returned object's properties to execute getters
// oxlint-disable-next-line no-unused-vars
const { program, comments, module, errors } = ret;
});
defineBenchmark(testStandard, "parser_napi_async", async () => {
const ret = await parseAsync(filename, code);
// Read returned object's properties to execute getters which deserialize
// oxlint-disable-next-line no-unused-vars
const { program, comments, module, errors } = ret;
});
defineBenchmark(testRaw, "parser_napi_async_raw", async () => {
const ret = await parseAsync(filename, code, { experimentalRawTransfer: true });
// Read returned object's properties to execute getters
// oxlint-disable-next-line no-unused-vars
const { program, comments, module, errors } = ret;
});
defineBenchmark(testRaw, "parser_napi_raw_no_deser", () => {
const { buffer, sourceByteLen } = prepareRaw(code);
parseRawSync(filename, buffer, sourceByteLen, {});
returnBufferToCache(buffer);
});
// Prepare buffer but don't deserialize
const { buffer, sourceByteLen } = prepareRaw(code);
parseRawSync(filename, buffer, sourceByteLen, {});
const deserialize = isJsAst(buffer) ? deserializeJS : deserializeTS;
defineBenchmark(testRaw, "parser_napi_raw_deser_only", () => {
deserialize(buffer, code, sourceByteLen, true);
});
// oxlint-disable-next-line no-unused-vars
let debuggerCount = 0;
const debuggerVisitor = new Visitor({
DebuggerStatement(_debuggerStmt) {
debuggerCount++;
},
});
// oxlint-disable-next-line no-unused-vars
let identCount = 0;
const identVisitor = new Visitor({
BindingIdentifier(_ident) {
identCount++;
},
IdentifierReference(_ident) {
identCount++;
},
IdentifierName(_ident) {
identCount++;
},
});
// These 4 currently not working, due to 2 instances of `Visitor` getting loaded via CJS and ESM.
// TODO: Fix it.
/*
defineBenchmark(testRaw, 'parser_napi_raw_lazy_visit(debugger)', () => {
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
debuggerCount = 0;
visit(debuggerVisitor);
dispose();
});
defineBenchmark(testRaw, 'parser_napi_raw_lazy_visit(ident)', () => {
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
identCount = 0;
visit(identVisitor);
dispose();
});
defineBenchmark(testRaw, 'parser_napi_raw_lazy_visitor(debugger)', () => {
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
debuggerCount = 0;
const debuggerVisitor = new Visitor({
DebuggerStatement(_debuggerStmt) {
debuggerCount++;
},
});
visit(debuggerVisitor);
dispose();
});
defineBenchmark(testRaw, 'parser_napi_raw_lazy_visitor(ident)', () => {
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
identCount = 0;
const identVisitor = new Visitor({
BindingIdentifier(_ident) {
identCount++;
},
IdentifierReference(_ident) {
identCount++;
},
IdentifierName(_ident) {
identCount++;
},
});
visit(identVisitor);
dispose();
});
*/
const debuggerVisitorsArr = getVisitorsArr(debuggerVisitor);
const identVisitorsArr = getVisitorsArr(identVisitor);
const ast = {
buffer,
sourceText: code,
sourceByteLen,
sourceIsAscii: code.length === sourceByteLen,
nodes: null, // Initialized in bench functions
token: TOKEN,
};
const programPos = buffer.int32[DATA_POINTER_POS_32] + PROGRAM_OFFSET;
defineBenchmark(testRaw, "parser_napi_raw_lazy_visit_only(debugger)", () => {
ast.nodes = new Map();
debuggerCount = 0;
walkProgram(programPos, ast, debuggerVisitorsArr);
});
defineBenchmark(testRaw, "parser_napi_raw_lazy_visit_only(ident)", () => {
ast.nodes = new Map();
identCount = 0;
walkProgram(programPos, ast, identVisitorsArr);
});
});
}