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/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
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;\nconst BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');\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>(options: ExecutorOptions<R>): Promise<TargetReport<R>[]> {\n const {\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 } = options;\n if (this.#executed) {\n throw new Error(\"Benchmark is executed and can't be reused\");\n }\n this.#executed = true;\n const benchmarkUrl = (options as unknown as Record<symbol, unknown>)[BENCHMARK_URL];\n\n const executor = createExecutor<unknown, TInput, R>({\n workers,\n warmupCycles,\n maxCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n reportTypes,\n [BENCHMARK_URL]: benchmarkUrl,\n } as Required<ExecutorOptions<R>>);\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","BENCHMARK_URL","Symbol","for","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","options","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","Error","benchmarkUrl","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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"}
|
package/build/runner.cjs
CHANGED
|
@@ -13,17 +13,47 @@ const _typescjs = require("./types.cjs");
|
|
|
13
13
|
const _gcwatchercjs = require("./gc-watcher.cjs");
|
|
14
14
|
const COMPLETE_VALUE = 100_00;
|
|
15
15
|
const hr = process.hrtime.bigint.bind(process.hrtime);
|
|
16
|
-
const
|
|
16
|
+
const sink = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
17
|
+
const consume = (value)=>{
|
|
18
|
+
let payload = 0;
|
|
19
|
+
switch(typeof value){
|
|
20
|
+
case 'number':
|
|
21
|
+
payload = Number.isFinite(value) ? Math.trunc(value) : 0;
|
|
22
|
+
break;
|
|
23
|
+
case 'bigint':
|
|
24
|
+
payload = Number(value & 0xffff_ffffn);
|
|
25
|
+
break;
|
|
26
|
+
case 'string':
|
|
27
|
+
payload = value.length;
|
|
28
|
+
break;
|
|
29
|
+
case 'boolean':
|
|
30
|
+
payload = value ? 1 : 0;
|
|
31
|
+
break;
|
|
32
|
+
case 'object':
|
|
33
|
+
payload = value === null ? 0 : 1;
|
|
34
|
+
break;
|
|
35
|
+
case 'function':
|
|
36
|
+
payload = 1;
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
payload = -1;
|
|
40
|
+
}
|
|
41
|
+
Atomics.xor(sink, 0, payload);
|
|
42
|
+
};
|
|
43
|
+
const runSync = (run, overhead)=>{
|
|
17
44
|
return (...args)=>{
|
|
18
45
|
const start = hr();
|
|
19
|
-
run(...args);
|
|
20
|
-
|
|
46
|
+
const result = run(...args);
|
|
47
|
+
consume(result);
|
|
48
|
+
const duration = hr() - start;
|
|
49
|
+
return duration > overhead ? duration - overhead : 0n;
|
|
21
50
|
};
|
|
22
51
|
};
|
|
23
52
|
const runAsync = (run)=>{
|
|
24
53
|
return async (...args)=>{
|
|
25
54
|
const start = hr();
|
|
26
|
-
await run(...args);
|
|
55
|
+
const result = await run(...args);
|
|
56
|
+
consume(result);
|
|
27
57
|
return hr() - start;
|
|
28
58
|
};
|
|
29
59
|
};
|
|
@@ -37,16 +67,62 @@ const GC_STRIDE = 32;
|
|
|
37
67
|
const OUTLIER_MULTIPLIER = 4;
|
|
38
68
|
const OUTLIER_IQR_MULTIPLIER = 3;
|
|
39
69
|
const OUTLIER_WINDOW = 64;
|
|
40
|
-
const
|
|
70
|
+
const OUTLIER_ABS_THRESHOLD_NS = 10_000;
|
|
71
|
+
const BASELINE_SAMPLES = 16;
|
|
72
|
+
const OUTLIER_SCRATCH = new Float64Array(OUTLIER_WINDOW);
|
|
73
|
+
const measureTimerOverhead = ()=>{
|
|
74
|
+
let total = 0n;
|
|
75
|
+
for(let i = 0; i < BASELINE_SAMPLES; i++){
|
|
76
|
+
const start = hr();
|
|
77
|
+
consume(0);
|
|
78
|
+
total += hr() - start;
|
|
79
|
+
}
|
|
80
|
+
return total / BigInt(BASELINE_SAMPLES);
|
|
81
|
+
};
|
|
82
|
+
const collectSample = async ({ batchSize, run, runRaw, runIsAsync, pre, preIsAsync, post, postIsAsync, context, data, nextNonce })=>{
|
|
83
|
+
const canBatchTime = !runIsAsync && !pre && !post;
|
|
84
|
+
if (canBatchTime) {
|
|
85
|
+
const batchStart = hr();
|
|
86
|
+
if (nextNonce) {
|
|
87
|
+
for(let b = 0; b < batchSize; b++){
|
|
88
|
+
consume(runRaw(context, data, nextNonce()));
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
for(let b = 0; b < batchSize; b++){
|
|
92
|
+
consume(runRaw(context, data));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return (hr() - batchStart) / BigInt(batchSize);
|
|
96
|
+
}
|
|
41
97
|
let sampleDuration = 0n;
|
|
42
98
|
for(let b = 0; b < batchSize; b++){
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
99
|
+
if (pre) {
|
|
100
|
+
if (preIsAsync) {
|
|
101
|
+
await pre(context, data);
|
|
102
|
+
} else {
|
|
103
|
+
pre(context, data);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (runIsAsync) {
|
|
107
|
+
const runAsyncFn = run;
|
|
108
|
+
const duration = nextNonce ? await runAsyncFn(context, data, nextNonce()) : await runAsyncFn(context, data);
|
|
109
|
+
sampleDuration += duration;
|
|
110
|
+
} else {
|
|
111
|
+
const runSyncFn = run;
|
|
112
|
+
const duration = nextNonce ? runSyncFn(context, data, nextNonce()) : runSyncFn(context, data);
|
|
113
|
+
sampleDuration += duration;
|
|
114
|
+
}
|
|
115
|
+
if (post) {
|
|
116
|
+
if (postIsAsync) {
|
|
117
|
+
await post(context, data);
|
|
118
|
+
} else {
|
|
119
|
+
post(context, data);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
46
122
|
}
|
|
47
123
|
return sampleDuration / BigInt(batchSize);
|
|
48
124
|
};
|
|
49
|
-
const tuneParameters = async ({ initialBatch, run, pre, post, context, data, minCycles, relThreshold, maxCycles })=>{
|
|
125
|
+
const tuneParameters = async ({ initialBatch, run, runRaw, runIsAsync, pre, preIsAsync, post, postIsAsync, context, data, minCycles, relThreshold, maxCycles, nextNonce })=>{
|
|
50
126
|
let batchSize = initialBatch;
|
|
51
127
|
let bestCv = Number.POSITIVE_INFINITY;
|
|
52
128
|
let bestBatch = batchSize;
|
|
@@ -54,7 +130,19 @@ const tuneParameters = async ({ initialBatch, run, pre, post, context, data, min
|
|
|
54
130
|
const samples = [];
|
|
55
131
|
const sampleCount = Math.min(8, maxCycles);
|
|
56
132
|
for(let s = 0; s < sampleCount; s++){
|
|
57
|
-
const duration = await collectSample(
|
|
133
|
+
const duration = await collectSample({
|
|
134
|
+
batchSize,
|
|
135
|
+
run,
|
|
136
|
+
runRaw,
|
|
137
|
+
runIsAsync,
|
|
138
|
+
pre,
|
|
139
|
+
preIsAsync,
|
|
140
|
+
post,
|
|
141
|
+
postIsAsync,
|
|
142
|
+
context,
|
|
143
|
+
data,
|
|
144
|
+
nextNonce
|
|
145
|
+
});
|
|
58
146
|
samples.push(Number(duration));
|
|
59
147
|
}
|
|
60
148
|
const mean = samples.reduce((acc, v)=>acc + v, 0) / samples.length;
|
|
@@ -134,15 +222,17 @@ const medianAndIqr = (arr)=>{
|
|
|
134
222
|
median: 0,
|
|
135
223
|
iqr: 0
|
|
136
224
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
225
|
+
for(let i = 0; i < arr.length; i++){
|
|
226
|
+
OUTLIER_SCRATCH[i] = arr[i];
|
|
227
|
+
}
|
|
228
|
+
const view = OUTLIER_SCRATCH.subarray(0, arr.length);
|
|
229
|
+
view.sort();
|
|
230
|
+
const mid = Math.floor(view.length / 2);
|
|
231
|
+
const median = view.length % 2 === 0 ? (view[mid - 1] + view[mid]) / 2 : view[mid];
|
|
232
|
+
const q1Idx = Math.floor(view.length * 0.25);
|
|
233
|
+
const q3Idx = Math.floor(view.length * 0.75);
|
|
234
|
+
const q1 = view[q1Idx];
|
|
235
|
+
const q3 = view[q3Idx];
|
|
146
236
|
return {
|
|
147
237
|
median,
|
|
148
238
|
iqr: q3 - q1
|
|
@@ -162,56 +252,132 @@ const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data, warmup
|
|
|
162
252
|
control[_typescjs.Control.PROGRESS] = 0;
|
|
163
253
|
control[_typescjs.Control.COMPLETE] = 255;
|
|
164
254
|
const context = await setup?.();
|
|
255
|
+
const input = data;
|
|
165
256
|
const maxCycles = durations.length;
|
|
166
257
|
const gcWatcher = gcObserver ? new _gcwatchercjs.GCWatcher() : null;
|
|
167
258
|
const gcTracker = gcObserver ? createGCTracker() : null;
|
|
168
259
|
try {
|
|
169
|
-
|
|
260
|
+
let preIsAsync = false;
|
|
261
|
+
if (pre) {
|
|
262
|
+
const preResult = pre(context, input);
|
|
263
|
+
preIsAsync = isThenable(preResult);
|
|
264
|
+
if (preIsAsync) {
|
|
265
|
+
await preResult;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
170
268
|
const probeStart = hr();
|
|
171
|
-
const probeResult = runRaw(context,
|
|
172
|
-
const
|
|
173
|
-
if (
|
|
174
|
-
await probeResult;
|
|
269
|
+
const probeResult = runRaw(context, input);
|
|
270
|
+
const runIsAsync = isThenable(probeResult);
|
|
271
|
+
if (runIsAsync) {
|
|
272
|
+
const resolved = await probeResult;
|
|
273
|
+
consume(resolved);
|
|
274
|
+
} else {
|
|
275
|
+
consume(probeResult);
|
|
276
|
+
}
|
|
277
|
+
const durationProbeRaw = hr() - probeStart;
|
|
278
|
+
let postIsAsync = false;
|
|
279
|
+
if (post) {
|
|
280
|
+
const postResult = post(context, input);
|
|
281
|
+
postIsAsync = isThenable(postResult);
|
|
282
|
+
if (postIsAsync) {
|
|
283
|
+
await postResult;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const timerOverhead = runIsAsync ? 0n : measureTimerOverhead();
|
|
287
|
+
let durationProbe = runIsAsync ? durationProbeRaw : durationProbeRaw > timerOverhead ? durationProbeRaw - timerOverhead : 0n;
|
|
288
|
+
const shouldPerturbInput = process.env.OVERTAKE_PERTURB_INPUT === '1';
|
|
289
|
+
let nonce = 0;
|
|
290
|
+
const nextNonce = shouldPerturbInput ? ()=>{
|
|
291
|
+
nonce = nonce + 1 | 0;
|
|
292
|
+
return nonce;
|
|
293
|
+
} : null;
|
|
294
|
+
if (!runIsAsync && !pre && !post) {
|
|
295
|
+
const batchProbeSize = 10_000;
|
|
296
|
+
const batchProbeStart = hr();
|
|
297
|
+
if (nextNonce) {
|
|
298
|
+
for(let i = 0; i < batchProbeSize; i++){
|
|
299
|
+
consume(runRaw(context, input, nextNonce()));
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
for(let i = 0; i < batchProbeSize; i++){
|
|
303
|
+
consume(runRaw(context, input));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
durationProbe = (hr() - batchProbeStart) / BigInt(batchProbeSize);
|
|
175
307
|
}
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
const run =
|
|
308
|
+
const runTimedSync = runIsAsync ? null : runSync(runRaw, timerOverhead);
|
|
309
|
+
const runTimedAsync = runIsAsync ? runAsync(runRaw) : null;
|
|
310
|
+
const run = runIsAsync ? runTimedAsync : runTimedSync;
|
|
311
|
+
const runOnceSync = runIsAsync ? null : nextNonce ? (ctx, dataValue)=>runTimedSync(ctx, dataValue, nextNonce()) : runTimedSync;
|
|
312
|
+
const runOnceAsync = runIsAsync ? nextNonce ? (ctx, dataValue)=>runTimedAsync(ctx, dataValue, nextNonce()) : runTimedAsync : null;
|
|
313
|
+
const preSync = preIsAsync ? null : pre;
|
|
314
|
+
const preAsync = preIsAsync ? pre : null;
|
|
315
|
+
const postSync = postIsAsync ? null : post;
|
|
316
|
+
const postAsync = postIsAsync ? post : null;
|
|
179
317
|
const durationPerRun = durationProbe === 0n ? 1n : durationProbe;
|
|
180
318
|
const suggestedBatch = Number(TARGET_SAMPLE_NS / durationPerRun);
|
|
181
|
-
const
|
|
319
|
+
const minBatchForFastOps = durationProbe < 100n ? 100_000 : 1;
|
|
320
|
+
const initialBatchSize = Math.min(MAX_BATCH, Math.max(minBatchForFastOps, suggestedBatch));
|
|
182
321
|
const tuned = await tuneParameters({
|
|
183
322
|
initialBatch: initialBatchSize,
|
|
184
323
|
run,
|
|
324
|
+
runRaw,
|
|
325
|
+
runIsAsync,
|
|
185
326
|
pre,
|
|
327
|
+
preIsAsync,
|
|
186
328
|
post,
|
|
329
|
+
postIsAsync,
|
|
187
330
|
context,
|
|
188
|
-
data:
|
|
331
|
+
data: input,
|
|
189
332
|
minCycles,
|
|
190
333
|
relThreshold,
|
|
191
|
-
maxCycles
|
|
334
|
+
maxCycles,
|
|
335
|
+
nextNonce
|
|
192
336
|
});
|
|
193
337
|
let batchSize = tuned.batchSize;
|
|
194
338
|
minCycles = tuned.minCycles;
|
|
195
339
|
relThreshold = tuned.relThreshold;
|
|
196
|
-
const warmupStart =
|
|
340
|
+
const warmupStart = _nodeperf_hooks.performance.now();
|
|
197
341
|
let warmupRemaining = warmupCycles;
|
|
198
342
|
const warmupWindow = [];
|
|
199
343
|
const warmupCap = Math.max(warmupCycles, Math.min(maxCycles, warmupCycles * 4 || 1000));
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
344
|
+
const canBatchTime = !runIsAsync && !preSync && !preAsync && !postSync && !postAsync;
|
|
345
|
+
const runWarmup = async ()=>{
|
|
346
|
+
if (canBatchTime) {
|
|
347
|
+
const batchStart = hr();
|
|
348
|
+
if (nextNonce) {
|
|
349
|
+
for(let b = 0; b < batchSize; b++){
|
|
350
|
+
consume(runRaw(context, input, nextNonce()));
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
for(let b = 0; b < batchSize; b++){
|
|
354
|
+
consume(runRaw(context, input));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return (hr() - batchStart) / BigInt(batchSize);
|
|
358
|
+
}
|
|
359
|
+
if (preSync) {
|
|
360
|
+
preSync(context, input);
|
|
361
|
+
} else if (preAsync) {
|
|
362
|
+
await preAsync(context, input);
|
|
363
|
+
}
|
|
364
|
+
const duration = runIsAsync ? await runOnceAsync(context, input) : runOnceSync(context, input);
|
|
365
|
+
if (postSync) {
|
|
366
|
+
postSync(context, input);
|
|
367
|
+
} else if (postAsync) {
|
|
368
|
+
await postAsync(context, input);
|
|
369
|
+
}
|
|
370
|
+
return duration;
|
|
371
|
+
};
|
|
372
|
+
while(_nodeperf_hooks.performance.now() - warmupStart < 1_000 && warmupRemaining > 0){
|
|
373
|
+
const duration = await runWarmup();
|
|
374
|
+
pushWindow(warmupWindow, Number(duration), warmupCap);
|
|
206
375
|
warmupRemaining--;
|
|
207
376
|
}
|
|
208
377
|
let warmupDone = 0;
|
|
209
378
|
while(warmupDone < warmupRemaining){
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
await run(context, data);
|
|
213
|
-
await post?.(context, data);
|
|
214
|
-
pushWindow(warmupWindow, Number(hr() - start), warmupCap);
|
|
379
|
+
const duration = await runWarmup();
|
|
380
|
+
pushWindow(warmupWindow, Number(duration), warmupCap);
|
|
215
381
|
warmupDone++;
|
|
216
382
|
if (global.gc && warmupDone % GC_STRIDE === 0) {
|
|
217
383
|
global.gc();
|
|
@@ -222,45 +388,80 @@ const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data, warmup
|
|
|
222
388
|
if (cv <= relThreshold * 2) {
|
|
223
389
|
break;
|
|
224
390
|
}
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
await run(context, data);
|
|
228
|
-
await post?.(context, data);
|
|
229
|
-
pushWindow(warmupWindow, Number(hr() - start), warmupCap);
|
|
391
|
+
const duration = await runWarmup();
|
|
392
|
+
pushWindow(warmupWindow, Number(duration), warmupCap);
|
|
230
393
|
}
|
|
231
394
|
let i = 0;
|
|
232
395
|
let mean = 0n;
|
|
233
396
|
let m2 = 0n;
|
|
234
397
|
const outlierWindow = [];
|
|
398
|
+
let skipped = 0;
|
|
399
|
+
const maxSkipped = maxCycles * 10;
|
|
400
|
+
let disableFiltering = false;
|
|
235
401
|
while(true){
|
|
236
402
|
if (i >= maxCycles) break;
|
|
403
|
+
if (!disableFiltering && skipped >= maxSkipped) {
|
|
404
|
+
console.error(`Warning: ${skipped} samples skipped due to noise/outlier detection. ` + `Disabling filtering for remaining samples. Results may have higher variance.`);
|
|
405
|
+
disableFiltering = true;
|
|
406
|
+
}
|
|
407
|
+
if (global.gc && i > 0 && i % GC_STRIDE === 0) {
|
|
408
|
+
global.gc();
|
|
409
|
+
}
|
|
237
410
|
const gcMarker = gcWatcher?.start();
|
|
238
411
|
const sampleStart = _nodeperf_hooks.performance.now();
|
|
239
412
|
let sampleDuration = 0n;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
413
|
+
if (canBatchTime) {
|
|
414
|
+
const batchStart = hr();
|
|
415
|
+
if (nextNonce) {
|
|
416
|
+
for(let b = 0; b < batchSize; b++){
|
|
417
|
+
consume(runRaw(context, input, nextNonce()));
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
for(let b = 0; b < batchSize; b++){
|
|
421
|
+
consume(runRaw(context, input));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const batchDuration = hr() - batchStart;
|
|
425
|
+
sampleDuration = batchDuration / BigInt(batchSize);
|
|
426
|
+
} else {
|
|
427
|
+
for(let b = 0; b < batchSize; b++){
|
|
428
|
+
if (preSync) {
|
|
429
|
+
preSync(context, input);
|
|
430
|
+
} else if (preAsync) {
|
|
431
|
+
await preAsync(context, input);
|
|
432
|
+
}
|
|
433
|
+
const duration = runIsAsync ? await runOnceAsync(context, input) : runOnceSync(context, input);
|
|
434
|
+
sampleDuration += duration;
|
|
435
|
+
if (postSync) {
|
|
436
|
+
postSync(context, input);
|
|
437
|
+
} else if (postAsync) {
|
|
438
|
+
await postAsync(context, input);
|
|
439
|
+
}
|
|
246
440
|
}
|
|
441
|
+
sampleDuration /= BigInt(batchSize);
|
|
247
442
|
}
|
|
248
|
-
sampleDuration /= BigInt(batchSize);
|
|
249
443
|
const sampleEnd = _nodeperf_hooks.performance.now();
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
444
|
+
if (!disableFiltering) {
|
|
445
|
+
const gcNoise = (gcMarker ? gcWatcher.seen(gcMarker) : false) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);
|
|
446
|
+
if (gcNoise) {
|
|
447
|
+
skipped++;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
253
450
|
}
|
|
254
451
|
const durationNumber = Number(sampleDuration);
|
|
255
452
|
pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
453
|
+
if (!disableFiltering) {
|
|
454
|
+
const { median, iqr } = medianAndIqr(outlierWindow);
|
|
455
|
+
const maxAllowed = median + OUTLIER_IQR_MULTIPLIER * iqr || Number.POSITIVE_INFINITY;
|
|
456
|
+
if (outlierWindow.length >= 8 && durationNumber > maxAllowed && durationNumber - median > OUTLIER_ABS_THRESHOLD_NS) {
|
|
457
|
+
skipped++;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const meanNumber = Number(mean);
|
|
461
|
+
if (i >= 8 && meanNumber > 0 && durationNumber > OUTLIER_MULTIPLIER * meanNumber && durationNumber - meanNumber > OUTLIER_ABS_THRESHOLD_NS) {
|
|
462
|
+
skipped++;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
264
465
|
}
|
|
265
466
|
durations[i++] = sampleDuration;
|
|
266
467
|
const delta = sampleDuration - mean;
|
package/build/runner.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { performance, PerformanceObserver } from 'node:perf_hooks';\nimport { Options, Control } from './types.js';\nimport { GCWatcher } from './gc-watcher.js';\nimport { StepFn, MaybePromise } from './types.js';\n\nconst COMPLETE_VALUE = 100_00;\n\nconst hr = process.hrtime.bigint.bind(process.hrtime);\n\nconst runSync = (run: Function) => {\n return (...args: unknown[]) => {\n const start = hr();\n run(...args);\n return hr() - start;\n };\n};\n\nconst runAsync = (run: Function) => {\n return async (...args: unknown[]) => {\n const start = hr();\n await run(...args);\n return hr() - start;\n };\n};\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> => {\n return value !== null && (typeof value === 'object' || typeof value === 'function') && typeof (value as PromiseLike<unknown>).then === 'function';\n};\n\nconst TARGET_SAMPLE_NS = 1_000_000n; // aim for ~1ms per measured sample\nconst MAX_BATCH = 1_048_576;\nconst PROGRESS_STRIDE = 16;\nconst GC_STRIDE = 32;\nconst OUTLIER_MULTIPLIER = 4;\nconst OUTLIER_IQR_MULTIPLIER = 3;\nconst OUTLIER_WINDOW = 64;\n\ntype GCEvent = { start: number; end: number };\n\nconst collectSample = async <TContext, TInput>(\n batchSize: number,\n run: (ctx: TContext, data: TInput) => MaybePromise<bigint>,\n pre: StepFn<TContext, TInput> | undefined,\n post: StepFn<TContext, TInput> | undefined,\n context: TContext,\n data: TInput,\n) => {\n let sampleDuration = 0n;\n for (let b = 0; b < batchSize; b++) {\n await pre?.(context, data);\n sampleDuration += await run(context, data);\n await post?.(context, data);\n }\n return sampleDuration / BigInt(batchSize);\n};\n\nconst tuneParameters = async <TContext, TInput>({\n initialBatch,\n run,\n pre,\n post,\n context,\n data,\n minCycles,\n relThreshold,\n maxCycles,\n}: {\n initialBatch: number;\n run: (ctx: TContext, data: TInput) => MaybePromise<bigint>;\n pre?: StepFn<TContext, TInput>;\n post?: StepFn<TContext, TInput>;\n context: TContext;\n data: TInput;\n minCycles: number;\n relThreshold: number;\n maxCycles: number;\n}) => {\n let batchSize = initialBatch;\n let bestCv = Number.POSITIVE_INFINITY;\n let bestBatch = batchSize;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const samples: number[] = [];\n const sampleCount = Math.min(8, maxCycles);\n for (let s = 0; s < sampleCount; s++) {\n const duration = await collectSample(batchSize, run, pre, post, context, data);\n samples.push(Number(duration));\n }\n const mean = samples.reduce((acc, v) => acc + v, 0) / samples.length;\n const variance = samples.reduce((acc, v) => acc + (v - mean) * (v - mean), 0) / Math.max(1, samples.length - 1);\n const stddev = Math.sqrt(variance);\n const cv = mean === 0 ? Number.POSITIVE_INFINITY : stddev / mean;\n\n if (cv < bestCv) {\n bestCv = cv;\n bestBatch = batchSize;\n }\n\n if (cv <= relThreshold || batchSize >= MAX_BATCH) {\n break;\n }\n batchSize = Math.min(MAX_BATCH, batchSize * 2);\n }\n\n const tunedRel = bestCv < relThreshold ? Math.max(bestCv * 1.5, relThreshold * 0.5) : relThreshold;\n const tunedMin = Math.min(maxCycles, Math.max(minCycles, Math.ceil(minCycles * Math.max(1, bestCv / (relThreshold || 1e-6)))));\n\n return { batchSize: bestBatch, relThreshold: tunedRel, minCycles: tunedMin };\n};\n\nconst createGCTracker = () => {\n if (process.env.OVERTAKE_GC_OBSERVER !== '1') {\n return null;\n }\n if (typeof PerformanceObserver === 'undefined') {\n return null;\n }\n\n const events: GCEvent[] = [];\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n events.push({ start: entry.startTime, end: entry.startTime + entry.duration });\n }\n });\n\n try {\n observer.observe({ entryTypes: ['gc'] });\n } catch {\n return null;\n }\n\n const overlaps = (start: number, end: number) => {\n let noisy = false;\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i];\n if (event.end < start - 5_000) {\n events.splice(i, 1);\n continue;\n }\n if (event.start <= end && event.end >= start) {\n noisy = true;\n }\n }\n return noisy;\n };\n\n const dispose = () => observer.disconnect();\n\n return { overlaps, dispose };\n};\n\nconst pushWindow = (arr: number[], value: number, cap: number) => {\n if (arr.length === cap) {\n arr.shift();\n }\n arr.push(value);\n};\n\nconst medianAndIqr = (arr: number[]) => {\n if (arr.length === 0) return { median: 0, iqr: 0 };\n const sorted = [...arr].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n const median = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];\n const q1Idx = Math.floor(sorted.length * 0.25);\n const q3Idx = Math.floor(sorted.length * 0.75);\n const q1 = sorted[q1Idx];\n const q3 = sorted[q3Idx];\n return { median, iqr: q3 - q1 };\n};\n\nconst windowCv = (arr: number[]) => {\n if (arr.length < 2) return Number.POSITIVE_INFINITY;\n const mean = arr.reduce((a, v) => a + v, 0) / arr.length;\n const variance = arr.reduce((a, v) => a + (v - mean) * (v - mean), 0) / (arr.length - 1);\n const stddev = Math.sqrt(variance);\n return mean === 0 ? Number.POSITIVE_INFINITY : stddev / mean;\n};\n\nexport const benchmark = async <TContext, TInput>({\n setup,\n teardown,\n pre,\n run: runRaw,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = false,\n\n durationsSAB,\n controlSAB,\n}: Required<Options<TContext, TInput>>) => {\n const durations = new BigUint64Array(durationsSAB);\n const control = new Int32Array(controlSAB);\n\n control[Control.INDEX] = 0;\n control[Control.PROGRESS] = 0;\n control[Control.COMPLETE] = 255;\n\n const context = (await setup?.()) as TContext;\n const maxCycles = durations.length;\n const gcWatcher = gcObserver ? new GCWatcher() : null;\n const gcTracker = gcObserver ? createGCTracker() : null;\n\n try {\n // classify sync/async and capture initial duration\n await pre?.(context, data!);\n const probeStart = hr();\n const probeResult = runRaw(context, data!);\n const isAsync = isThenable(probeResult);\n if (isAsync) {\n await probeResult;\n }\n const durationProbe = hr() - probeStart;\n await post?.(context, data!);\n\n const run = isAsync ? runAsync(runRaw) : runSync(runRaw);\n\n // choose batch size to amortize timer overhead\n const durationPerRun = durationProbe === 0n ? 1n : durationProbe;\n const suggestedBatch = Number(TARGET_SAMPLE_NS / durationPerRun);\n const initialBatchSize = Math.min(MAX_BATCH, Math.max(1, suggestedBatch));\n\n // auto-tune based on warmup samples\n const tuned = await tuneParameters({\n initialBatch: initialBatchSize,\n run,\n pre,\n post,\n context,\n data: data as TInput,\n minCycles,\n relThreshold,\n maxCycles,\n });\n let batchSize = tuned.batchSize;\n minCycles = tuned.minCycles;\n relThreshold = tuned.relThreshold;\n\n // warmup: run until requested cycles, adapt if unstable\n const warmupStart = Date.now();\n let warmupRemaining = warmupCycles;\n const warmupWindow: number[] = [];\n const warmupCap = Math.max(warmupCycles, Math.min(maxCycles, warmupCycles * 4 || 1000));\n\n while (Date.now() - warmupStart < 1_000 && warmupRemaining > 0) {\n const start = hr();\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n pushWindow(warmupWindow, Number(hr() - start), warmupCap);\n warmupRemaining--;\n }\n let warmupDone = 0;\n while (warmupDone < warmupRemaining) {\n const start = hr();\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n pushWindow(warmupWindow, Number(hr() - start), warmupCap);\n warmupDone++;\n if (global.gc && warmupDone % GC_STRIDE === 0) {\n global.gc();\n }\n }\n while (warmupWindow.length >= 8 && warmupWindow.length < warmupCap) {\n const cv = windowCv(warmupWindow);\n if (cv <= relThreshold * 2) {\n break;\n }\n const start = hr();\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n pushWindow(warmupWindow, Number(hr() - start), warmupCap);\n }\n\n let i = 0;\n let mean = 0n;\n let m2 = 0n;\n const outlierWindow: number[] = [];\n\n while (true) {\n if (i >= maxCycles) break;\n\n const gcMarker = gcWatcher?.start();\n const sampleStart = performance.now();\n let sampleDuration = 0n;\n for (let b = 0; b < batchSize; b++) {\n await pre?.(context, data!);\n sampleDuration += await run(context, data);\n await post?.(context, data!);\n if (global.gc && (i + b) % GC_STRIDE === 0) {\n global.gc();\n }\n }\n\n // normalize by batch size\n sampleDuration /= BigInt(batchSize);\n\n const sampleEnd = performance.now();\n const gcNoise = (gcMarker ? gcWatcher!.seen(gcMarker) : false) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);\n if (gcNoise) {\n continue;\n }\n\n const durationNumber = Number(sampleDuration);\n pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);\n const { median, iqr } = medianAndIqr(outlierWindow);\n const maxAllowed = median + OUTLIER_IQR_MULTIPLIER * iqr || Number.POSITIVE_INFINITY;\n if (outlierWindow.length >= 8 && durationNumber > maxAllowed) {\n continue;\n }\n\n const meanNumber = Number(mean);\n if (i >= 8 && meanNumber > 0 && durationNumber > OUTLIER_MULTIPLIER * meanNumber) {\n continue;\n }\n\n durations[i++] = sampleDuration;\n const delta = sampleDuration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (sampleDuration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n if (i % PROGRESS_STRIDE === 0) {\n control[Control.PROGRESS] = progress;\n }\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n gcTracker?.dispose?.();\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["benchmark","COMPLETE_VALUE","hr","process","hrtime","bigint","bind","runSync","run","args","start","runAsync","isThenable","value","then","TARGET_SAMPLE_NS","MAX_BATCH","PROGRESS_STRIDE","GC_STRIDE","OUTLIER_MULTIPLIER","OUTLIER_IQR_MULTIPLIER","OUTLIER_WINDOW","collectSample","batchSize","pre","post","context","data","sampleDuration","b","BigInt","tuneParameters","initialBatch","minCycles","relThreshold","maxCycles","bestCv","Number","POSITIVE_INFINITY","bestBatch","attempt","samples","sampleCount","Math","min","s","duration","push","mean","reduce","acc","v","length","variance","max","stddev","sqrt","cv","tunedRel","tunedMin","ceil","createGCTracker","env","OVERTAKE_GC_OBSERVER","PerformanceObserver","events","observer","list","entry","getEntries","startTime","end","observe","entryTypes","overlaps","noisy","i","event","splice","dispose","disconnect","pushWindow","arr","cap","shift","medianAndIqr","median","iqr","sorted","sort","a","mid","floor","q1Idx","q3Idx","q1","q3","windowCv","setup","teardown","runRaw","warmupCycles","absThreshold","gcObserver","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","Control","INDEX","PROGRESS","COMPLETE","gcWatcher","GCWatcher","gcTracker","probeStart","probeResult","isAsync","durationProbe","durationPerRun","suggestedBatch","initialBatchSize","tuned","warmupStart","Date","now","warmupRemaining","warmupWindow","warmupCap","warmupDone","global","gc","m2","outlierWindow","gcMarker","sampleStart","performance","sampleEnd","gcNoise","seen","durationNumber","maxAllowed","meanNumber","delta","progress","meanNum","cov","e","console","error","stack"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { performance, PerformanceObserver } from 'node:perf_hooks';\nimport { Options, Control } from './types.js';\nimport { GCWatcher } from './gc-watcher.js';\nimport { StepFn } from './types.js';\n\nconst COMPLETE_VALUE = 100_00;\n\nconst hr = process.hrtime.bigint.bind(process.hrtime);\n\nconst sink = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));\nconst consume = (value: unknown) => {\n let payload = 0;\n switch (typeof value) {\n case 'number':\n payload = Number.isFinite(value) ? Math.trunc(value) : 0;\n break;\n case 'bigint':\n payload = Number(value & 0xffff_ffffn);\n break;\n case 'string':\n payload = value.length;\n break;\n case 'boolean':\n payload = value ? 1 : 0;\n break;\n case 'object':\n payload = value === null ? 0 : 1;\n break;\n case 'function':\n payload = 1;\n break;\n default:\n payload = -1;\n }\n Atomics.xor(sink, 0, payload);\n};\n\nconst runSync = (run: Function, overhead: bigint) => {\n return (...args: unknown[]) => {\n const start = hr();\n const result = run(...args);\n consume(result);\n const duration = hr() - start;\n return duration > overhead ? duration - overhead : 0n;\n };\n};\n\nconst runAsync = (run: Function) => {\n return async (...args: unknown[]) => {\n const start = hr();\n const result = await run(...args);\n consume(result);\n return hr() - start;\n };\n};\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> => {\n return value !== null && (typeof value === 'object' || typeof value === 'function') && typeof (value as PromiseLike<unknown>).then === 'function';\n};\n\nconst TARGET_SAMPLE_NS = 1_000_000n; // aim for ~1ms per measured sample\nconst MAX_BATCH = 1_048_576;\nconst PROGRESS_STRIDE = 16;\nconst GC_STRIDE = 32;\nconst OUTLIER_MULTIPLIER = 4;\nconst OUTLIER_IQR_MULTIPLIER = 3;\nconst OUTLIER_WINDOW = 64;\nconst OUTLIER_ABS_THRESHOLD_NS = 10_000;\nconst BASELINE_SAMPLES = 16;\nconst OUTLIER_SCRATCH = new Float64Array(OUTLIER_WINDOW);\n\ntype GCEvent = { start: number; end: number };\ntype RunTimedSync<TContext, TInput> = (ctx: TContext, data: TInput, nonce?: number) => bigint;\ntype RunTimedAsync<TContext, TInput> = (ctx: TContext, data: TInput, nonce?: number) => Promise<bigint>;\n\nconst measureTimerOverhead = () => {\n let total = 0n;\n for (let i = 0; i < BASELINE_SAMPLES; i++) {\n const start = hr();\n consume(0);\n total += hr() - start;\n }\n return total / BigInt(BASELINE_SAMPLES);\n};\n\nconst collectSample = async <TContext, TInput>({\n batchSize,\n run,\n runRaw,\n runIsAsync,\n pre,\n preIsAsync,\n post,\n postIsAsync,\n context,\n data,\n nextNonce,\n}: {\n batchSize: number;\n run: RunTimedSync<TContext, TInput> | RunTimedAsync<TContext, TInput>;\n runRaw: StepFn<TContext, TInput>;\n runIsAsync: boolean;\n pre: StepFn<TContext, TInput> | undefined;\n preIsAsync: boolean;\n post: StepFn<TContext, TInput> | undefined;\n postIsAsync: boolean;\n context: TContext;\n data: TInput;\n nextNonce: (() => number) | null;\n}) => {\n const canBatchTime = !runIsAsync && !pre && !post;\n if (canBatchTime) {\n const batchStart = hr();\n if (nextNonce) {\n for (let b = 0; b < batchSize; b++) {\n consume((runRaw as Function)(context, data, nextNonce()));\n }\n } else {\n for (let b = 0; b < batchSize; b++) {\n consume(runRaw(context, data));\n }\n }\n return (hr() - batchStart) / BigInt(batchSize);\n }\n\n let sampleDuration = 0n;\n for (let b = 0; b < batchSize; b++) {\n if (pre) {\n if (preIsAsync) {\n await pre(context, data);\n } else {\n pre(context, data);\n }\n }\n\n if (runIsAsync) {\n const runAsyncFn = run as RunTimedAsync<TContext, TInput>;\n const duration = nextNonce ? await runAsyncFn(context, data, nextNonce()) : await runAsyncFn(context, data);\n sampleDuration += duration;\n } else {\n const runSyncFn = run as RunTimedSync<TContext, TInput>;\n const duration = nextNonce ? runSyncFn(context, data, nextNonce()) : runSyncFn(context, data);\n sampleDuration += duration;\n }\n\n if (post) {\n if (postIsAsync) {\n await post(context, data);\n } else {\n post(context, data);\n }\n }\n }\n return sampleDuration / BigInt(batchSize);\n};\n\nconst tuneParameters = async <TContext, TInput>({\n initialBatch,\n run,\n runRaw,\n runIsAsync,\n pre,\n preIsAsync,\n post,\n postIsAsync,\n context,\n data,\n minCycles,\n relThreshold,\n maxCycles,\n nextNonce,\n}: {\n initialBatch: number;\n run: RunTimedSync<TContext, TInput> | RunTimedAsync<TContext, TInput>;\n runRaw: StepFn<TContext, TInput>;\n runIsAsync: boolean;\n pre?: StepFn<TContext, TInput>;\n preIsAsync: boolean;\n post?: StepFn<TContext, TInput>;\n postIsAsync: boolean;\n context: TContext;\n data: TInput;\n minCycles: number;\n relThreshold: number;\n maxCycles: number;\n nextNonce: (() => number) | null;\n}) => {\n let batchSize = initialBatch;\n let bestCv = Number.POSITIVE_INFINITY;\n let bestBatch = batchSize;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const samples: number[] = [];\n const sampleCount = Math.min(8, maxCycles);\n for (let s = 0; s < sampleCount; s++) {\n const duration = await collectSample({\n batchSize,\n run,\n runRaw,\n runIsAsync,\n pre,\n preIsAsync,\n post,\n postIsAsync,\n context,\n data,\n nextNonce,\n });\n samples.push(Number(duration));\n }\n const mean = samples.reduce((acc, v) => acc + v, 0) / samples.length;\n const variance = samples.reduce((acc, v) => acc + (v - mean) * (v - mean), 0) / Math.max(1, samples.length - 1);\n const stddev = Math.sqrt(variance);\n const cv = mean === 0 ? Number.POSITIVE_INFINITY : stddev / mean;\n\n if (cv < bestCv) {\n bestCv = cv;\n bestBatch = batchSize;\n }\n\n if (cv <= relThreshold || batchSize >= MAX_BATCH) {\n break;\n }\n batchSize = Math.min(MAX_BATCH, batchSize * 2);\n }\n\n const tunedRel = bestCv < relThreshold ? Math.max(bestCv * 1.5, relThreshold * 0.5) : relThreshold;\n const tunedMin = Math.min(maxCycles, Math.max(minCycles, Math.ceil(minCycles * Math.max(1, bestCv / (relThreshold || 1e-6)))));\n\n return { batchSize: bestBatch, relThreshold: tunedRel, minCycles: tunedMin };\n};\n\nconst createGCTracker = () => {\n if (process.env.OVERTAKE_GC_OBSERVER !== '1') {\n return null;\n }\n if (typeof PerformanceObserver === 'undefined') {\n return null;\n }\n\n const events: GCEvent[] = [];\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n events.push({ start: entry.startTime, end: entry.startTime + entry.duration });\n }\n });\n\n try {\n observer.observe({ entryTypes: ['gc'] });\n } catch {\n return null;\n }\n\n const overlaps = (start: number, end: number) => {\n let noisy = false;\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i];\n if (event.end < start - 5_000) {\n events.splice(i, 1);\n continue;\n }\n if (event.start <= end && event.end >= start) {\n noisy = true;\n }\n }\n return noisy;\n };\n\n const dispose = () => observer.disconnect();\n\n return { overlaps, dispose };\n};\n\nconst pushWindow = (arr: number[], value: number, cap: number) => {\n if (arr.length === cap) {\n arr.shift();\n }\n arr.push(value);\n};\n\nconst medianAndIqr = (arr: number[]) => {\n if (arr.length === 0) return { median: 0, iqr: 0 };\n for (let i = 0; i < arr.length; i++) {\n OUTLIER_SCRATCH[i] = arr[i];\n }\n const view = OUTLIER_SCRATCH.subarray(0, arr.length);\n view.sort();\n const mid = Math.floor(view.length / 2);\n const median = view.length % 2 === 0 ? (view[mid - 1] + view[mid]) / 2 : view[mid];\n const q1Idx = Math.floor(view.length * 0.25);\n const q3Idx = Math.floor(view.length * 0.75);\n const q1 = view[q1Idx];\n const q3 = view[q3Idx];\n return { median, iqr: q3 - q1 };\n};\n\nconst windowCv = (arr: number[]) => {\n if (arr.length < 2) return Number.POSITIVE_INFINITY;\n const mean = arr.reduce((a, v) => a + v, 0) / arr.length;\n const variance = arr.reduce((a, v) => a + (v - mean) * (v - mean), 0) / (arr.length - 1);\n const stddev = Math.sqrt(variance);\n return mean === 0 ? Number.POSITIVE_INFINITY : stddev / mean;\n};\n\nexport const benchmark = async <TContext, TInput>({\n setup,\n teardown,\n pre,\n run: runRaw,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = false,\n\n durationsSAB,\n controlSAB,\n}: Required<Options<TContext, TInput>>) => {\n const durations = new BigUint64Array(durationsSAB);\n const control = new Int32Array(controlSAB);\n\n control[Control.INDEX] = 0;\n control[Control.PROGRESS] = 0;\n control[Control.COMPLETE] = 255;\n\n const context = (await setup?.()) as TContext;\n const input = data as TInput;\n const maxCycles = durations.length;\n const gcWatcher = gcObserver ? new GCWatcher() : null;\n const gcTracker = gcObserver ? createGCTracker() : null;\n\n try {\n // classify sync/async and capture initial duration\n let preIsAsync = false;\n if (pre) {\n const preResult = pre(context, input);\n preIsAsync = isThenable(preResult);\n if (preIsAsync) {\n await preResult;\n }\n }\n\n const probeStart = hr();\n const probeResult = runRaw(context, input);\n const runIsAsync = isThenable(probeResult);\n if (runIsAsync) {\n const resolved = await probeResult;\n consume(resolved);\n } else {\n consume(probeResult);\n }\n const durationProbeRaw = hr() - probeStart;\n\n let postIsAsync = false;\n if (post) {\n const postResult = post(context, input);\n postIsAsync = isThenable(postResult);\n if (postIsAsync) {\n await postResult;\n }\n }\n\n const timerOverhead = runIsAsync ? 0n : measureTimerOverhead();\n let durationProbe = runIsAsync ? durationProbeRaw : durationProbeRaw > timerOverhead ? durationProbeRaw - timerOverhead : 0n;\n\n const shouldPerturbInput = process.env.OVERTAKE_PERTURB_INPUT === '1';\n let nonce = 0;\n const nextNonce = shouldPerturbInput\n ? () => {\n nonce = (nonce + 1) | 0;\n return nonce;\n }\n : null;\n\n if (!runIsAsync && !pre && !post) {\n const batchProbeSize = 10_000;\n const batchProbeStart = hr();\n if (nextNonce) {\n for (let i = 0; i < batchProbeSize; i++) {\n consume((runRaw as Function)(context, input, nextNonce()));\n }\n } else {\n for (let i = 0; i < batchProbeSize; i++) {\n consume(runRaw(context, input));\n }\n }\n durationProbe = (hr() - batchProbeStart) / BigInt(batchProbeSize);\n }\n\n const runTimedSync = runIsAsync ? null : runSync(runRaw, timerOverhead);\n const runTimedAsync = runIsAsync ? runAsync(runRaw) : null;\n const run = runIsAsync ? runTimedAsync! : runTimedSync!;\n\n const runOnceSync: RunTimedSync<TContext, TInput> | null = runIsAsync ? null : nextNonce ? (ctx, dataValue) => runTimedSync!(ctx, dataValue, nextNonce()) : runTimedSync!;\n const runOnceAsync: RunTimedAsync<TContext, TInput> | null = runIsAsync ? (nextNonce ? (ctx, dataValue) => runTimedAsync!(ctx, dataValue, nextNonce()) : runTimedAsync!) : null;\n\n const preSync = preIsAsync ? null : pre;\n const preAsync = preIsAsync ? pre : null;\n const postSync = postIsAsync ? null : post;\n const postAsync = postIsAsync ? post : null;\n\n // choose batch size to amortize timer overhead\n const durationPerRun = durationProbe === 0n ? 1n : durationProbe;\n const suggestedBatch = Number(TARGET_SAMPLE_NS / durationPerRun);\n const minBatchForFastOps = durationProbe < 100n ? 100_000 : 1;\n const initialBatchSize = Math.min(MAX_BATCH, Math.max(minBatchForFastOps, suggestedBatch));\n\n // auto-tune based on warmup samples\n const tuned = await tuneParameters({\n initialBatch: initialBatchSize,\n run,\n runRaw,\n runIsAsync,\n pre,\n preIsAsync,\n post,\n postIsAsync,\n context,\n data: input,\n minCycles,\n relThreshold,\n maxCycles,\n nextNonce,\n });\n let batchSize = tuned.batchSize;\n minCycles = tuned.minCycles;\n relThreshold = tuned.relThreshold;\n\n // warmup: run until requested cycles, adapt if unstable\n const warmupStart = performance.now();\n let warmupRemaining = warmupCycles;\n const warmupWindow: number[] = [];\n const warmupCap = Math.max(warmupCycles, Math.min(maxCycles, warmupCycles * 4 || 1000));\n const canBatchTime = !runIsAsync && !preSync && !preAsync && !postSync && !postAsync;\n\n const runWarmup = async () => {\n if (canBatchTime) {\n const batchStart = hr();\n if (nextNonce) {\n for (let b = 0; b < batchSize; b++) {\n consume((runRaw as Function)(context, input, nextNonce()));\n }\n } else {\n for (let b = 0; b < batchSize; b++) {\n consume(runRaw(context, input));\n }\n }\n return (hr() - batchStart) / BigInt(batchSize);\n }\n\n if (preSync) {\n preSync(context, input);\n } else if (preAsync) {\n await preAsync(context, input);\n }\n\n const duration = runIsAsync ? await runOnceAsync!(context, input) : runOnceSync!(context, input);\n\n if (postSync) {\n postSync(context, input);\n } else if (postAsync) {\n await postAsync(context, input);\n }\n\n return duration;\n };\n\n while (performance.now() - warmupStart < 1_000 && warmupRemaining > 0) {\n const duration = await runWarmup();\n pushWindow(warmupWindow, Number(duration), warmupCap);\n warmupRemaining--;\n }\n let warmupDone = 0;\n while (warmupDone < warmupRemaining) {\n const duration = await runWarmup();\n pushWindow(warmupWindow, Number(duration), warmupCap);\n warmupDone++;\n if (global.gc && warmupDone % GC_STRIDE === 0) {\n global.gc();\n }\n }\n while (warmupWindow.length >= 8 && warmupWindow.length < warmupCap) {\n const cv = windowCv(warmupWindow);\n if (cv <= relThreshold * 2) {\n break;\n }\n const duration = await runWarmup();\n pushWindow(warmupWindow, Number(duration), warmupCap);\n }\n\n let i = 0;\n let mean = 0n;\n let m2 = 0n;\n const outlierWindow: number[] = [];\n let skipped = 0;\n const maxSkipped = maxCycles * 10;\n let disableFiltering = false;\n\n while (true) {\n if (i >= maxCycles) break;\n if (!disableFiltering && skipped >= maxSkipped) {\n console.error(`Warning: ${skipped} samples skipped due to noise/outlier detection. ` + `Disabling filtering for remaining samples. Results may have higher variance.`);\n disableFiltering = true;\n }\n\n if (global.gc && i > 0 && i % GC_STRIDE === 0) {\n global.gc();\n }\n\n const gcMarker = gcWatcher?.start();\n const sampleStart = performance.now();\n let sampleDuration = 0n;\n\n if (canBatchTime) {\n const batchStart = hr();\n if (nextNonce) {\n for (let b = 0; b < batchSize; b++) {\n consume((runRaw as Function)(context, input, nextNonce()));\n }\n } else {\n for (let b = 0; b < batchSize; b++) {\n consume(runRaw(context, input));\n }\n }\n const batchDuration = hr() - batchStart;\n sampleDuration = batchDuration / BigInt(batchSize);\n } else {\n for (let b = 0; b < batchSize; b++) {\n if (preSync) {\n preSync(context, input);\n } else if (preAsync) {\n await preAsync(context, input);\n }\n\n const duration = runIsAsync ? await runOnceAsync!(context, input) : runOnceSync!(context, input);\n sampleDuration += duration;\n\n if (postSync) {\n postSync(context, input);\n } else if (postAsync) {\n await postAsync(context, input);\n }\n }\n sampleDuration /= BigInt(batchSize);\n }\n\n const sampleEnd = performance.now();\n if (!disableFiltering) {\n const gcNoise = (gcMarker ? gcWatcher!.seen(gcMarker) : false) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);\n if (gcNoise) {\n skipped++;\n continue;\n }\n }\n\n const durationNumber = Number(sampleDuration);\n pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);\n if (!disableFiltering) {\n const { median, iqr } = medianAndIqr(outlierWindow);\n const maxAllowed = median + OUTLIER_IQR_MULTIPLIER * iqr || Number.POSITIVE_INFINITY;\n if (outlierWindow.length >= 8 && durationNumber > maxAllowed && durationNumber - median > OUTLIER_ABS_THRESHOLD_NS) {\n skipped++;\n continue;\n }\n\n const meanNumber = Number(mean);\n if (i >= 8 && meanNumber > 0 && durationNumber > OUTLIER_MULTIPLIER * meanNumber && durationNumber - meanNumber > OUTLIER_ABS_THRESHOLD_NS) {\n skipped++;\n continue;\n }\n }\n\n durations[i++] = sampleDuration;\n const delta = sampleDuration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (sampleDuration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n if (i % PROGRESS_STRIDE === 0) {\n control[Control.PROGRESS] = progress;\n }\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n gcTracker?.dispose?.();\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["benchmark","COMPLETE_VALUE","hr","process","hrtime","bigint","bind","sink","Int32Array","SharedArrayBuffer","BYTES_PER_ELEMENT","consume","value","payload","Number","isFinite","Math","trunc","length","Atomics","xor","runSync","run","overhead","args","start","result","duration","runAsync","isThenable","then","TARGET_SAMPLE_NS","MAX_BATCH","PROGRESS_STRIDE","GC_STRIDE","OUTLIER_MULTIPLIER","OUTLIER_IQR_MULTIPLIER","OUTLIER_WINDOW","OUTLIER_ABS_THRESHOLD_NS","BASELINE_SAMPLES","OUTLIER_SCRATCH","Float64Array","measureTimerOverhead","total","i","BigInt","collectSample","batchSize","runRaw","runIsAsync","pre","preIsAsync","post","postIsAsync","context","data","nextNonce","canBatchTime","batchStart","b","sampleDuration","runAsyncFn","runSyncFn","tuneParameters","initialBatch","minCycles","relThreshold","maxCycles","bestCv","POSITIVE_INFINITY","bestBatch","attempt","samples","sampleCount","min","s","push","mean","reduce","acc","v","variance","max","stddev","sqrt","cv","tunedRel","tunedMin","ceil","createGCTracker","env","OVERTAKE_GC_OBSERVER","PerformanceObserver","events","observer","list","entry","getEntries","startTime","end","observe","entryTypes","overlaps","noisy","event","splice","dispose","disconnect","pushWindow","arr","cap","shift","medianAndIqr","median","iqr","view","subarray","sort","mid","floor","q1Idx","q3Idx","q1","q3","windowCv","a","setup","teardown","warmupCycles","absThreshold","gcObserver","durationsSAB","controlSAB","durations","BigUint64Array","control","Control","INDEX","PROGRESS","COMPLETE","input","gcWatcher","GCWatcher","gcTracker","preResult","probeStart","probeResult","resolved","durationProbeRaw","postResult","timerOverhead","durationProbe","shouldPerturbInput","OVERTAKE_PERTURB_INPUT","nonce","batchProbeSize","batchProbeStart","runTimedSync","runTimedAsync","runOnceSync","ctx","dataValue","runOnceAsync","preSync","preAsync","postSync","postAsync","durationPerRun","suggestedBatch","minBatchForFastOps","initialBatchSize","tuned","warmupStart","performance","now","warmupRemaining","warmupWindow","warmupCap","runWarmup","warmupDone","global","gc","m2","outlierWindow","skipped","maxSkipped","disableFiltering","console","error","gcMarker","sampleStart","batchDuration","sampleEnd","gcNoise","seen","durationNumber","maxAllowed","meanNumber","delta","progress","meanNum","cov","e","stack"],"mappings":"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"}
|