overtake 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.cjs +2 -1
- package/build/cli.cjs.map +1 -1
- package/build/cli.js +2 -1
- package/build/cli.js.map +1 -1
- package/build/executor.cjs +7 -3
- package/build/executor.cjs.map +1 -1
- package/build/executor.d.ts +1 -2
- package/build/executor.js +7 -3
- package/build/executor.js.map +1 -1
- package/build/gc-watcher.cjs +5 -6
- package/build/gc-watcher.cjs.map +1 -1
- package/build/gc-watcher.d.ts +0 -1
- package/build/gc-watcher.js +5 -6
- package/build/gc-watcher.js.map +1 -1
- package/build/index.cjs +6 -9
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.js +6 -9
- package/build/index.js.map +1 -1
- package/build/runner.cjs +266 -65
- package/build/runner.cjs.map +1 -1
- package/build/runner.js +266 -65
- package/build/runner.js.map +1 -1
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +2 -4
- package/build/types.js.map +1 -1
- package/build/worker.cjs +65 -33
- package/build/worker.cjs.map +1 -1
- package/build/worker.js +66 -34
- package/build/worker.js.map +1 -1
- package/examples/accuracy.ts +30 -5
- package/package.json +4 -4
- package/src/cli.ts +3 -2
- package/src/executor.ts +9 -14
- package/src/gc-watcher.ts +5 -6
- package/src/index.ts +15 -20
- package/src/runner.ts +325 -72
- package/src/types.ts +2 -4
- package/src/worker.ts +74 -38
package/build/worker.cjs
CHANGED
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
const _nodeworker_threads = require("node:worker_threads");
|
|
6
6
|
const _nodevm = require("node:vm");
|
|
7
7
|
const _nodemodule = require("node:module");
|
|
8
|
+
const _nodepath = require("node:path");
|
|
8
9
|
const _nodeurl = require("node:url");
|
|
9
10
|
const _runnercjs = require("./runner.cjs");
|
|
10
11
|
function _getRequireWildcardCache(nodeInterop) {
|
|
@@ -48,17 +49,22 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
48
49
|
}
|
|
49
50
|
return newObj;
|
|
50
51
|
}
|
|
51
|
-
const {
|
|
52
|
-
const serialize = (code)=>code ? code : '
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
const { benchmarkUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = _nodeworker_threads.workerData;
|
|
53
|
+
const serialize = (code)=>code ? code : 'undefined';
|
|
54
|
+
const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : (0, _nodeurl.pathToFileURL)(process.cwd()).href;
|
|
55
|
+
const benchmarkDirUrl = new URL('.', resolvedBenchmarkUrl).href;
|
|
56
|
+
const requireFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(new URL('benchmark.js', benchmarkDirUrl)));
|
|
57
|
+
const resolveSpecifier = (specifier)=>{
|
|
58
|
+
if (specifier.startsWith('file:')) {
|
|
59
|
+
return specifier;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
if (specifier.startsWith('./') || specifier.startsWith('../')) {
|
|
62
|
+
return new URL(specifier, benchmarkDirUrl).href;
|
|
63
|
+
}
|
|
64
|
+
if ((0, _nodepath.isAbsolute)(specifier)) {
|
|
65
|
+
return (0, _nodeurl.pathToFileURL)(specifier).href;
|
|
66
|
+
}
|
|
67
|
+
return requireFrom.resolve(specifier);
|
|
62
68
|
};
|
|
63
69
|
const source = `
|
|
64
70
|
export const setup = ${serialize(setupCode)};
|
|
@@ -72,41 +78,67 @@ const context = (0, _nodevm.createContext)({
|
|
|
72
78
|
Buffer
|
|
73
79
|
});
|
|
74
80
|
const imports = new Map();
|
|
81
|
+
const createSyntheticModule = (moduleExports, exportNames, identifier)=>{
|
|
82
|
+
const mod = new _nodevm.SyntheticModule(exportNames, ()=>{
|
|
83
|
+
for (const name of exportNames){
|
|
84
|
+
if (name === 'default') {
|
|
85
|
+
mod.setExport(name, moduleExports);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
mod.setExport(name, moduleExports[name]);
|
|
89
|
+
}
|
|
90
|
+
}, {
|
|
91
|
+
identifier,
|
|
92
|
+
context
|
|
93
|
+
});
|
|
94
|
+
return mod;
|
|
95
|
+
};
|
|
96
|
+
const isCjsModule = (target)=>target.endsWith('.cjs') || target.endsWith('.cts');
|
|
97
|
+
const toRequireTarget = (target)=>target.startsWith('file:') ? (0, _nodeurl.fileURLToPath)(target) : target;
|
|
98
|
+
const loadModule = async (target)=>{
|
|
99
|
+
const cached = imports.get(target);
|
|
100
|
+
if (cached) return cached;
|
|
101
|
+
if (isCjsModule(target)) {
|
|
102
|
+
const required = requireFrom(toRequireTarget(target));
|
|
103
|
+
const exportNames = required && (typeof required === 'object' || typeof required === 'function') ? Object.keys(required) : [];
|
|
104
|
+
if (!exportNames.includes('default')) {
|
|
105
|
+
exportNames.push('default');
|
|
106
|
+
}
|
|
107
|
+
const mod = createSyntheticModule(required, exportNames, target);
|
|
108
|
+
imports.set(target, mod);
|
|
109
|
+
return mod;
|
|
110
|
+
}
|
|
111
|
+
const importedModule = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
112
|
+
const exportNames = Object.keys(importedModule);
|
|
113
|
+
const mod = createSyntheticModule(importedModule, exportNames, target);
|
|
114
|
+
imports.set(target, mod);
|
|
115
|
+
return mod;
|
|
116
|
+
};
|
|
117
|
+
const loadDynamicModule = async (target)=>{
|
|
118
|
+
const mod = await loadModule(target);
|
|
119
|
+
if (mod.status !== 'evaluated') {
|
|
120
|
+
await mod.evaluate();
|
|
121
|
+
}
|
|
122
|
+
return mod;
|
|
123
|
+
};
|
|
75
124
|
const mod = new _nodevm.SourceTextModule(source, {
|
|
76
|
-
identifier:
|
|
125
|
+
identifier: resolvedBenchmarkUrl,
|
|
77
126
|
context,
|
|
78
127
|
initializeImportMeta (meta) {
|
|
79
|
-
meta.url =
|
|
128
|
+
meta.url = resolvedBenchmarkUrl;
|
|
80
129
|
},
|
|
81
|
-
importModuleDynamically (specifier
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
return Promise.resolve(resolved).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
130
|
+
importModuleDynamically (specifier) {
|
|
131
|
+
const resolved = resolveSpecifier(specifier);
|
|
132
|
+
return loadDynamicModule(resolved);
|
|
85
133
|
}
|
|
86
134
|
});
|
|
87
|
-
await mod.link(async (specifier
|
|
88
|
-
const base = referencingModule.identifier ?? baseUrl;
|
|
89
|
-
const target = resolveSpecifier(specifier, base);
|
|
90
|
-
const cached = imports.get(target);
|
|
91
|
-
if (cached) return cached;
|
|
92
|
-
const importedModule = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
93
|
-
const exportNames = Object.keys(importedModule);
|
|
94
|
-
const imported = new _nodevm.SyntheticModule(exportNames, ()=>{
|
|
95
|
-
exportNames.forEach((key)=>imported.setExport(key, importedModule[key]));
|
|
96
|
-
}, {
|
|
97
|
-
identifier: target,
|
|
98
|
-
context: referencingModule.context
|
|
99
|
-
});
|
|
100
|
-
imports.set(target, imported);
|
|
101
|
-
return imported;
|
|
102
|
-
});
|
|
135
|
+
await mod.link(async (specifier)=>loadModule(resolveSpecifier(specifier)));
|
|
103
136
|
await mod.evaluate();
|
|
104
137
|
const { setup, teardown, pre, run, post } = mod.namespace;
|
|
105
138
|
if (!run) {
|
|
106
139
|
throw new Error('Benchmark run function is required');
|
|
107
140
|
}
|
|
108
141
|
process.exitCode = await (0, _runnercjs.benchmark)({
|
|
109
|
-
baseUrl,
|
|
110
142
|
setup,
|
|
111
143
|
teardown,
|
|
112
144
|
pre,
|
package/build/worker.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { SourceTextModule, SyntheticModule, createContext } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\nimport { benchmark } from './runner.js';\nimport { WorkerOptions } from './types.js';\n\nconst {\n
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { SourceTextModule, SyntheticModule, createContext } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { isAbsolute } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { benchmark } from './runner.js';\nimport { WorkerOptions } from './types.js';\n\nconst {\n benchmarkUrl,\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = true,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst serialize = (code?: string) => (code ? code : 'undefined');\n\nconst resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\nconst benchmarkDirUrl = new URL('.', resolvedBenchmarkUrl).href;\nconst requireFrom = createRequire(fileURLToPath(new URL('benchmark.js', benchmarkDirUrl)));\n\nconst resolveSpecifier = (specifier: string) => {\n if (specifier.startsWith('file:')) {\n return specifier;\n }\n if (specifier.startsWith('./') || specifier.startsWith('../')) {\n return new URL(specifier, benchmarkDirUrl).href;\n }\n if (isAbsolute(specifier)) {\n return pathToFileURL(specifier).href;\n }\n return requireFrom.resolve(specifier);\n};\n\nconst source = `\nexport const setup = ${serialize(setupCode)};\nexport const teardown = ${serialize(teardownCode)};\nexport const pre = ${serialize(preCode)};\nexport const run = ${serialize(runCode)};\nexport const post = ${serialize(postCode)};\n `;\n\nconst context = createContext({ console, Buffer });\nconst imports = new Map<string, SyntheticModule>();\n\nconst createSyntheticModule = (moduleExports: unknown, exportNames: string[], identifier: string) => {\n const mod = new SyntheticModule(\n exportNames,\n () => {\n for (const name of exportNames) {\n if (name === 'default') {\n mod.setExport(name, moduleExports);\n continue;\n }\n mod.setExport(name, (moduleExports as Record<string, unknown>)[name]);\n }\n },\n { identifier, context },\n );\n return mod;\n};\n\nconst isCjsModule = (target: string) => target.endsWith('.cjs') || target.endsWith('.cts');\n\nconst toRequireTarget = (target: string) => (target.startsWith('file:') ? fileURLToPath(target) : target);\n\nconst loadModule = async (target: string) => {\n const cached = imports.get(target);\n if (cached) return cached;\n\n if (isCjsModule(target)) {\n const required = requireFrom(toRequireTarget(target));\n const exportNames = required && (typeof required === 'object' || typeof required === 'function') ? Object.keys(required) : [];\n if (!exportNames.includes('default')) {\n exportNames.push('default');\n }\n const mod = createSyntheticModule(required, exportNames, target);\n imports.set(target, mod);\n return mod;\n }\n\n const importedModule = await import(target);\n const exportNames = Object.keys(importedModule);\n const mod = createSyntheticModule(importedModule, exportNames, target);\n imports.set(target, mod);\n return mod;\n};\n\nconst loadDynamicModule = async (target: string) => {\n const mod = await loadModule(target);\n if (mod.status !== 'evaluated') {\n await mod.evaluate();\n }\n return mod;\n};\nconst mod = new SourceTextModule(source, {\n identifier: resolvedBenchmarkUrl,\n context,\n initializeImportMeta(meta) {\n meta.url = resolvedBenchmarkUrl;\n },\n importModuleDynamically(specifier) {\n const resolved = resolveSpecifier(specifier);\n return loadDynamicModule(resolved);\n },\n});\n\nawait mod.link(async (specifier) => loadModule(resolveSpecifier(specifier)));\n\nawait mod.evaluate();\nconst { setup, teardown, pre, run, post } = mod.namespace as any;\n\nif (!run) {\n throw new Error('Benchmark run function is required');\n}\n\nprocess.exitCode = await benchmark({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n\n durationsSAB,\n controlSAB,\n});\n"],"names":["benchmarkUrl","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","gcObserver","durationsSAB","controlSAB","workerData","serialize","code","resolvedBenchmarkUrl","pathToFileURL","process","cwd","href","benchmarkDirUrl","URL","requireFrom","createRequire","fileURLToPath","resolveSpecifier","specifier","startsWith","isAbsolute","resolve","source","context","createContext","console","Buffer","imports","Map","createSyntheticModule","moduleExports","exportNames","identifier","mod","SyntheticModule","name","setExport","isCjsModule","target","endsWith","toRequireTarget","loadModule","cached","get","required","Object","keys","includes","push","set","importedModule","loadDynamicModule","status","evaluate","SourceTextModule","initializeImportMeta","meta","url","importModuleDynamically","resolved","link","setup","teardown","pre","run","post","namespace","Error","exitCode","benchmark"],"mappings":"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"}
|
package/build/worker.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { workerData } from 'node:worker_threads';
|
|
2
2
|
import { SourceTextModule, SyntheticModule, createContext } from 'node:vm';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
|
-
import {
|
|
4
|
+
import { isAbsolute } from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
6
|
import { benchmark } from "./runner.js";
|
|
6
|
-
const {
|
|
7
|
-
const serialize = (code)=>code ? code : '
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const { benchmarkUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = workerData;
|
|
8
|
+
const serialize = (code)=>code ? code : 'undefined';
|
|
9
|
+
const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;
|
|
10
|
+
const benchmarkDirUrl = new URL('.', resolvedBenchmarkUrl).href;
|
|
11
|
+
const requireFrom = createRequire(fileURLToPath(new URL('benchmark.js', benchmarkDirUrl)));
|
|
12
|
+
const resolveSpecifier = (specifier)=>{
|
|
13
|
+
if (specifier.startsWith('file:')) {
|
|
14
|
+
return specifier;
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
if (specifier.startsWith('./') || specifier.startsWith('../')) {
|
|
17
|
+
return new URL(specifier, benchmarkDirUrl).href;
|
|
18
|
+
}
|
|
19
|
+
if (isAbsolute(specifier)) {
|
|
20
|
+
return pathToFileURL(specifier).href;
|
|
21
|
+
}
|
|
22
|
+
return requireFrom.resolve(specifier);
|
|
17
23
|
};
|
|
18
24
|
const source = `
|
|
19
25
|
export const setup = ${serialize(setupCode)};
|
|
@@ -27,41 +33,67 @@ const context = createContext({
|
|
|
27
33
|
Buffer
|
|
28
34
|
});
|
|
29
35
|
const imports = new Map();
|
|
36
|
+
const createSyntheticModule = (moduleExports, exportNames, identifier)=>{
|
|
37
|
+
const mod = new SyntheticModule(exportNames, ()=>{
|
|
38
|
+
for (const name of exportNames){
|
|
39
|
+
if (name === 'default') {
|
|
40
|
+
mod.setExport(name, moduleExports);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
mod.setExport(name, moduleExports[name]);
|
|
44
|
+
}
|
|
45
|
+
}, {
|
|
46
|
+
identifier,
|
|
47
|
+
context
|
|
48
|
+
});
|
|
49
|
+
return mod;
|
|
50
|
+
};
|
|
51
|
+
const isCjsModule = (target)=>target.endsWith('.cjs') || target.endsWith('.cts');
|
|
52
|
+
const toRequireTarget = (target)=>target.startsWith('file:') ? fileURLToPath(target) : target;
|
|
53
|
+
const loadModule = async (target)=>{
|
|
54
|
+
const cached = imports.get(target);
|
|
55
|
+
if (cached) return cached;
|
|
56
|
+
if (isCjsModule(target)) {
|
|
57
|
+
const required = requireFrom(toRequireTarget(target));
|
|
58
|
+
const exportNames = required && (typeof required === 'object' || typeof required === 'function') ? Object.keys(required) : [];
|
|
59
|
+
if (!exportNames.includes('default')) {
|
|
60
|
+
exportNames.push('default');
|
|
61
|
+
}
|
|
62
|
+
const mod = createSyntheticModule(required, exportNames, target);
|
|
63
|
+
imports.set(target, mod);
|
|
64
|
+
return mod;
|
|
65
|
+
}
|
|
66
|
+
const importedModule = await import(target);
|
|
67
|
+
const exportNames = Object.keys(importedModule);
|
|
68
|
+
const mod = createSyntheticModule(importedModule, exportNames, target);
|
|
69
|
+
imports.set(target, mod);
|
|
70
|
+
return mod;
|
|
71
|
+
};
|
|
72
|
+
const loadDynamicModule = async (target)=>{
|
|
73
|
+
const mod = await loadModule(target);
|
|
74
|
+
if (mod.status !== 'evaluated') {
|
|
75
|
+
await mod.evaluate();
|
|
76
|
+
}
|
|
77
|
+
return mod;
|
|
78
|
+
};
|
|
30
79
|
const mod = new SourceTextModule(source, {
|
|
31
|
-
identifier:
|
|
80
|
+
identifier: resolvedBenchmarkUrl,
|
|
32
81
|
context,
|
|
33
82
|
initializeImportMeta (meta) {
|
|
34
|
-
meta.url =
|
|
83
|
+
meta.url = resolvedBenchmarkUrl;
|
|
35
84
|
},
|
|
36
|
-
importModuleDynamically (specifier
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return import(resolved);
|
|
85
|
+
importModuleDynamically (specifier) {
|
|
86
|
+
const resolved = resolveSpecifier(specifier);
|
|
87
|
+
return loadDynamicModule(resolved);
|
|
40
88
|
}
|
|
41
89
|
});
|
|
42
|
-
await mod.link(async (specifier
|
|
43
|
-
const base = referencingModule.identifier ?? baseUrl;
|
|
44
|
-
const target = resolveSpecifier(specifier, base);
|
|
45
|
-
const cached = imports.get(target);
|
|
46
|
-
if (cached) return cached;
|
|
47
|
-
const importedModule = await import(target);
|
|
48
|
-
const exportNames = Object.keys(importedModule);
|
|
49
|
-
const imported = new SyntheticModule(exportNames, ()=>{
|
|
50
|
-
exportNames.forEach((key)=>imported.setExport(key, importedModule[key]));
|
|
51
|
-
}, {
|
|
52
|
-
identifier: target,
|
|
53
|
-
context: referencingModule.context
|
|
54
|
-
});
|
|
55
|
-
imports.set(target, imported);
|
|
56
|
-
return imported;
|
|
57
|
-
});
|
|
90
|
+
await mod.link(async (specifier)=>loadModule(resolveSpecifier(specifier)));
|
|
58
91
|
await mod.evaluate();
|
|
59
92
|
const { setup, teardown, pre, run, post } = mod.namespace;
|
|
60
93
|
if (!run) {
|
|
61
94
|
throw new Error('Benchmark run function is required');
|
|
62
95
|
}
|
|
63
96
|
process.exitCode = await benchmark({
|
|
64
|
-
baseUrl,
|
|
65
97
|
setup,
|
|
66
98
|
teardown,
|
|
67
99
|
pre,
|
package/build/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { SourceTextModule, SyntheticModule, createContext } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\nimport { benchmark } from './runner.js';\nimport { WorkerOptions } from './types.js';\n\nconst {\n
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { SourceTextModule, SyntheticModule, createContext } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { isAbsolute } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { benchmark } from './runner.js';\nimport { WorkerOptions } from './types.js';\n\nconst {\n benchmarkUrl,\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = true,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst serialize = (code?: string) => (code ? code : 'undefined');\n\nconst resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\nconst benchmarkDirUrl = new URL('.', resolvedBenchmarkUrl).href;\nconst requireFrom = createRequire(fileURLToPath(new URL('benchmark.js', benchmarkDirUrl)));\n\nconst resolveSpecifier = (specifier: string) => {\n if (specifier.startsWith('file:')) {\n return specifier;\n }\n if (specifier.startsWith('./') || specifier.startsWith('../')) {\n return new URL(specifier, benchmarkDirUrl).href;\n }\n if (isAbsolute(specifier)) {\n return pathToFileURL(specifier).href;\n }\n return requireFrom.resolve(specifier);\n};\n\nconst source = `\nexport const setup = ${serialize(setupCode)};\nexport const teardown = ${serialize(teardownCode)};\nexport const pre = ${serialize(preCode)};\nexport const run = ${serialize(runCode)};\nexport const post = ${serialize(postCode)};\n `;\n\nconst context = createContext({ console, Buffer });\nconst imports = new Map<string, SyntheticModule>();\n\nconst createSyntheticModule = (moduleExports: unknown, exportNames: string[], identifier: string) => {\n const mod = new SyntheticModule(\n exportNames,\n () => {\n for (const name of exportNames) {\n if (name === 'default') {\n mod.setExport(name, moduleExports);\n continue;\n }\n mod.setExport(name, (moduleExports as Record<string, unknown>)[name]);\n }\n },\n { identifier, context },\n );\n return mod;\n};\n\nconst isCjsModule = (target: string) => target.endsWith('.cjs') || target.endsWith('.cts');\n\nconst toRequireTarget = (target: string) => (target.startsWith('file:') ? fileURLToPath(target) : target);\n\nconst loadModule = async (target: string) => {\n const cached = imports.get(target);\n if (cached) return cached;\n\n if (isCjsModule(target)) {\n const required = requireFrom(toRequireTarget(target));\n const exportNames = required && (typeof required === 'object' || typeof required === 'function') ? Object.keys(required) : [];\n if (!exportNames.includes('default')) {\n exportNames.push('default');\n }\n const mod = createSyntheticModule(required, exportNames, target);\n imports.set(target, mod);\n return mod;\n }\n\n const importedModule = await import(target);\n const exportNames = Object.keys(importedModule);\n const mod = createSyntheticModule(importedModule, exportNames, target);\n imports.set(target, mod);\n return mod;\n};\n\nconst loadDynamicModule = async (target: string) => {\n const mod = await loadModule(target);\n if (mod.status !== 'evaluated') {\n await mod.evaluate();\n }\n return mod;\n};\nconst mod = new SourceTextModule(source, {\n identifier: resolvedBenchmarkUrl,\n context,\n initializeImportMeta(meta) {\n meta.url = resolvedBenchmarkUrl;\n },\n importModuleDynamically(specifier) {\n const resolved = resolveSpecifier(specifier);\n return loadDynamicModule(resolved);\n },\n});\n\nawait mod.link(async (specifier) => loadModule(resolveSpecifier(specifier)));\n\nawait mod.evaluate();\nconst { setup, teardown, pre, run, post } = mod.namespace as any;\n\nif (!run) {\n throw new Error('Benchmark run function is required');\n}\n\nprocess.exitCode = await benchmark({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n\n durationsSAB,\n controlSAB,\n});\n"],"names":["workerData","SourceTextModule","SyntheticModule","createContext","createRequire","isAbsolute","fileURLToPath","pathToFileURL","benchmark","benchmarkUrl","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","gcObserver","durationsSAB","controlSAB","serialize","code","resolvedBenchmarkUrl","process","cwd","href","benchmarkDirUrl","URL","requireFrom","resolveSpecifier","specifier","startsWith","resolve","source","context","console","Buffer","imports","Map","createSyntheticModule","moduleExports","exportNames","identifier","mod","name","setExport","isCjsModule","target","endsWith","toRequireTarget","loadModule","cached","get","required","Object","keys","includes","push","set","importedModule","loadDynamicModule","status","evaluate","initializeImportMeta","meta","url","importModuleDynamically","resolved","link","setup","teardown","pre","run","post","namespace","Error","exitCode"],"mappings":"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"}
|
package/examples/accuracy.ts
CHANGED
|
@@ -6,10 +6,11 @@ const baseline = benchmark('accuracy tuning feed', () => {
|
|
|
6
6
|
for (let i = 0; i < uints.length; i++) {
|
|
7
7
|
uints[i] = (i * 31) ^ 0x9e3779b1;
|
|
8
8
|
}
|
|
9
|
+
const tiny = Buffer.alloc(4_096, 3);
|
|
9
10
|
const src = Buffer.alloc(bytes, 7);
|
|
10
11
|
const dst = Buffer.allocUnsafe(bytes);
|
|
11
12
|
|
|
12
|
-
return { uints, src, dst };
|
|
13
|
+
return { uints, tiny, src, dst };
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
baseline
|
|
@@ -17,10 +18,10 @@ baseline
|
|
|
17
18
|
return { scratch: 0 };
|
|
18
19
|
})
|
|
19
20
|
.measure('sum uint32 array', (ctx, { uints }) => {
|
|
20
|
-
let acc = ctx.scratch;
|
|
21
|
+
let acc = ctx.scratch | 0;
|
|
21
22
|
for (let round = 0; round < 8; round++) {
|
|
22
23
|
for (let i = 0; i < uints.length; i++) {
|
|
23
|
-
acc
|
|
24
|
+
acc = (acc + uints[i]) | 0;
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
ctx.scratch = acc;
|
|
@@ -45,10 +46,34 @@ baseline
|
|
|
45
46
|
dst.fill(0);
|
|
46
47
|
});
|
|
47
48
|
|
|
48
|
-
baseline.target('steady loop baseline').measure('counter increment', () => {
|
|
49
|
+
baseline.target('steady loop baseline').measure('counter increment', (_, { uints }) => {
|
|
49
50
|
let x = 0;
|
|
50
51
|
for (let i = 0; i < 200_000; i++) {
|
|
51
|
-
x
|
|
52
|
+
x = (x + uints[i & (uints.length - 1)]) | 0;
|
|
53
|
+
}
|
|
54
|
+
return x;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const gcImpact = baseline.target('gc impact', () => {
|
|
58
|
+
const pool: Buffer[] = Array.from({ length: 512 }, () => Buffer.alloc(4_096));
|
|
59
|
+
return { pool };
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
gcImpact.measure('alloc churn', (_, { tiny }) => {
|
|
63
|
+
let x = 0;
|
|
64
|
+
for (let i = 0; i < 512; i++) {
|
|
65
|
+
const buf = Buffer.from(tiny);
|
|
66
|
+
x ^= buf[0];
|
|
67
|
+
}
|
|
68
|
+
return x;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
gcImpact.measure('pool reuse', (ctx, { tiny }) => {
|
|
72
|
+
let x = 0;
|
|
73
|
+
const { pool } = ctx;
|
|
74
|
+
for (let i = 0; i < pool.length; i++) {
|
|
75
|
+
pool[i].set(tiny);
|
|
76
|
+
x ^= pool[i][0];
|
|
52
77
|
}
|
|
53
78
|
return x;
|
|
54
79
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtake",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "NodeJS performance benchmark",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -47,15 +47,15 @@
|
|
|
47
47
|
"@types/jest": "^30.0.0",
|
|
48
48
|
"@types/node": "^25.0.3",
|
|
49
49
|
"husky": "^9.1.7",
|
|
50
|
-
"inop": "^0.8.
|
|
50
|
+
"inop": "^0.8.10",
|
|
51
51
|
"jest": "^30.2.0",
|
|
52
|
-
"overtake": "^1.1.
|
|
52
|
+
"overtake": "^1.1.1",
|
|
53
53
|
"prettier": "^3.7.4",
|
|
54
54
|
"pretty-quick": "^4.2.2",
|
|
55
55
|
"typescript": "^5.9.3"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@swc/core": "^1.15.
|
|
58
|
+
"@swc/core": "^1.15.8",
|
|
59
59
|
"async": "^3.2.6",
|
|
60
60
|
"commander": "^14.0.2",
|
|
61
61
|
"glob": "^13.0.0"
|
package/src/cli.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { REPORT_TYPES } from './types.js';
|
|
|
10
10
|
|
|
11
11
|
const require = createRequire(import.meta.url);
|
|
12
12
|
const { name, description, version } = require('../package.json');
|
|
13
|
+
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
13
14
|
|
|
14
15
|
const commander = new Command();
|
|
15
16
|
|
|
@@ -97,8 +98,8 @@ commander
|
|
|
97
98
|
if (instance) {
|
|
98
99
|
const reports = await instance.execute({
|
|
99
100
|
...executeOptions,
|
|
100
|
-
|
|
101
|
-
});
|
|
101
|
+
[BENCHMARK_URL]: identifier,
|
|
102
|
+
} as typeof executeOptions);
|
|
102
103
|
switch (executeOptions.format) {
|
|
103
104
|
case 'json':
|
|
104
105
|
{
|
package/src/executor.ts
CHANGED
|
@@ -9,23 +9,18 @@ import { RunOptions, ReportOptions, WorkerOptions, BenchmarkOptions, Control, Re
|
|
|
9
9
|
export type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & { count: number };
|
|
10
10
|
|
|
11
11
|
export interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {
|
|
12
|
-
baseUrl?: string;
|
|
13
12
|
workers?: number;
|
|
14
13
|
maxCycles?: number;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
warmupCycles,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
gcObserver = true,
|
|
26
|
-
reportTypes,
|
|
27
|
-
}: Required<ExecutorOptions<R>>) => {
|
|
28
|
-
const executor = queue<RunOptions<TContext, TInput>>(async ({ baseUrl: runBaseUrl = baseUrl, setup, teardown, pre, run, post, data }) => {
|
|
16
|
+
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
17
|
+
|
|
18
|
+
export const createExecutor = <TContext, TInput, R extends ReportTypeList>(options: Required<ExecutorOptions<R>>) => {
|
|
19
|
+
const { workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes } = options;
|
|
20
|
+
const benchmarkUrl = (options as Record<symbol, unknown>)[BENCHMARK_URL];
|
|
21
|
+
const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;
|
|
22
|
+
|
|
23
|
+
const executor = queue<RunOptions<TContext, TInput>>(async ({ setup, teardown, pre, run, post, data }) => {
|
|
29
24
|
const setupCode = setup?.toString();
|
|
30
25
|
const teardownCode = teardown?.toString();
|
|
31
26
|
const preCode = pre?.toString();
|
|
@@ -37,7 +32,7 @@ export const createExecutor = <TContext, TInput, R extends ReportTypeList>({
|
|
|
37
32
|
|
|
38
33
|
const workerFile = new URL('./worker.js', import.meta.url);
|
|
39
34
|
const workerData: WorkerOptions = {
|
|
40
|
-
|
|
35
|
+
benchmarkUrl: resolvedBenchmarkUrl,
|
|
41
36
|
setupCode,
|
|
42
37
|
teardownCode,
|
|
43
38
|
preCode,
|
package/src/gc-watcher.ts
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
export interface GCMarker {
|
|
2
2
|
ref: WeakRef<object>;
|
|
3
|
-
token: object;
|
|
4
3
|
}
|
|
5
4
|
|
|
6
5
|
export class GCWatcher {
|
|
7
6
|
#registry = new FinalizationRegistry(() => {});
|
|
8
7
|
|
|
9
8
|
start(): GCMarker {
|
|
10
|
-
const
|
|
11
|
-
const ref = new WeakRef(
|
|
12
|
-
this.#registry.register(
|
|
13
|
-
return { ref
|
|
9
|
+
const target = {};
|
|
10
|
+
const ref = new WeakRef(target);
|
|
11
|
+
this.#registry.register(target, null, ref);
|
|
12
|
+
return { ref };
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
seen(marker: GCMarker): boolean {
|
|
17
16
|
const collected = marker.ref.deref() === undefined;
|
|
18
17
|
if (!collected) {
|
|
19
|
-
this.#registry.unregister(marker.
|
|
18
|
+
this.#registry.unregister(marker.ref);
|
|
20
19
|
}
|
|
21
20
|
return collected;
|
|
22
21
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { cpus } from 'node:os';
|
|
2
|
-
import { pathToFileURL } from 'node:url';
|
|
3
2
|
import { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';
|
|
4
3
|
import { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';
|
|
5
4
|
|
|
@@ -10,6 +9,7 @@ declare global {
|
|
|
10
9
|
export const DEFAULT_WORKERS = cpus().length;
|
|
11
10
|
|
|
12
11
|
export const AsyncFunction = (async () => {}).constructor;
|
|
12
|
+
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
13
13
|
|
|
14
14
|
export interface TargetReport<R extends ReportTypeList> {
|
|
15
15
|
target: string;
|
|
@@ -135,29 +135,24 @@ export class Benchmark<TInput> {
|
|
|
135
135
|
return new Target<TContext, TInput>(target);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
138
|
+
async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>(options: ExecutorOptions<R>): Promise<TargetReport<R>[]> {
|
|
139
|
+
const {
|
|
140
|
+
workers = DEFAULT_WORKERS,
|
|
141
|
+
warmupCycles = 20,
|
|
142
|
+
maxCycles = DEFAULT_CYCLES,
|
|
143
|
+
minCycles = 50,
|
|
144
|
+
absThreshold = 1_000,
|
|
145
|
+
relThreshold = 0.02,
|
|
146
|
+
gcObserver = true,
|
|
147
|
+
reportTypes = DEFAULT_REPORT_TYPES as unknown as R,
|
|
148
|
+
} = options;
|
|
149
149
|
if (this.#executed) {
|
|
150
150
|
throw new Error("Benchmark is executed and can't be reused");
|
|
151
151
|
}
|
|
152
152
|
this.#executed = true;
|
|
153
|
-
|
|
154
|
-
const resolvedBaseUrl = baseUrl ?? pathToFileURL(process.cwd()).href;
|
|
155
|
-
if (!baseUrl) {
|
|
156
|
-
console.warn("Overtake: baseUrl not provided; defaulting to process.cwd(). Pass the benchmark's import.meta.url so relative imports resolve correctly.");
|
|
157
|
-
}
|
|
153
|
+
const benchmarkUrl = (options as unknown as Record<symbol, unknown>)[BENCHMARK_URL];
|
|
158
154
|
|
|
159
155
|
const executor = createExecutor<unknown, TInput, R>({
|
|
160
|
-
baseUrl: resolvedBaseUrl,
|
|
161
156
|
workers,
|
|
162
157
|
warmupCycles,
|
|
163
158
|
maxCycles,
|
|
@@ -166,7 +161,8 @@ export class Benchmark<TInput> {
|
|
|
166
161
|
relThreshold,
|
|
167
162
|
gcObserver,
|
|
168
163
|
reportTypes,
|
|
169
|
-
|
|
164
|
+
[BENCHMARK_URL]: benchmarkUrl,
|
|
165
|
+
} as Required<ExecutorOptions<R>>);
|
|
170
166
|
|
|
171
167
|
const reports: TargetReport<R>[] = [];
|
|
172
168
|
for (const target of this.#targets) {
|
|
@@ -177,7 +173,6 @@ export class Benchmark<TInput> {
|
|
|
177
173
|
const data = await feed.fn?.();
|
|
178
174
|
executor
|
|
179
175
|
.push<ExecutorReport<R>>({
|
|
180
|
-
baseUrl: resolvedBaseUrl,
|
|
181
176
|
setup: target.setup,
|
|
182
177
|
teardown: target.teardown,
|
|
183
178
|
pre: measure.pre,
|