overtake 1.0.5 → 1.1.0
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/README.md +25 -29
- package/build/cli.cjs +43 -33
- package/build/cli.cjs.map +1 -1
- package/build/cli.js +42 -32
- package/build/cli.js.map +1 -1
- package/build/executor.cjs +6 -3
- package/build/executor.cjs.map +1 -1
- package/build/executor.d.ts +3 -2
- package/build/executor.js +6 -3
- package/build/executor.js.map +1 -1
- package/build/gc-watcher.cjs +31 -0
- package/build/gc-watcher.cjs.map +1 -0
- package/build/gc-watcher.d.ts +9 -0
- package/build/gc-watcher.js +21 -0
- package/build/gc-watcher.js.map +1 -0
- package/build/index.cjs +9 -1
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.js +9 -1
- package/build/index.js.map +1 -1
- package/build/runner.cjs +226 -24
- package/build/runner.cjs.map +1 -1
- package/build/runner.d.ts +1 -1
- package/build/runner.js +226 -24
- package/build/runner.js.map +1 -1
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +4 -0
- package/build/types.js.map +1 -1
- package/build/utils.cjs +21 -0
- package/build/utils.cjs.map +1 -1
- package/build/utils.d.ts +1 -0
- package/build/utils.js +18 -0
- package/build/utils.js.map +1 -1
- package/build/worker.cjs +95 -14
- package/build/worker.cjs.map +1 -1
- package/build/worker.d.ts +1 -1
- package/build/worker.js +54 -8
- package/build/worker.js.map +1 -1
- package/examples/accuracy.ts +54 -0
- package/examples/custom-reports.ts +0 -1
- package/examples/imports.ts +3 -7
- package/examples/quick-start.ts +2 -0
- package/package.json +10 -9
- package/src/cli.ts +42 -29
- package/src/executor.ts +8 -2
- package/src/gc-watcher.ts +23 -0
- package/src/index.ts +11 -0
- package/src/runner.ts +265 -23
- package/src/types.ts +4 -0
- package/src/utils.ts +20 -0
- package/src/worker.ts +59 -9
package/build/worker.cjs
CHANGED
|
@@ -2,21 +2,102 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
Object.defineProperty(exports, "exitCode", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return exitCode;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
5
|
const _nodeworker_threads = require("node:worker_threads");
|
|
6
|
+
const _nodevm = require("node:vm");
|
|
7
|
+
const _nodemodule = require("node:module");
|
|
8
|
+
const _nodeurl = require("node:url");
|
|
12
9
|
const _runnercjs = require("./runner.cjs");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
11
|
+
if (typeof WeakMap !== "function") return null;
|
|
12
|
+
var cacheBabelInterop = new WeakMap();
|
|
13
|
+
var cacheNodeInterop = new WeakMap();
|
|
14
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
15
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
16
|
+
})(nodeInterop);
|
|
17
|
+
}
|
|
18
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
19
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
20
|
+
return obj;
|
|
21
|
+
}
|
|
22
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
23
|
+
return {
|
|
24
|
+
default: obj
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
28
|
+
if (cache && cache.has(obj)) {
|
|
29
|
+
return cache.get(obj);
|
|
30
|
+
}
|
|
31
|
+
var newObj = {
|
|
32
|
+
__proto__: null
|
|
33
|
+
};
|
|
34
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
35
|
+
for(var key in obj){
|
|
36
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
37
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
38
|
+
if (desc && (desc.get || desc.set)) {
|
|
39
|
+
Object.defineProperty(newObj, key, desc);
|
|
40
|
+
} else {
|
|
41
|
+
newObj[key] = obj[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
newObj.default = obj;
|
|
46
|
+
if (cache) {
|
|
47
|
+
cache.set(obj, newObj);
|
|
48
|
+
}
|
|
49
|
+
return newObj;
|
|
50
|
+
}
|
|
51
|
+
const { baseUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = _nodeworker_threads.workerData;
|
|
52
|
+
const serialize = (code)=>code ? code : '() => {}';
|
|
53
|
+
const source = `
|
|
54
|
+
export const setup = ${serialize(setupCode)};
|
|
55
|
+
export const teardown = ${serialize(teardownCode)};
|
|
56
|
+
export const pre = ${serialize(preCode)};
|
|
57
|
+
export const run = ${serialize(runCode)};
|
|
58
|
+
export const post = ${serialize(postCode)};
|
|
59
|
+
`;
|
|
60
|
+
const context = (0, _nodevm.createContext)({
|
|
61
|
+
console,
|
|
62
|
+
Buffer
|
|
63
|
+
});
|
|
64
|
+
const imports = new Map();
|
|
65
|
+
const mod = new _nodevm.SourceTextModule(source, {
|
|
66
|
+
identifier: baseUrl,
|
|
67
|
+
context,
|
|
68
|
+
initializeImportMeta (meta) {
|
|
69
|
+
meta.url = baseUrl;
|
|
70
|
+
},
|
|
71
|
+
importModuleDynamically (specifier, referencingModule) {
|
|
72
|
+
const base = referencingModule.identifier ?? baseUrl;
|
|
73
|
+
const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(base));
|
|
74
|
+
return Promise.resolve(resolveFrom.resolve(specifier)).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
await mod.link(async (specifier, referencingModule)=>{
|
|
78
|
+
const base = referencingModule.identifier ?? baseUrl;
|
|
79
|
+
const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(base));
|
|
80
|
+
const target = resolveFrom.resolve(specifier);
|
|
81
|
+
const cached = imports.get(target);
|
|
82
|
+
if (cached) return cached;
|
|
83
|
+
const importedModule = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
|
|
84
|
+
const exportNames = Object.keys(importedModule);
|
|
85
|
+
const imported = new _nodevm.SyntheticModule(exportNames, ()=>{
|
|
86
|
+
exportNames.forEach((key)=>imported.setExport(key, importedModule[key]));
|
|
87
|
+
}, {
|
|
88
|
+
identifier: target,
|
|
89
|
+
context: referencingModule.context
|
|
90
|
+
});
|
|
91
|
+
imports.set(target, imported);
|
|
92
|
+
return imported;
|
|
93
|
+
});
|
|
94
|
+
await mod.evaluate();
|
|
95
|
+
const { setup, teardown, pre, run, post } = mod.namespace;
|
|
96
|
+
if (!run) {
|
|
97
|
+
throw new Error('Benchmark run function is required');
|
|
98
|
+
}
|
|
99
|
+
process.exitCode = await (0, _runnercjs.benchmark)({
|
|
100
|
+
baseUrl,
|
|
20
101
|
setup,
|
|
21
102
|
teardown,
|
|
22
103
|
pre,
|
|
@@ -27,9 +108,9 @@ const exitCode = await (0, _runnercjs.benchmark)({
|
|
|
27
108
|
minCycles,
|
|
28
109
|
absThreshold,
|
|
29
110
|
relThreshold,
|
|
111
|
+
gcObserver,
|
|
30
112
|
durationsSAB,
|
|
31
113
|
controlSAB
|
|
32
114
|
});
|
|
33
|
-
process.exit(exitCode);
|
|
34
115
|
|
|
35
116
|
//# sourceMappingURL=worker.cjs.map
|
package/build/worker.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { benchmark } from './runner.js';\nimport {
|
|
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 baseUrl,\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 : '() => {}');\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>();\nconst mod = new SourceTextModule(source, {\n identifier: baseUrl,\n context,\n initializeImportMeta(meta) {\n meta.url = baseUrl;\n },\n importModuleDynamically(specifier, referencingModule) {\n const base = referencingModule.identifier ?? baseUrl;\n const resolveFrom = createRequire(fileURLToPath(base));\n return import(resolveFrom.resolve(specifier));\n },\n});\n\nawait mod.link(async (specifier, referencingModule) => {\n const base = referencingModule.identifier ?? baseUrl;\n const resolveFrom = createRequire(fileURLToPath(base));\n const target = resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) return cached;\n\n const importedModule = await import(target);\n const exportNames = Object.keys(importedModule);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, importedModule[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n imports.set(target, imported);\n return imported;\n});\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 baseUrl,\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":["baseUrl","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","gcObserver","durationsSAB","controlSAB","workerData","serialize","code","source","context","createContext","console","Buffer","imports","Map","mod","SourceTextModule","identifier","initializeImportMeta","meta","url","importModuleDynamically","specifier","referencingModule","base","resolveFrom","createRequire","fileURLToPath","resolve","link","target","cached","get","importedModule","exportNames","Object","keys","imported","SyntheticModule","forEach","key","setExport","set","evaluate","setup","teardown","pre","run","post","namespace","Error","process","exitCode","benchmark"],"mappings":";;;;oCAA2B;wBACsC;4BACnC;yBACA;2BACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG1B,MAAM,EACJA,OAAO,EACPC,SAAS,EACTC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,QAAQ,EACRC,IAAI,EAEJC,YAAY,EACZC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,aAAa,IAAI,EAEjBC,YAAY,EACZC,UAAU,EACX,GAAkBC,8BAAU;AAE7B,MAAMC,YAAY,CAACC,OAAmBA,OAAOA,OAAO;AAEpD,MAAMC,SAAS,CAAC;qBACK,EAAEF,UAAUd,WAAW;wBACpB,EAAEc,UAAUb,cAAc;mBAC/B,EAAEa,UAAUZ,SAAS;mBACrB,EAAEY,UAAUX,SAAS;oBACpB,EAAEW,UAAUV,UAAU;EACxC,CAAC;AAEH,MAAMa,UAAUC,IAAAA,qBAAa,EAAC;IAAEC;IAASC;AAAO;AAChD,MAAMC,UAAU,IAAIC;AACpB,MAAMC,MAAM,IAAIC,wBAAgB,CAACR,QAAQ;IACvCS,YAAY1B;IACZkB;IACAS,sBAAqBC,IAAI;QACvBA,KAAKC,GAAG,GAAG7B;IACb;IACA8B,yBAAwBC,SAAS,EAAEC,iBAAiB;QAClD,MAAMC,OAAOD,kBAAkBN,UAAU,IAAI1B;QAC7C,MAAMkC,cAAcC,IAAAA,yBAAa,EAACC,IAAAA,sBAAa,EAACH;QAChD,OAAO,gBAAOC,YAAYG,OAAO,CAACN,8DAA3B;IACT;AACF;AAEA,MAAMP,IAAIc,IAAI,CAAC,OAAOP,WAAWC;IAC/B,MAAMC,OAAOD,kBAAkBN,UAAU,IAAI1B;IAC7C,MAAMkC,cAAcC,IAAAA,yBAAa,EAACC,IAAAA,sBAAa,EAACH;IAChD,MAAMM,SAASL,YAAYG,OAAO,CAACN;IACnC,MAAMS,SAASlB,QAAQmB,GAAG,CAACF;IAC3B,IAAIC,QAAQ,OAAOA;IAEnB,MAAME,iBAAiB,MAAM,gBAAOH,0DAAP;IAC7B,MAAMI,cAAcC,OAAOC,IAAI,CAACH;IAChC,MAAMI,WAAW,IAAIC,uBAAe,CAClCJ,aACA;QACEA,YAAYK,OAAO,CAAC,CAACC,MAAQH,SAASI,SAAS,CAACD,KAAKP,cAAc,CAACO,IAAI;IAC1E,GACA;QAAEvB,YAAYa;QAAQrB,SAASc,kBAAkBd,OAAO;IAAC;IAE3DI,QAAQ6B,GAAG,CAACZ,QAAQO;IACpB,OAAOA;AACT;AAEA,MAAMtB,IAAI4B,QAAQ;AAClB,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,GAAG,EAAEC,IAAI,EAAE,GAAGjC,IAAIkC,SAAS;AAEzD,IAAI,CAACF,KAAK;IACR,MAAM,IAAIG,MAAM;AAClB;AAEAC,QAAQC,QAAQ,GAAG,MAAMC,IAAAA,oBAAS,EAAC;IACjC9D;IACAqD;IACAC;IACAC;IACAC;IACAC;IACAnD;IAEAC;IACAC;IACAC;IACAC;IACAC;IAEAC;IACAC;AACF"}
|
package/build/worker.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {};
|
package/build/worker.js
CHANGED
|
@@ -1,12 +1,58 @@
|
|
|
1
1
|
import { workerData } from 'node:worker_threads';
|
|
2
|
+
import { SourceTextModule, SyntheticModule, createContext } from 'node:vm';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
2
5
|
import { benchmark } from "./runner.js";
|
|
3
|
-
const { setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, durationsSAB, controlSAB } = workerData;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
export const
|
|
6
|
+
const { baseUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = workerData;
|
|
7
|
+
const serialize = (code)=>code ? code : '() => {}';
|
|
8
|
+
const source = `
|
|
9
|
+
export const setup = ${serialize(setupCode)};
|
|
10
|
+
export const teardown = ${serialize(teardownCode)};
|
|
11
|
+
export const pre = ${serialize(preCode)};
|
|
12
|
+
export const run = ${serialize(runCode)};
|
|
13
|
+
export const post = ${serialize(postCode)};
|
|
14
|
+
`;
|
|
15
|
+
const context = createContext({
|
|
16
|
+
console,
|
|
17
|
+
Buffer
|
|
18
|
+
});
|
|
19
|
+
const imports = new Map();
|
|
20
|
+
const mod = new SourceTextModule(source, {
|
|
21
|
+
identifier: baseUrl,
|
|
22
|
+
context,
|
|
23
|
+
initializeImportMeta (meta) {
|
|
24
|
+
meta.url = baseUrl;
|
|
25
|
+
},
|
|
26
|
+
importModuleDynamically (specifier, referencingModule) {
|
|
27
|
+
const base = referencingModule.identifier ?? baseUrl;
|
|
28
|
+
const resolveFrom = createRequire(fileURLToPath(base));
|
|
29
|
+
return import(resolveFrom.resolve(specifier));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
await mod.link(async (specifier, referencingModule)=>{
|
|
33
|
+
const base = referencingModule.identifier ?? baseUrl;
|
|
34
|
+
const resolveFrom = createRequire(fileURLToPath(base));
|
|
35
|
+
const target = resolveFrom.resolve(specifier);
|
|
36
|
+
const cached = imports.get(target);
|
|
37
|
+
if (cached) return cached;
|
|
38
|
+
const importedModule = await import(target);
|
|
39
|
+
const exportNames = Object.keys(importedModule);
|
|
40
|
+
const imported = new SyntheticModule(exportNames, ()=>{
|
|
41
|
+
exportNames.forEach((key)=>imported.setExport(key, importedModule[key]));
|
|
42
|
+
}, {
|
|
43
|
+
identifier: target,
|
|
44
|
+
context: referencingModule.context
|
|
45
|
+
});
|
|
46
|
+
imports.set(target, imported);
|
|
47
|
+
return imported;
|
|
48
|
+
});
|
|
49
|
+
await mod.evaluate();
|
|
50
|
+
const { setup, teardown, pre, run, post } = mod.namespace;
|
|
51
|
+
if (!run) {
|
|
52
|
+
throw new Error('Benchmark run function is required');
|
|
53
|
+
}
|
|
54
|
+
process.exitCode = await benchmark({
|
|
55
|
+
baseUrl,
|
|
10
56
|
setup,
|
|
11
57
|
teardown,
|
|
12
58
|
pre,
|
|
@@ -17,9 +63,9 @@ export const exitCode = await benchmark({
|
|
|
17
63
|
minCycles,
|
|
18
64
|
absThreshold,
|
|
19
65
|
relThreshold,
|
|
66
|
+
gcObserver,
|
|
20
67
|
durationsSAB,
|
|
21
68
|
controlSAB
|
|
22
69
|
});
|
|
23
|
-
process.exit(exitCode);
|
|
24
70
|
|
|
25
71
|
//# sourceMappingURL=worker.js.map
|
package/build/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { benchmark } from './runner.js';\nimport {
|
|
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 baseUrl,\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 : '() => {}');\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>();\nconst mod = new SourceTextModule(source, {\n identifier: baseUrl,\n context,\n initializeImportMeta(meta) {\n meta.url = baseUrl;\n },\n importModuleDynamically(specifier, referencingModule) {\n const base = referencingModule.identifier ?? baseUrl;\n const resolveFrom = createRequire(fileURLToPath(base));\n return import(resolveFrom.resolve(specifier));\n },\n});\n\nawait mod.link(async (specifier, referencingModule) => {\n const base = referencingModule.identifier ?? baseUrl;\n const resolveFrom = createRequire(fileURLToPath(base));\n const target = resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) return cached;\n\n const importedModule = await import(target);\n const exportNames = Object.keys(importedModule);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, importedModule[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n imports.set(target, imported);\n return imported;\n});\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 baseUrl,\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","fileURLToPath","benchmark","baseUrl","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","gcObserver","durationsSAB","controlSAB","serialize","code","source","context","console","Buffer","imports","Map","mod","identifier","initializeImportMeta","meta","url","importModuleDynamically","specifier","referencingModule","base","resolveFrom","resolve","link","target","cached","get","importedModule","exportNames","Object","keys","imported","forEach","key","setExport","set","evaluate","setup","teardown","pre","run","post","namespace","Error","process","exitCode"],"mappings":"AAAA,SAASA,UAAU,QAAQ,sBAAsB;AACjD,SAASC,gBAAgB,EAAEC,eAAe,EAAEC,aAAa,QAAQ,UAAU;AAC3E,SAASC,aAAa,QAAQ,cAAc;AAC5C,SAASC,aAAa,QAAQ,WAAW;AACzC,SAASC,SAAS,QAAQ,cAAc;AAGxC,MAAM,EACJC,OAAO,EACPC,SAAS,EACTC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,QAAQ,EACRC,IAAI,EAEJC,YAAY,EACZC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,aAAa,IAAI,EAEjBC,YAAY,EACZC,UAAU,EACX,GAAkBpB;AAEnB,MAAMqB,YAAY,CAACC,OAAmBA,OAAOA,OAAO;AAEpD,MAAMC,SAAS,CAAC;qBACK,EAAEF,UAAUb,WAAW;wBACpB,EAAEa,UAAUZ,cAAc;mBAC/B,EAAEY,UAAUX,SAAS;mBACrB,EAAEW,UAAUV,SAAS;oBACpB,EAAEU,UAAUT,UAAU;EACxC,CAAC;AAEH,MAAMY,UAAUrB,cAAc;IAAEsB;IAASC;AAAO;AAChD,MAAMC,UAAU,IAAIC;AACpB,MAAMC,MAAM,IAAI5B,iBAAiBsB,QAAQ;IACvCO,YAAYvB;IACZiB;IACAO,sBAAqBC,IAAI;QACvBA,KAAKC,GAAG,GAAG1B;IACb;IACA2B,yBAAwBC,SAAS,EAAEC,iBAAiB;QAClD,MAAMC,OAAOD,kBAAkBN,UAAU,IAAIvB;QAC7C,MAAM+B,cAAclC,cAAcC,cAAcgC;QAChD,OAAO,MAAM,CAACC,YAAYC,OAAO,CAACJ;IACpC;AACF;AAEA,MAAMN,IAAIW,IAAI,CAAC,OAAOL,WAAWC;IAC/B,MAAMC,OAAOD,kBAAkBN,UAAU,IAAIvB;IAC7C,MAAM+B,cAAclC,cAAcC,cAAcgC;IAChD,MAAMI,SAASH,YAAYC,OAAO,CAACJ;IACnC,MAAMO,SAASf,QAAQgB,GAAG,CAACF;IAC3B,IAAIC,QAAQ,OAAOA;IAEnB,MAAME,iBAAiB,MAAM,MAAM,CAACH;IACpC,MAAMI,cAAcC,OAAOC,IAAI,CAACH;IAChC,MAAMI,WAAW,IAAI9C,gBACnB2C,aACA;QACEA,YAAYI,OAAO,CAAC,CAACC,MAAQF,SAASG,SAAS,CAACD,KAAKN,cAAc,CAACM,IAAI;IAC1E,GACA;QAAEpB,YAAYW;QAAQjB,SAASY,kBAAkBZ,OAAO;IAAC;IAE3DG,QAAQyB,GAAG,CAACX,QAAQO;IACpB,OAAOA;AACT;AAEA,MAAMnB,IAAIwB,QAAQ;AAClB,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,GAAG,EAAEC,IAAI,EAAE,GAAG7B,IAAI8B,SAAS;AAEzD,IAAI,CAACF,KAAK;IACR,MAAM,IAAIG,MAAM;AAClB;AAEAC,QAAQC,QAAQ,GAAG,MAAMxD,UAAU;IACjCC;IACA+C;IACAC;IACAC;IACAC;IACAC;IACA7C;IAEAC;IACAC;IACAC;IACAC;IACAC;IAEAC;IACAC;AACF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import 'overtake';
|
|
2
|
+
|
|
3
|
+
const baseline = benchmark('accuracy tuning feed', () => {
|
|
4
|
+
const bytes = 131_072;
|
|
5
|
+
const uints = new Uint32Array(bytes / 4);
|
|
6
|
+
for (let i = 0; i < uints.length; i++) {
|
|
7
|
+
uints[i] = (i * 31) ^ 0x9e3779b1;
|
|
8
|
+
}
|
|
9
|
+
const src = Buffer.alloc(bytes, 7);
|
|
10
|
+
const dst = Buffer.allocUnsafe(bytes);
|
|
11
|
+
|
|
12
|
+
return { uints, src, dst };
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
baseline
|
|
16
|
+
.target('deterministic math', () => {
|
|
17
|
+
return { scratch: 0 };
|
|
18
|
+
})
|
|
19
|
+
.measure('sum uint32 array', (ctx, { uints }) => {
|
|
20
|
+
let acc = ctx.scratch;
|
|
21
|
+
for (let round = 0; round < 8; round++) {
|
|
22
|
+
for (let i = 0; i < uints.length; i++) {
|
|
23
|
+
acc += uints[i];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
ctx.scratch = acc;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
baseline.target('buffer reuse copy').measure('copy preallocated buffer', (_, { src, dst }) => {
|
|
30
|
+
for (let round = 0; round < 8; round++) {
|
|
31
|
+
dst.set(src);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
baseline
|
|
36
|
+
.target('buffer reuse xor')
|
|
37
|
+
.measure('xor in place', (_, { src, dst }) => {
|
|
38
|
+
for (let round = 0; round < 4; round++) {
|
|
39
|
+
for (let i = 0; i < src.length; i++) {
|
|
40
|
+
dst[i] ^= src[i];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.pre((_, { dst }) => {
|
|
45
|
+
dst.fill(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
baseline.target('steady loop baseline').measure('counter increment', () => {
|
|
49
|
+
let x = 0;
|
|
50
|
+
for (let i = 0; i < 200_000; i++) {
|
|
51
|
+
x += (i & 1023) >>> 0;
|
|
52
|
+
}
|
|
53
|
+
return x;
|
|
54
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Demonstrates custom report types and statistics
|
|
2
2
|
// CLI mode: npx overtake examples/custom-reports.ts -f table -r ops mean median p95 p99
|
|
3
3
|
// Programmatic mode: node examples/custom-reports.js
|
|
4
|
-
|
|
5
4
|
import { Benchmark, printTableReports } from '../build/index.js';
|
|
6
5
|
|
|
7
6
|
const performanceSuite = new Benchmark('10K numbers', () => Array.from({ length: 10_000 }, () => Math.random() * 1000));
|
package/examples/imports.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Demonstrates correct import patterns for worker context
|
|
2
2
|
// Run: npx overtake examples/imports.ts
|
|
3
|
+
import 'overtake';
|
|
3
4
|
|
|
4
5
|
const importExamples = benchmark('test data', () => Buffer.from('hello world'));
|
|
5
6
|
|
|
@@ -13,14 +14,9 @@ importExamples
|
|
|
13
14
|
createHash('sha256').update(buffer).digest('hex');
|
|
14
15
|
});
|
|
15
16
|
|
|
16
|
-
//
|
|
17
|
-
// Relative imports resolve from node_modules/overtake/build/, not your project
|
|
17
|
+
// Relative imports resolve relative to benchmark file
|
|
18
18
|
const localFilesTarget = importExamples.target('local files', async () => {
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
// Build absolute path to your local module
|
|
22
|
-
const localModulePath = join(process.cwd(), './build/types.js');
|
|
23
|
-
const { DEFAULT_CYCLES, Control } = await import(localModulePath);
|
|
19
|
+
const { DEFAULT_CYCLES, Control } = await import('../build/types.js');
|
|
24
20
|
|
|
25
21
|
return {
|
|
26
22
|
DEFAULT_CYCLES,
|
package/examples/quick-start.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Minimal example - comparing array sum algorithms
|
|
2
2
|
// Run: npx overtake examples/quick-start.ts
|
|
3
|
+
// Import overtake to support global types
|
|
4
|
+
import 'overtake';
|
|
3
5
|
|
|
4
6
|
const sumBenchmark = benchmark('1M numbers', () => Array.from({ length: 1_000_000 }, (_, index) => index));
|
|
5
7
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtake",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "NodeJS performance benchmark",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -41,23 +41,24 @@
|
|
|
41
41
|
},
|
|
42
42
|
"homepage": "https://github.com/3axap4eHko/overtake#readme",
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@jest/globals": "^30.0
|
|
44
|
+
"@jest/globals": "^30.2.0",
|
|
45
45
|
"@swc/jest": "^0.2.39",
|
|
46
46
|
"@types/async": "^3.2.25",
|
|
47
47
|
"@types/jest": "^30.0.0",
|
|
48
|
-
"@types/node": "^24.
|
|
48
|
+
"@types/node": "^24.10.1",
|
|
49
49
|
"husky": "^9.1.7",
|
|
50
|
-
"inop": "^0.
|
|
51
|
-
"jest": "^30.0
|
|
50
|
+
"inop": "^0.8.0",
|
|
51
|
+
"jest": "^30.2.0",
|
|
52
|
+
"overtake": "^1.0.5",
|
|
52
53
|
"prettier": "^3.6.2",
|
|
53
54
|
"pretty-quick": "^4.2.2",
|
|
54
|
-
"typescript": "^5.9.
|
|
55
|
+
"typescript": "^5.9.3"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
|
-
"@swc/core": "^1.
|
|
58
|
+
"@swc/core": "^1.15.2",
|
|
58
59
|
"async": "^3.2.6",
|
|
59
|
-
"commander": "^14.0.
|
|
60
|
-
"glob": "^
|
|
60
|
+
"commander": "^14.0.2",
|
|
61
|
+
"glob": "^13.0.0"
|
|
61
62
|
},
|
|
62
63
|
"scripts": {
|
|
63
64
|
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
package/src/cli.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createRequire, Module } from 'node:module';
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
2
3
|
import { SyntheticModule, createContext, SourceTextModule } from 'node:vm';
|
|
3
4
|
import { stat, readFile } from 'node:fs/promises';
|
|
4
|
-
import { transform } from '@swc/core';
|
|
5
5
|
import { Command, Option } from 'commander';
|
|
6
6
|
import { glob } from 'glob';
|
|
7
7
|
import { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';
|
|
8
|
+
import { transpile } from './utils.js';
|
|
8
9
|
import { REPORT_TYPES } from './types.js';
|
|
9
10
|
|
|
10
11
|
const require = createRequire(import.meta.url);
|
|
@@ -12,29 +13,11 @@ const { name, description, version } = require('../package.json');
|
|
|
12
13
|
|
|
13
14
|
const commander = new Command();
|
|
14
15
|
|
|
15
|
-
const transpile = async (code: string): Promise<string> => {
|
|
16
|
-
const output = await transform(code, {
|
|
17
|
-
filename: 'benchmark.ts',
|
|
18
|
-
jsc: {
|
|
19
|
-
parser: {
|
|
20
|
-
syntax: 'typescript',
|
|
21
|
-
tsx: false,
|
|
22
|
-
dynamicImport: true,
|
|
23
|
-
},
|
|
24
|
-
target: 'esnext',
|
|
25
|
-
},
|
|
26
|
-
module: {
|
|
27
|
-
type: 'es6',
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
return output.code;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
16
|
commander
|
|
34
17
|
.name(name)
|
|
35
18
|
.description(description)
|
|
36
19
|
.version(version)
|
|
37
|
-
.argument('<
|
|
20
|
+
.argument('<paths...>', 'glob pattern to find benchmarks')
|
|
38
21
|
.addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES))
|
|
39
22
|
.addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt))
|
|
40
23
|
.addOption(new Option('-f, --format [format]', 'output format').default('simple').choices(['simple', 'json', 'pjson', 'table']))
|
|
@@ -43,12 +26,21 @@ commander
|
|
|
43
26
|
.addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt))
|
|
44
27
|
.addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt))
|
|
45
28
|
.addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt))
|
|
46
|
-
.
|
|
47
|
-
|
|
29
|
+
.addOption(new Option('--no-gc-observer', 'disable GC overlap detection'))
|
|
30
|
+
.action(async (patterns: string[], executeOptions) => {
|
|
31
|
+
const files = new Set<string>();
|
|
32
|
+
await Promise.all(
|
|
33
|
+
patterns.map(async (pattern) => {
|
|
34
|
+
const matches = await glob(pattern, { absolute: true, cwd: process.cwd() }).catch(() => []);
|
|
35
|
+
matches.forEach((file) => files.add(file));
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
|
|
48
39
|
for (const file of files) {
|
|
49
40
|
const stats = await stat(file).catch(() => false as const);
|
|
50
41
|
if (stats && stats.isFile()) {
|
|
51
42
|
const content = await readFile(file, 'utf8');
|
|
43
|
+
const identifier = pathToFileURL(file).href;
|
|
52
44
|
const code = await transpile(content);
|
|
53
45
|
let instance: Benchmark<unknown> | undefined;
|
|
54
46
|
const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {
|
|
@@ -59,33 +51,54 @@ commander
|
|
|
59
51
|
return instance;
|
|
60
52
|
};
|
|
61
53
|
const script = new SourceTextModule(code, {
|
|
54
|
+
identifier,
|
|
62
55
|
context: createContext({
|
|
63
56
|
benchmark,
|
|
64
57
|
Buffer,
|
|
58
|
+
console,
|
|
65
59
|
}),
|
|
60
|
+
initializeImportMeta(meta) {
|
|
61
|
+
meta.url = identifier;
|
|
62
|
+
},
|
|
63
|
+
async importModuleDynamically(specifier, referencingModule) {
|
|
64
|
+
if (Module.isBuiltin(specifier)) {
|
|
65
|
+
return import(specifier);
|
|
66
|
+
}
|
|
67
|
+
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
68
|
+
const resolveFrom = createRequire(fileURLToPath(baseIdentifier));
|
|
69
|
+
const resolved = resolveFrom.resolve(specifier);
|
|
70
|
+
return import(resolved);
|
|
71
|
+
},
|
|
66
72
|
});
|
|
67
|
-
const imports = new Map();
|
|
73
|
+
const imports = new Map<string, SyntheticModule>();
|
|
68
74
|
await script.link(async (specifier: string, referencingModule) => {
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
const baseIdentifier = referencingModule.identifier ?? identifier;
|
|
76
|
+
const resolveFrom = createRequire(fileURLToPath(baseIdentifier));
|
|
77
|
+
const target = Module.isBuiltin(specifier) ? specifier : resolveFrom.resolve(specifier);
|
|
78
|
+
const cached = imports.get(target);
|
|
79
|
+
if (cached) {
|
|
80
|
+
return cached;
|
|
71
81
|
}
|
|
72
|
-
const mod = await import(
|
|
82
|
+
const mod = await import(target);
|
|
73
83
|
const exportNames = Object.keys(mod);
|
|
74
84
|
const imported = new SyntheticModule(
|
|
75
85
|
exportNames,
|
|
76
86
|
() => {
|
|
77
87
|
exportNames.forEach((key) => imported.setExport(key, mod[key]));
|
|
78
88
|
},
|
|
79
|
-
{ identifier:
|
|
89
|
+
{ identifier: target, context: referencingModule.context },
|
|
80
90
|
);
|
|
81
91
|
|
|
82
|
-
imports.set(
|
|
92
|
+
imports.set(target, imported);
|
|
83
93
|
return imported;
|
|
84
94
|
});
|
|
85
95
|
await script.evaluate();
|
|
86
96
|
|
|
87
97
|
if (instance) {
|
|
88
|
-
const reports = await instance.execute(
|
|
98
|
+
const reports = await instance.execute({
|
|
99
|
+
...executeOptions,
|
|
100
|
+
baseUrl: identifier,
|
|
101
|
+
});
|
|
89
102
|
switch (executeOptions.format) {
|
|
90
103
|
case 'json':
|
|
91
104
|
{
|
package/src/executor.ts
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import { Worker } from 'node:worker_threads';
|
|
2
2
|
import { once } from 'node:events';
|
|
3
3
|
import { queue } from 'async';
|
|
4
|
-
import {
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
5
|
import { createReport, Report } from './reporter.js';
|
|
6
6
|
import { cmp } from './utils.js';
|
|
7
|
+
import { RunOptions, ReportOptions, WorkerOptions, BenchmarkOptions, Control, ReportType, ReportTypeList, CONTROL_SLOTS } from './types.js';
|
|
7
8
|
|
|
8
9
|
export type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & { count: number };
|
|
9
10
|
|
|
10
11
|
export interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {
|
|
12
|
+
baseUrl?: string;
|
|
11
13
|
workers?: number;
|
|
12
14
|
maxCycles?: number;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export const createExecutor = <TContext, TInput, R extends ReportTypeList>({
|
|
18
|
+
baseUrl = pathToFileURL(process.cwd()).href,
|
|
16
19
|
workers,
|
|
17
20
|
warmupCycles,
|
|
18
21
|
maxCycles,
|
|
19
22
|
minCycles,
|
|
20
23
|
absThreshold,
|
|
21
24
|
relThreshold,
|
|
25
|
+
gcObserver = true,
|
|
22
26
|
reportTypes,
|
|
23
27
|
}: Required<ExecutorOptions<R>>) => {
|
|
24
|
-
const executor = queue<RunOptions<TContext, TInput>>(async ({ setup, teardown, pre, run, post, data }) => {
|
|
28
|
+
const executor = queue<RunOptions<TContext, TInput>>(async ({ baseUrl: runBaseUrl = baseUrl, setup, teardown, pre, run, post, data }) => {
|
|
25
29
|
const setupCode = setup?.toString();
|
|
26
30
|
const teardownCode = teardown?.toString();
|
|
27
31
|
const preCode = pre?.toString();
|
|
@@ -33,6 +37,7 @@ export const createExecutor = <TContext, TInput, R extends ReportTypeList>({
|
|
|
33
37
|
|
|
34
38
|
const workerFile = new URL('./worker.js', import.meta.url);
|
|
35
39
|
const workerData: WorkerOptions = {
|
|
40
|
+
baseUrl: runBaseUrl,
|
|
36
41
|
setupCode,
|
|
37
42
|
teardownCode,
|
|
38
43
|
preCode,
|
|
@@ -44,6 +49,7 @@ export const createExecutor = <TContext, TInput, R extends ReportTypeList>({
|
|
|
44
49
|
minCycles,
|
|
45
50
|
absThreshold,
|
|
46
51
|
relThreshold,
|
|
52
|
+
gcObserver,
|
|
47
53
|
|
|
48
54
|
controlSAB,
|
|
49
55
|
durationsSAB,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface GCMarker {
|
|
2
|
+
ref: WeakRef<object>;
|
|
3
|
+
token: object;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export class GCWatcher {
|
|
7
|
+
#registry = new FinalizationRegistry(() => {});
|
|
8
|
+
|
|
9
|
+
start(): GCMarker {
|
|
10
|
+
const token = {};
|
|
11
|
+
const ref = new WeakRef(token);
|
|
12
|
+
this.#registry.register(token, null, token);
|
|
13
|
+
return { ref, token };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
seen(marker: GCMarker): boolean {
|
|
17
|
+
const collected = marker.ref.deref() === undefined;
|
|
18
|
+
if (!collected) {
|
|
19
|
+
this.#registry.unregister(marker.token);
|
|
20
|
+
}
|
|
21
|
+
return collected;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cpus } from 'node:os';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
2
3
|
import { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';
|
|
3
4
|
import { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';
|
|
4
5
|
|
|
@@ -141,20 +142,29 @@ export class Benchmark<TInput> {
|
|
|
141
142
|
minCycles = 50,
|
|
142
143
|
absThreshold = 1_000,
|
|
143
144
|
relThreshold = 0.02,
|
|
145
|
+
gcObserver = true,
|
|
144
146
|
reportTypes = DEFAULT_REPORT_TYPES as unknown as R,
|
|
147
|
+
baseUrl,
|
|
145
148
|
}: ExecutorOptions<R>): Promise<TargetReport<R>[]> {
|
|
146
149
|
if (this.#executed) {
|
|
147
150
|
throw new Error("Benchmark is executed and can't be reused");
|
|
148
151
|
}
|
|
149
152
|
this.#executed = true;
|
|
150
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
|
+
}
|
|
158
|
+
|
|
151
159
|
const executor = createExecutor<unknown, TInput, R>({
|
|
160
|
+
baseUrl: resolvedBaseUrl,
|
|
152
161
|
workers,
|
|
153
162
|
warmupCycles,
|
|
154
163
|
maxCycles,
|
|
155
164
|
minCycles,
|
|
156
165
|
absThreshold,
|
|
157
166
|
relThreshold,
|
|
167
|
+
gcObserver,
|
|
158
168
|
reportTypes,
|
|
159
169
|
});
|
|
160
170
|
|
|
@@ -167,6 +177,7 @@ export class Benchmark<TInput> {
|
|
|
167
177
|
const data = await feed.fn?.();
|
|
168
178
|
executor
|
|
169
179
|
.push<ExecutorReport<R>>({
|
|
180
|
+
baseUrl: resolvedBaseUrl,
|
|
170
181
|
setup: target.setup,
|
|
171
182
|
teardown: target.teardown,
|
|
172
183
|
pre: measure.pre,
|