overtake 1.0.4 → 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 +68 -25
- package/bin/overtake.js +1 -1
- package/build/cli.cjs +44 -33
- package/build/cli.cjs.map +1 -1
- package/build/cli.js +43 -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 -18
- package/build/runner.cjs.map +1 -1
- package/build/runner.d.ts +1 -1
- package/build/runner.js +226 -18
- 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 -8
- package/build/worker.cjs.map +1 -1
- package/build/worker.js +54 -8
- package/build/worker.js.map +1 -1
- package/examples/accuracy.ts +54 -0
- package/examples/complete.ts +3 -3
- package/examples/custom-reports.ts +21 -0
- package/examples/imports.ts +47 -0
- package/examples/quick-start.ts +10 -9
- package/package.json +10 -9
- package/src/cli.ts +46 -30
- package/src/executor.ts +8 -2
- package/src/gc-watcher.ts +23 -0
- package/src/index.ts +11 -0
- package/src/runner.ts +266 -17
- package/src/types.ts +4 -0
- package/src/utils.ts +20 -0
- package/src/worker.ts +59 -9
- package/CLAUDE.md +0 -145
- package/examples/array-copy.ts +0 -17
- package/examples/object-merge.ts +0 -41
- package/examples/serialization.ts +0 -22
package/build/executor.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
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 } from "./reporter.js";
|
|
6
6
|
import { cmp } from "./utils.js";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { Control, CONTROL_SLOTS } from "./types.js";
|
|
8
|
+
export const createExecutor = ({ baseUrl = pathToFileURL(process.cwd()).href, workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes })=>{
|
|
9
|
+
const executor = queue(async ({ baseUrl: runBaseUrl = baseUrl, setup, teardown, pre, run, post, data })=>{
|
|
9
10
|
const setupCode = setup?.toString();
|
|
10
11
|
const teardownCode = teardown?.toString();
|
|
11
12
|
const preCode = pre?.toString();
|
|
@@ -15,6 +16,7 @@ export const createExecutor = ({ workers, warmupCycles, maxCycles, minCycles, ab
|
|
|
15
16
|
const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);
|
|
16
17
|
const workerFile = new URL('./worker.js', import.meta.url);
|
|
17
18
|
const workerData = {
|
|
19
|
+
baseUrl: runBaseUrl,
|
|
18
20
|
setupCode,
|
|
19
21
|
teardownCode,
|
|
20
22
|
preCode,
|
|
@@ -25,6 +27,7 @@ export const createExecutor = ({ workers, warmupCycles, maxCycles, minCycles, ab
|
|
|
25
27
|
minCycles,
|
|
26
28
|
absThreshold,
|
|
27
29
|
relThreshold,
|
|
30
|
+
gcObserver,
|
|
28
31
|
controlSAB,
|
|
29
32
|
durationsSAB
|
|
30
33
|
};
|
package/build/executor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/executor.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport { once } from 'node:events';\nimport { queue } from 'async';\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/executor.ts"],"sourcesContent":["import { Worker } from 'node:worker_threads';\nimport { once } from 'node:events';\nimport { queue } from 'async';\nimport { pathToFileURL } from 'node:url';\nimport { createReport, Report } from './reporter.js';\nimport { cmp } from './utils.js';\nimport { RunOptions, ReportOptions, WorkerOptions, BenchmarkOptions, Control, ReportType, ReportTypeList, CONTROL_SLOTS } from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & { count: number };\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n baseUrl?: string;\n workers?: number;\n maxCycles?: number;\n}\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>({\n baseUrl = pathToFileURL(process.cwd()).href,\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = true,\n reportTypes,\n}: Required<ExecutorOptions<R>>) => {\n const executor = queue<RunOptions<TContext, TInput>>(async ({ baseUrl: runBaseUrl = baseUrl, setup, teardown, pre, run, post, data }) => {\n const setupCode = setup?.toString();\n const teardownCode = teardown?.toString();\n const preCode = pre?.toString();\n const runCode = run.toString()!;\n const postCode = post?.toString();\n\n const controlSAB = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * CONTROL_SLOTS);\n const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);\n\n const workerFile = new URL('./worker.js', import.meta.url);\n const workerData: WorkerOptions = {\n baseUrl: runBaseUrl,\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n\n controlSAB,\n durationsSAB,\n };\n\n const worker = new Worker(workerFile, {\n workerData,\n });\n const [exitCode] = await once(worker, 'exit');\n if (exitCode !== 0) {\n throw new Error(`worker exited with code ${exitCode}`);\n }\n\n const control = new Int32Array(controlSAB);\n const count = control[Control.INDEX];\n const durations = new BigUint64Array(durationsSAB).slice(0, count).sort(cmp);\n\n const report = reportTypes.map<[string, unknown]>((type) => [type, createReport(durations, type)] as [ReportType, Report]).concat([['count', count]]);\n return Object.fromEntries(report);\n }, workers);\n\n executor.error((err) => {\n console.error(err);\n });\n\n return executor;\n};\n"],"names":["Worker","once","queue","pathToFileURL","createReport","cmp","Control","CONTROL_SLOTS","createExecutor","baseUrl","process","cwd","href","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","executor","runBaseUrl","setup","teardown","pre","run","post","data","setupCode","toString","teardownCode","preCode","runCode","postCode","controlSAB","SharedArrayBuffer","Int32Array","BYTES_PER_ELEMENT","durationsSAB","BigUint64Array","workerFile","URL","url","workerData","worker","exitCode","Error","control","count","INDEX","durations","slice","sort","report","map","type","concat","Object","fromEntries","error","err","console"],"mappings":"AAAA,SAASA,MAAM,QAAQ,sBAAsB;AAC7C,SAASC,IAAI,QAAQ,cAAc;AACnC,SAASC,KAAK,QAAQ,QAAQ;AAC9B,SAASC,aAAa,QAAQ,WAAW;AACzC,SAASC,YAAY,QAAgB,gBAAgB;AACrD,SAASC,GAAG,QAAQ,aAAa;AACjC,SAAqEC,OAAO,EAA8BC,aAAa,QAAQ,aAAa;AAU5I,OAAO,MAAMC,iBAAiB,CAA6C,EACzEC,UAAUN,cAAcO,QAAQC,GAAG,IAAIC,IAAI,EAC3CC,OAAO,EACPC,YAAY,EACZC,SAAS,EACTC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,aAAa,IAAI,EACjBC,WAAW,EACkB;IAC7B,MAAMC,WAAWnB,MAAoC,OAAO,EAAEO,SAASa,aAAab,OAAO,EAAEc,KAAK,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,GAAG,EAAEC,IAAI,EAAEC,IAAI,EAAE;QAClI,MAAMC,YAAYN,OAAOO;QACzB,MAAMC,eAAeP,UAAUM;QAC/B,MAAME,UAAUP,KAAKK;QACrB,MAAMG,UAAUP,IAAII,QAAQ;QAC5B,MAAMI,WAAWP,MAAMG;QAEvB,MAAMK,aAAa,IAAIC,kBAAkBC,WAAWC,iBAAiB,GAAG/B;QACxE,MAAMgC,eAAe,IAAIH,kBAAkBI,eAAeF,iBAAiB,GAAGvB;QAE9E,MAAM0B,aAAa,IAAIC,IAAI,eAAe,YAAYC,GAAG;QACzD,MAAMC,aAA4B;YAChCnC,SAASa;YACTO;YACAE;YACAC;YACAC;YACAC;YACAN;YAEAd;YACAE;YACAC;YACAC;YACAC;YAEAgB;YACAI;QACF;QAEA,MAAMM,SAAS,IAAI7C,OAAOyC,YAAY;YACpCG;QACF;QACA,MAAM,CAACE,SAAS,GAAG,MAAM7C,KAAK4C,QAAQ;QACtC,IAAIC,aAAa,GAAG;YAClB,MAAM,IAAIC,MAAM,CAAC,wBAAwB,EAAED,UAAU;QACvD;QAEA,MAAME,UAAU,IAAIX,WAAWF;QAC/B,MAAMc,QAAQD,OAAO,CAAC1C,QAAQ4C,KAAK,CAAC;QACpC,MAAMC,YAAY,IAAIX,eAAeD,cAAca,KAAK,CAAC,GAAGH,OAAOI,IAAI,CAAChD;QAExE,MAAMiD,SAASlC,YAAYmC,GAAG,CAAoB,CAACC,OAAS;gBAACA;gBAAMpD,aAAa+C,WAAWK;aAAM,EAA0BC,MAAM,CAAC;YAAC;gBAAC;gBAASR;aAAM;SAAC;QACpJ,OAAOS,OAAOC,WAAW,CAACL;IAC5B,GAAGzC;IAEHQ,SAASuC,KAAK,CAAC,CAACC;QACdC,QAAQF,KAAK,CAACC;IAChB;IAEA,OAAOxC;AACT,EAAE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "GCWatcher", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return GCWatcher;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
class GCWatcher {
|
|
12
|
+
#registry = new FinalizationRegistry(()=>{});
|
|
13
|
+
start() {
|
|
14
|
+
const token = {};
|
|
15
|
+
const ref = new WeakRef(token);
|
|
16
|
+
this.#registry.register(token, null, token);
|
|
17
|
+
return {
|
|
18
|
+
ref,
|
|
19
|
+
token
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
seen(marker) {
|
|
23
|
+
const collected = marker.ref.deref() === undefined;
|
|
24
|
+
if (!collected) {
|
|
25
|
+
this.#registry.unregister(marker.token);
|
|
26
|
+
}
|
|
27
|
+
return collected;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=gc-watcher.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/gc-watcher.ts"],"sourcesContent":["export interface GCMarker {\n ref: WeakRef<object>;\n token: object;\n}\n\nexport class GCWatcher {\n #registry = new FinalizationRegistry(() => {});\n\n start(): GCMarker {\n const token = {};\n const ref = new WeakRef(token);\n this.#registry.register(token, null, token);\n return { ref, token };\n }\n\n seen(marker: GCMarker): boolean {\n const collected = marker.ref.deref() === undefined;\n if (!collected) {\n this.#registry.unregister(marker.token);\n }\n return collected;\n }\n}\n"],"names":["GCWatcher","FinalizationRegistry","start","token","ref","WeakRef","register","seen","marker","collected","deref","undefined","unregister"],"mappings":";;;;+BAKaA;;;eAAAA;;;AAAN,MAAMA;IACX,CAAA,QAAS,GAAG,IAAIC,qBAAqB,KAAO,GAAG;IAE/CC,QAAkB;QAChB,MAAMC,QAAQ,CAAC;QACf,MAAMC,MAAM,IAAIC,QAAQF;QACxB,IAAI,CAAC,CAAA,QAAS,CAACG,QAAQ,CAACH,OAAO,MAAMA;QACrC,OAAO;YAAEC;YAAKD;QAAM;IACtB;IAEAI,KAAKC,MAAgB,EAAW;QAC9B,MAAMC,YAAYD,OAAOJ,GAAG,CAACM,KAAK,OAAOC;QACzC,IAAI,CAACF,WAAW;YACd,IAAI,CAAC,CAAA,QAAS,CAACG,UAAU,CAACJ,OAAOL,KAAK;QACxC;QACA,OAAOM;IACT;AACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class GCWatcher {
|
|
2
|
+
#registry = new FinalizationRegistry(()=>{});
|
|
3
|
+
start() {
|
|
4
|
+
const token = {};
|
|
5
|
+
const ref = new WeakRef(token);
|
|
6
|
+
this.#registry.register(token, null, token);
|
|
7
|
+
return {
|
|
8
|
+
ref,
|
|
9
|
+
token
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
seen(marker) {
|
|
13
|
+
const collected = marker.ref.deref() === undefined;
|
|
14
|
+
if (!collected) {
|
|
15
|
+
this.#registry.unregister(marker.token);
|
|
16
|
+
}
|
|
17
|
+
return collected;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=gc-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/gc-watcher.ts"],"sourcesContent":["export interface GCMarker {\n ref: WeakRef<object>;\n token: object;\n}\n\nexport class GCWatcher {\n #registry = new FinalizationRegistry(() => {});\n\n start(): GCMarker {\n const token = {};\n const ref = new WeakRef(token);\n this.#registry.register(token, null, token);\n return { ref, token };\n }\n\n seen(marker: GCMarker): boolean {\n const collected = marker.ref.deref() === undefined;\n if (!collected) {\n this.#registry.unregister(marker.token);\n }\n return collected;\n }\n}\n"],"names":["GCWatcher","FinalizationRegistry","start","token","ref","WeakRef","register","seen","marker","collected","deref","undefined","unregister"],"mappings":"AAKA,OAAO,MAAMA;IACX,CAAA,QAAS,GAAG,IAAIC,qBAAqB,KAAO,GAAG;IAE/CC,QAAkB;QAChB,MAAMC,QAAQ,CAAC;QACf,MAAMC,MAAM,IAAIC,QAAQF;QACxB,IAAI,CAAC,CAAA,QAAS,CAACG,QAAQ,CAACH,OAAO,MAAMA;QACrC,OAAO;YAAEC;YAAKD;QAAM;IACtB;IAEAI,KAAKC,MAAgB,EAAW;QAC9B,MAAMC,YAAYD,OAAOJ,GAAG,CAACM,KAAK,OAAOC;QACzC,IAAI,CAACF,WAAW;YACd,IAAI,CAAC,CAAA,QAAS,CAACG,UAAU,CAACJ,OAAOL,KAAK;QACxC;QACA,OAAOM;IACT;AACF"}
|
package/build/index.cjs
CHANGED
|
@@ -47,6 +47,7 @@ _export(exports, {
|
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
const _nodeos = require("node:os");
|
|
50
|
+
const _nodeurl = require("node:url");
|
|
50
51
|
const _executorcjs = require("./executor.cjs");
|
|
51
52
|
const _typescjs = require("./types.cjs");
|
|
52
53
|
const DEFAULT_WORKERS = (0, _nodeos.cpus)().length;
|
|
@@ -139,18 +140,24 @@ class Benchmark {
|
|
|
139
140
|
this.#targets.push(target);
|
|
140
141
|
return new Target(target);
|
|
141
142
|
}
|
|
142
|
-
async execute({ workers = DEFAULT_WORKERS, warmupCycles = 20, maxCycles = _typescjs.DEFAULT_CYCLES, minCycles = 50, absThreshold = 1_000, relThreshold = 0.02, reportTypes = DEFAULT_REPORT_TYPES }) {
|
|
143
|
+
async execute({ workers = DEFAULT_WORKERS, warmupCycles = 20, maxCycles = _typescjs.DEFAULT_CYCLES, minCycles = 50, absThreshold = 1_000, relThreshold = 0.02, gcObserver = true, reportTypes = DEFAULT_REPORT_TYPES, baseUrl }) {
|
|
143
144
|
if (this.#executed) {
|
|
144
145
|
throw new Error("Benchmark is executed and can't be reused");
|
|
145
146
|
}
|
|
146
147
|
this.#executed = true;
|
|
148
|
+
const resolvedBaseUrl = baseUrl ?? (0, _nodeurl.pathToFileURL)(process.cwd()).href;
|
|
149
|
+
if (!baseUrl) {
|
|
150
|
+
console.warn("Overtake: baseUrl not provided; defaulting to process.cwd(). Pass the benchmark's import.meta.url so relative imports resolve correctly.");
|
|
151
|
+
}
|
|
147
152
|
const executor = (0, _executorcjs.createExecutor)({
|
|
153
|
+
baseUrl: resolvedBaseUrl,
|
|
148
154
|
workers,
|
|
149
155
|
warmupCycles,
|
|
150
156
|
maxCycles,
|
|
151
157
|
minCycles,
|
|
152
158
|
absThreshold,
|
|
153
159
|
relThreshold,
|
|
160
|
+
gcObserver,
|
|
154
161
|
reportTypes
|
|
155
162
|
});
|
|
156
163
|
const reports = [];
|
|
@@ -167,6 +174,7 @@ class Benchmark {
|
|
|
167
174
|
for (const feed of this.#feeds){
|
|
168
175
|
const data = await feed.fn?.();
|
|
169
176
|
executor.push({
|
|
177
|
+
baseUrl: resolvedBaseUrl,
|
|
170
178
|
setup: target.setup,
|
|
171
179
|
teardown: target.teardown,
|
|
172
180
|
pre: measure.pre,
|
package/build/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const executor = createExecutor<unknown, TInput, R>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["AsyncFunction","Benchmark","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","FeedContext","Measure","MeasureContext","Target","TargetContext","printJSONReports","printSimpleReports","printTableReports","cpus","length","pre","post","title","run","ctx","fn","teardown","measures","setup","measure","push","create","feed","self","target","execute","workers","warmupCycles","maxCycles","DEFAULT_CYCLES","minCycles","absThreshold","relThreshold","reportTypes","Error","executor","createExecutor","reports","targetReport","measureReport","feeds","data","then","drain","kill","report","console","group","output","Object","entries","map","key","toString","join","log","groupEnd","table","fromEntries","padding","row","JSON","stringify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { pathToFileURL } from 'node:url';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n gcObserver = true,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n baseUrl,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const resolvedBaseUrl = baseUrl ?? pathToFileURL(process.cwd()).href;\n if (!baseUrl) {\n console.warn(\"Overtake: baseUrl not provided; defaulting to process.cwd(). Pass the benchmark's import.meta.url so relative imports resolve correctly.\");\n }\n\n const executor = createExecutor<unknown, TInput, R>({\n baseUrl: resolvedBaseUrl,\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n baseUrl: resolvedBaseUrl,\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["AsyncFunction","Benchmark","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","FeedContext","Measure","MeasureContext","Target","TargetContext","printJSONReports","printSimpleReports","printTableReports","cpus","length","pre","post","title","run","ctx","fn","teardown","measures","setup","measure","push","create","feed","self","target","execute","workers","warmupCycles","maxCycles","DEFAULT_CYCLES","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","baseUrl","Error","resolvedBaseUrl","pathToFileURL","process","cwd","href","console","warn","executor","createExecutor","reports","targetReport","measureReport","feeds","data","then","drain","kill","report","group","output","Object","entries","map","key","toString","join","log","groupEnd","table","fromEntries","padding","row","JSON","stringify"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export declare class Benchmark<TInput> {
|
|
|
60
60
|
feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;
|
|
61
61
|
target<TContext>(title: string): Target<void, TInput>;
|
|
62
62
|
target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;
|
|
63
|
-
execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({ workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, reportTypes, }: ExecutorOptions<R>): Promise<TargetReport<R>[]>;
|
|
63
|
+
execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({ workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver, reportTypes, baseUrl, }: ExecutorOptions<R>): Promise<TargetReport<R>[]>;
|
|
64
64
|
}
|
|
65
65
|
export declare const printSimpleReports: <R extends ReportTypeList>(reports: TargetReport<R>[]) => void;
|
|
66
66
|
export declare const printTableReports: <R extends ReportTypeList>(reports: TargetReport<R>[]) => void;
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cpus } from 'node:os';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
2
3
|
import { createExecutor } from "./executor.js";
|
|
3
4
|
import { DEFAULT_CYCLES } from "./types.js";
|
|
4
5
|
export const DEFAULT_WORKERS = cpus().length;
|
|
@@ -91,18 +92,24 @@ export class Benchmark {
|
|
|
91
92
|
this.#targets.push(target);
|
|
92
93
|
return new Target(target);
|
|
93
94
|
}
|
|
94
|
-
async execute({ workers = DEFAULT_WORKERS, warmupCycles = 20, maxCycles = DEFAULT_CYCLES, minCycles = 50, absThreshold = 1_000, relThreshold = 0.02, reportTypes = DEFAULT_REPORT_TYPES }) {
|
|
95
|
+
async execute({ workers = DEFAULT_WORKERS, warmupCycles = 20, maxCycles = DEFAULT_CYCLES, minCycles = 50, absThreshold = 1_000, relThreshold = 0.02, gcObserver = true, reportTypes = DEFAULT_REPORT_TYPES, baseUrl }) {
|
|
95
96
|
if (this.#executed) {
|
|
96
97
|
throw new Error("Benchmark is executed and can't be reused");
|
|
97
98
|
}
|
|
98
99
|
this.#executed = true;
|
|
100
|
+
const resolvedBaseUrl = baseUrl ?? pathToFileURL(process.cwd()).href;
|
|
101
|
+
if (!baseUrl) {
|
|
102
|
+
console.warn("Overtake: baseUrl not provided; defaulting to process.cwd(). Pass the benchmark's import.meta.url so relative imports resolve correctly.");
|
|
103
|
+
}
|
|
99
104
|
const executor = createExecutor({
|
|
105
|
+
baseUrl: resolvedBaseUrl,
|
|
100
106
|
workers,
|
|
101
107
|
warmupCycles,
|
|
102
108
|
maxCycles,
|
|
103
109
|
minCycles,
|
|
104
110
|
absThreshold,
|
|
105
111
|
relThreshold,
|
|
112
|
+
gcObserver,
|
|
106
113
|
reportTypes
|
|
107
114
|
});
|
|
108
115
|
const reports = [];
|
|
@@ -119,6 +126,7 @@ export class Benchmark {
|
|
|
119
126
|
for (const feed of this.#feeds){
|
|
120
127
|
const data = await feed.fn?.();
|
|
121
128
|
executor.push({
|
|
129
|
+
baseUrl: resolvedBaseUrl,
|
|
122
130
|
setup: target.setup,
|
|
123
131
|
teardown: target.teardown,
|
|
124
132
|
pre: measure.pre,
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const executor = createExecutor<unknown, TInput, R>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["cpus","createExecutor","DEFAULT_CYCLES","DEFAULT_WORKERS","length","AsyncFunction","DEFAULT_REPORT_TYPES","MeasureContext","pre","post","title","run","Measure","ctx","fn","TargetContext","teardown","measures","setup","Target","measure","push","FeedContext","Benchmark","create","feed","self","target","execute","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","reportTypes","Error","executor","reports","targetReport","measureReport","feeds","data","then","drain","kill","printSimpleReports","report","console","group","output","Object","entries","map","key","toString","join","log","groupEnd","printTableReports","table","fromEntries","printJSONReports","padding","row","JSON","stringify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport { pathToFileURL } from 'node:url';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = cpus().length;\n\nexport const AsyncFunction = (async () => {}).constructor;\n\nexport interface TargetReport<R extends ReportTypeList> {\n target: string;\n measures: MeasureReport<R>[];\n}\n\nexport interface MeasureReport<R extends ReportTypeList> {\n measure: string;\n feeds: FeedReport<R>[];\n}\n\nexport interface FeedReport<R extends ReportTypeList> {\n feed: string;\n data: ExecutorReport<R>;\n}\n\nexport const DEFAULT_REPORT_TYPES = ['ops'] as const;\nexport type DefaultReportTypes = (typeof DEFAULT_REPORT_TYPES)[number];\n\nexport class MeasureContext<TContext, TInput> {\n public pre?: StepFn<TContext, TInput>;\n public post?: StepFn<TContext, TInput>;\n\n constructor(\n public title: string,\n public run: StepFn<TContext, TInput>,\n ) {}\n}\n\nexport class Measure<TContext, TInput> {\n #ctx: MeasureContext<TContext, TInput>;\n\n constructor(ctx: MeasureContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n\n pre(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.pre = fn;\n return this;\n }\n post(fn: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n this.#ctx.post = fn;\n return this;\n }\n}\n\nexport class TargetContext<TContext, TInput> {\n public teardown?: TeardownFn<TContext>;\n public measures: MeasureContext<TContext, TInput>[] = [];\n\n constructor(\n readonly title: string,\n readonly setup?: SetupFn<MaybePromise<TContext>>,\n ) {}\n}\n\nexport class Target<TContext, TInput> {\n #ctx: TargetContext<TContext, TInput>;\n\n constructor(ctx: TargetContext<TContext, TInput>) {\n this.#ctx = ctx;\n }\n teardown(fn: TeardownFn<TContext>): Target<TContext, TInput> {\n this.#ctx.teardown = fn;\n\n return this;\n }\n measure(title: string, run: StepFn<TContext, TInput>): Measure<TContext, TInput> {\n const measure = new MeasureContext(title, run);\n this.#ctx.measures.push(measure);\n\n return new Measure(measure);\n }\n}\n\nexport class FeedContext<TInput> {\n constructor(\n readonly title: string,\n readonly fn?: FeedFn<TInput>,\n ) {}\n}\n\nexport class Benchmark<TInput> {\n #targets: TargetContext<unknown, TInput>[] = [];\n #feeds: FeedContext<TInput>[] = [];\n #executed = false;\n\n static create(title: string): Benchmark<void>;\n static create<I>(title: string, fn: FeedFn<I>): Benchmark<I>;\n static create<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<I> {\n if (fn) {\n return new Benchmark(title, fn);\n } else {\n return new Benchmark(title);\n }\n }\n\n constructor(title: string);\n constructor(title: string, fn: FeedFn<TInput>);\n constructor(title: string, fn?: FeedFn<TInput> | undefined) {\n if (fn) {\n this.feed(title, fn);\n } else {\n this.feed(title);\n }\n }\n\n feed(title: string): Benchmark<TInput | void>;\n feed<I>(title: string, fn: FeedFn<I>): Benchmark<TInput | I>;\n feed<I>(title: string, fn?: FeedFn<I> | undefined): Benchmark<TInput | I> {\n const self = this as Benchmark<TInput | I>;\n self.#feeds.push(fn ? new FeedContext(title, fn) : new FeedContext(title));\n\n return self;\n }\n\n target<TContext>(title: string): Target<void, TInput>;\n target<TContext>(title: string, setup: SetupFn<Awaited<TContext>>): Target<TContext, TInput>;\n target<TContext>(title: string, setup?: SetupFn<Awaited<TContext>> | undefined): Target<TContext, TInput> {\n const target = new TargetContext<TContext, TInput>(title, setup);\n this.#targets.push(target as TargetContext<unknown, TInput>);\n\n return new Target<TContext, TInput>(target);\n }\n\n async execute<R extends readonly ReportType[] = typeof DEFAULT_REPORT_TYPES>({\n workers = DEFAULT_WORKERS,\n warmupCycles = 20,\n maxCycles = DEFAULT_CYCLES,\n minCycles = 50,\n absThreshold = 1_000,\n relThreshold = 0.02,\n gcObserver = true,\n reportTypes = DEFAULT_REPORT_TYPES as unknown as R,\n baseUrl,\n }: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n\n const resolvedBaseUrl = baseUrl ?? pathToFileURL(process.cwd()).href;\n if (!baseUrl) {\n console.warn(\"Overtake: baseUrl not provided; defaulting to process.cwd(). Pass the benchmark's import.meta.url so relative imports resolve correctly.\");\n }\n\n const executor = createExecutor<unknown, TInput, R>({\n baseUrl: resolvedBaseUrl,\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n reportTypes,\n });\n\n const reports: TargetReport<R>[] = [];\n for (const target of this.#targets) {\n const targetReport: TargetReport<R> = { target: target.title, measures: [] };\n for (const measure of target.measures) {\n const measureReport: MeasureReport<R> = { measure: measure.title, feeds: [] };\n for (const feed of this.#feeds) {\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n baseUrl: resolvedBaseUrl,\n setup: target.setup,\n teardown: target.teardown,\n pre: measure.pre,\n run: measure.run,\n post: measure.post,\n data,\n })\n .then((data) => {\n measureReport.feeds.push({\n feed: feed.title,\n data,\n });\n });\n }\n targetReport.measures.push(measureReport);\n }\n reports.push(targetReport);\n }\n await executor.drain();\n executor.kill();\n\n return reports;\n }\n}\n\nexport const printSimpleReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.group('\\n', report.target, measure);\n for (const { feed, data } of feeds) {\n const output = Object.entries(data)\n .map(([key, report]) => `${key}: ${report.toString()}`)\n .join('; ');\n console.log(feed, output);\n }\n console.groupEnd();\n }\n }\n};\n\nexport const printTableReports = <R extends ReportTypeList>(reports: TargetReport<R>[]) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log('\\n', report.target, measure);\n const table: Record<string, unknown> = {};\n for (const { feed, data } of feeds) {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n console.table(table);\n }\n }\n};\n\nexport const printJSONReports = <R extends ReportTypeList>(reports: TargetReport<R>[], padding?: number) => {\n const output = {} as Record<string, Record<string, Record<string, string>>>;\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n const row = {} as Record<string, Record<string, string>>;\n for (const { feed, data } of feeds) {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n"],"names":["cpus","pathToFileURL","createExecutor","DEFAULT_CYCLES","DEFAULT_WORKERS","length","AsyncFunction","DEFAULT_REPORT_TYPES","MeasureContext","pre","post","title","run","Measure","ctx","fn","TargetContext","teardown","measures","setup","Target","measure","push","FeedContext","Benchmark","create","feed","self","target","execute","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","baseUrl","Error","resolvedBaseUrl","process","cwd","href","console","warn","executor","reports","targetReport","measureReport","feeds","data","then","drain","kill","printSimpleReports","report","group","output","Object","entries","map","key","toString","join","log","groupEnd","printTableReports","table","fromEntries","printJSONReports","padding","row","JSON","stringify"],"mappings":"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"}
|