Skip to content

Instantly share code, notes, and snippets.

@mizchi
Last active June 28, 2023 13:32
Show Gist options
  • Save mizchi/25619126dac28223998e5dbc6603f05c to your computer and use it in GitHub Desktop.
Save mizchi/25619126dac28223998e5dbc6603f05c to your computer and use it in GitHub Desktop.
/*
# Benchmark for typescript compiler API - parse/print/transform
CAUTION: JIT is not taken into account
I use https://github.com/mizchi/svelte2tsx-component/blob/main/src/core.mts
---
code-size 43377chars 5336nodes
Run 100 times
(createSourceFile)
parse: 193.245ms
(visit all nodes)
full-traverse: 17.935ms
(noop transformer)
noop-transformer: 30.529ms
(visit many identifiers 2221/5336 41%)
full-transformer: 122.425ms
(printer.printFile(sourceFile))
print-file: 199.499ms
(ts-clone-node)
clone-node: 1.049s
Conclusion:
1 parse ≒ 1.2 printFile
1 * parse ≒ 10 * full-traverse
1 * parse ≒ 0.33 * (transformed-nodes/all-nodes)
1 * full-traverse ≒ 0.5 * one-transform
1 * clone-node ≒ 6 * parse
*/
// npx tsm bench.ts
import ts from "typescript";
// @ts-ignore
import fs from "node:fs";
import { cloneNode } from "ts-clone-node";
const __dirname = new URL(".", import.meta.url).pathname;
const code = fs.readFileSync(__dirname + "/src/core.mts", "utf8");
const sourceFile = ts.createSourceFile("test.ts", code, ts.ScriptTarget.ESNext);
let nodeCount = 0;
const visit = (node: ts.Node) => {
nodeCount++;
ts.forEachChild(node, visit);
};
visit(sourceFile);
console.log("code-size", code.length + "chars", nodeCount + "nodes");
console.time("parse");
for (let i = 0; i < 100; i++) {
ts.createSourceFile("test.ts", code, ts.ScriptTarget.ESNext);
}
console.timeEnd("parse");
// create once
console.time("full-traverse");
for (let i = 0; i < 100; i++) {
const visit = (node: ts.Node) => {
ts.forEachChild(node, visit);
};
ts.forEachChild(sourceFile, visit);
}
console.timeEnd("full-traverse");
// no transforme
const transformer: ts.TransformerFactory<any> = (context: ts.TransformationContext) => (root) => {
const visit = (node: ts.Node): ts.Node => {
return ts.visitEachChild(node, visit, context);
};
return ts.visitNode(root, visit);
};
console.time("noop-transformer");
for (let i = 0; i < 100; i++) {
ts.transform(sourceFile, [transformer]);
}
console.timeEnd("noop-transformer");
// let mutableNodeCount = 0;
const transformer2: ts.TransformerFactory<any> = (context: ts.TransformationContext) => (root) => {
const visit = (node: ts.Node): ts.Node => {
if (ts.isIdentifier(node)) {
// mutableNodeCount++;
// const random = Math.random().toString(36).substring(7);
const newName = node.text + "_new";
return context.factory.createIdentifier(newName);
}
return ts.visitEachChild(node, visit, context);
};
return ts.visitNode(root, visit);
};
// ts.transform(sourceFile, [transformer2]);
// console.log("transformed-node-count", mutableNodeCount);
// let mutableNodeCount = 0;
const transformer3: ts.TransformerFactory<any> = (context: ts.TransformationContext) => (root) => {
const visit = (node: ts.Node): ts.Node => {
if (ts.isIdentifier(node) && node.text === "isLastNode") {
return context.factory.createIdentifier("xxx");
}
return ts.visitEachChild(node, visit, context);
};
return ts.visitNode(root, visit);
};
// ts.transform(sourceFile, [transformer2]);
// console.log("transformed-node-count", mutableNodeCount);
console.time("full-transformer");
for (let i = 0; i < 100; i++) {
ts.transform(sourceFile, [transformer2]);
}
console.timeEnd("full-transformer");
console.time("one-transformer");
for (let i = 0; i < 100; i++) {
ts.transform(sourceFile, [transformer3]);
}
console.timeEnd("one-transformer");
const printer = ts.createPrinter();
console.time("print-file");
for (let i = 0; i < 100; i++) {
printer.printFile(sourceFile);
}
console.timeEnd("print-file");
console.time("clone-node");
for (let i = 0; i < 100; i++) {
cloneNode(sourceFile);
}
console.timeEnd("clone-node");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment