overtake 1.3.2 → 1.4.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 +8 -0
- package/build/executor.cjs +12 -5
- package/build/executor.cjs.map +1 -1
- package/build/executor.d.ts +1 -0
- package/build/executor.js +13 -6
- package/build/executor.js.map +1 -1
- package/build/index.cjs +58 -16
- package/build/index.cjs.map +1 -1
- package/build/index.js +58 -16
- package/build/index.js.map +1 -1
- package/build/reporter.cjs +63 -116
- package/build/reporter.cjs.map +1 -1
- package/build/reporter.js +65 -118
- package/build/reporter.js.map +1 -1
- package/build/runner.cjs +23 -19
- package/build/runner.cjs.map +1 -1
- package/build/runner.js +23 -19
- package/build/runner.js.map +1 -1
- package/build/types.cjs +1 -1
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +1 -1
- package/build/types.js +1 -1
- package/build/types.js.map +1 -1
- package/build/utils.cjs +53 -0
- package/build/utils.cjs.map +1 -1
- package/build/utils.d.ts +2 -0
- package/build/utils.js +48 -1
- package/build/utils.js.map +1 -1
- package/build/worker.cjs +6 -9
- package/build/worker.cjs.map +1 -1
- package/build/worker.js +7 -10
- package/build/worker.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/assert-no-closure.js +134 -0
- package/src/executor.ts +12 -7
- package/src/index.ts +50 -11
- package/src/reporter.ts +62 -118
- package/src/runner.ts +26 -20
- package/src/types.ts +1 -1
- package/src/utils.ts +56 -1
- package/src/worker.ts +7 -9
package/README.md
CHANGED
|
@@ -503,6 +503,14 @@ CLI mode enforces one benchmark per file. Calling `benchmark()` twice throws an
|
|
|
503
503
|
|
|
504
504
|
**Solution**: In CLI mode, don't import Benchmark or call `.execute()`. Use the global `benchmark` function.
|
|
505
505
|
|
|
506
|
+
### Worker out of memory
|
|
507
|
+
|
|
508
|
+
Benchmarks that retain objects across iterations (e.g. pushing to an array to prevent GC) can exhaust the worker heap. Increase the heap limit via `NODE_OPTIONS`:
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
NODE_OPTIONS='--max-old-space-size=8192' npx overtake bench.ts
|
|
512
|
+
```
|
|
513
|
+
|
|
506
514
|
### Results vary between runs
|
|
507
515
|
|
|
508
516
|
**Solution**: Increase `--min-cycles` for more samples, or use the `gcBlock` pattern to prevent garbage collection.
|
package/build/executor.cjs
CHANGED
|
@@ -26,6 +26,11 @@ const createExecutor = (options)=>{
|
|
|
26
26
|
const preCode = pre?.toString();
|
|
27
27
|
const runCode = run.toString();
|
|
28
28
|
const postCode = post?.toString();
|
|
29
|
+
if (setupCode) (0, _utilscjs.assertNoClosure)(setupCode, 'setup');
|
|
30
|
+
if (teardownCode) (0, _utilscjs.assertNoClosure)(teardownCode, 'teardown');
|
|
31
|
+
if (preCode) (0, _utilscjs.assertNoClosure)(preCode, 'pre');
|
|
32
|
+
(0, _utilscjs.assertNoClosure)(runCode, 'run');
|
|
33
|
+
if (postCode) (0, _utilscjs.assertNoClosure)(postCode, 'post');
|
|
29
34
|
const controlSAB = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * _typescjs.CONTROL_SLOTS);
|
|
30
35
|
const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);
|
|
31
36
|
const workerFile = new URL('./worker.js', require("url").pathToFileURL(__filename).toString());
|
|
@@ -62,17 +67,18 @@ const createExecutor = (options)=>{
|
|
|
62
67
|
const WORKER_TIMEOUT_MS = 300_000;
|
|
63
68
|
const exitPromise = (0, _nodeevents.once)(worker, 'exit');
|
|
64
69
|
const timeoutId = setTimeout(()=>worker.terminate(), WORKER_TIMEOUT_MS);
|
|
70
|
+
let workerError;
|
|
65
71
|
try {
|
|
66
72
|
const [exitCode] = await exitPromise;
|
|
67
73
|
clearTimeout(timeoutId);
|
|
68
74
|
if (progressIntervalId) clearInterval(progressIntervalId);
|
|
69
75
|
if (exitCode !== 0) {
|
|
70
|
-
|
|
76
|
+
workerError = `worker exited with code ${exitCode}`;
|
|
71
77
|
}
|
|
72
78
|
} catch (err) {
|
|
73
79
|
clearTimeout(timeoutId);
|
|
74
80
|
if (progressIntervalId) clearInterval(progressIntervalId);
|
|
75
|
-
|
|
81
|
+
workerError = err instanceof Error ? err.message : String(err);
|
|
76
82
|
}
|
|
77
83
|
const count = control[_typescjs.Control.INDEX];
|
|
78
84
|
const heapUsedKB = control[_typescjs.Control.HEAP_USED];
|
|
@@ -103,13 +109,14 @@ const createExecutor = (options)=>{
|
|
|
103
109
|
[
|
|
104
110
|
'dceWarning',
|
|
105
111
|
dceWarning
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
'error',
|
|
115
|
+
workerError
|
|
106
116
|
]
|
|
107
117
|
]);
|
|
108
118
|
return Object.fromEntries(report);
|
|
109
119
|
}, workers);
|
|
110
|
-
executor.error((err)=>{
|
|
111
|
-
console.error(err);
|
|
112
|
-
});
|
|
113
120
|
return executor;
|
|
114
121
|
};
|
|
115
122
|
|
package/build/executor.cjs.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 { pathToFileURL } from 'node:url';\nimport { createReport, Report } from './reporter.js';\nimport { cmp } from './utils.js';\nimport {\n ExecutorRunOptions,\n ReportOptions,\n WorkerOptions,\n BenchmarkOptions,\n Control,\n ReportType,\n ReportTypeList,\n CONTROL_SLOTS,\n COMPLETE_VALUE,\n ProgressCallback,\n} from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {\n count: number;\n heapUsedKB: number;\n dceWarning: boolean;\n};\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n workers?: number;\n maxCycles?: number;\n onProgress?: ProgressCallback;\n progressInterval?: number;\n}\n\nconst BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>(options: Required<ExecutorOptions<R>>) => {\n const { workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes, onProgress, progressInterval = 100 } = options;\n const benchmarkUrl = (options as Record<symbol, unknown>)[BENCHMARK_URL];\n const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\n\n const executor = queue<ExecutorRunOptions<TContext, TInput>>(async ({ id, 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 benchmarkUrl: resolvedBenchmarkUrl,\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\n const control = new Int32Array(controlSAB);\n let progressIntervalId: ReturnType<typeof setInterval> | undefined;\n if (onProgress && id) {\n progressIntervalId = setInterval(() => {\n const progress = control[Control.PROGRESS] / COMPLETE_VALUE;\n onProgress({ id, progress });\n }, progressInterval);\n }\n\n const WORKER_TIMEOUT_MS = 300_000;\n const exitPromise = once(worker, 'exit');\n const timeoutId = setTimeout(() => worker.terminate(), WORKER_TIMEOUT_MS);\n try {\n const [exitCode] = await exitPromise;\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n if (exitCode !== 0) {\n
|
|
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, assertNoClosure } from './utils.js';\nimport {\n ExecutorRunOptions,\n ReportOptions,\n WorkerOptions,\n BenchmarkOptions,\n Control,\n ReportType,\n ReportTypeList,\n CONTROL_SLOTS,\n COMPLETE_VALUE,\n ProgressCallback,\n} from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {\n count: number;\n heapUsedKB: number;\n dceWarning: boolean;\n error?: string;\n};\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n workers?: number;\n maxCycles?: number;\n onProgress?: ProgressCallback;\n progressInterval?: number;\n}\n\nconst BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>(options: Required<ExecutorOptions<R>>) => {\n const { workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes, onProgress, progressInterval = 100 } = options;\n const benchmarkUrl = (options as Record<symbol, unknown>)[BENCHMARK_URL];\n const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\n\n const executor = queue<ExecutorRunOptions<TContext, TInput>>(async ({ id, 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 if (setupCode) assertNoClosure(setupCode, 'setup');\n if (teardownCode) assertNoClosure(teardownCode, 'teardown');\n if (preCode) assertNoClosure(preCode, 'pre');\n assertNoClosure(runCode, 'run');\n if (postCode) assertNoClosure(postCode, 'post');\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 benchmarkUrl: resolvedBenchmarkUrl,\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\n const control = new Int32Array(controlSAB);\n let progressIntervalId: ReturnType<typeof setInterval> | undefined;\n if (onProgress && id) {\n progressIntervalId = setInterval(() => {\n const progress = control[Control.PROGRESS] / COMPLETE_VALUE;\n onProgress({ id, progress });\n }, progressInterval);\n }\n\n const WORKER_TIMEOUT_MS = 300_000;\n const exitPromise = once(worker, 'exit');\n const timeoutId = setTimeout(() => worker.terminate(), WORKER_TIMEOUT_MS);\n let workerError: string | undefined;\n try {\n const [exitCode] = await exitPromise;\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n if (exitCode !== 0) {\n workerError = `worker exited with code ${exitCode}`;\n }\n } catch (err) {\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n workerError = err instanceof Error ? err.message : String(err);\n }\n\n const count = control[Control.INDEX];\n const heapUsedKB = control[Control.HEAP_USED];\n const durations = new BigUint64Array(durationsSAB).slice(0, count).sort(cmp);\n\n const DCE_THRESHOLD_OPS = 5_000_000_000;\n let dceWarning = false;\n if (count > 0) {\n let sum = 0n;\n for (const d of durations) sum += d;\n const avgNs = Number(sum / BigInt(count)) / 1000;\n const opsPerSec = avgNs > 0 ? 1_000_000_000 / avgNs : Infinity;\n if (opsPerSec > DCE_THRESHOLD_OPS) {\n dceWarning = true;\n }\n }\n\n const report = reportTypes\n .map<[string, unknown]>((type) => [type, createReport(durations, type)] as [ReportType, Report])\n .concat([\n ['count', count],\n ['heapUsedKB', heapUsedKB],\n ['dceWarning', dceWarning],\n ['error', workerError],\n ]);\n return Object.fromEntries(report);\n }, workers);\n\n return executor;\n};\n"],"names":["createExecutor","BENCHMARK_URL","Symbol","for","options","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","onProgress","progressInterval","benchmarkUrl","resolvedBenchmarkUrl","pathToFileURL","process","cwd","href","executor","queue","id","setup","teardown","pre","run","post","data","setupCode","toString","teardownCode","preCode","runCode","postCode","assertNoClosure","controlSAB","SharedArrayBuffer","Int32Array","BYTES_PER_ELEMENT","CONTROL_SLOTS","durationsSAB","BigUint64Array","workerFile","URL","workerData","worker","Worker","control","progressIntervalId","setInterval","progress","Control","PROGRESS","COMPLETE_VALUE","WORKER_TIMEOUT_MS","exitPromise","once","timeoutId","setTimeout","terminate","workerError","exitCode","clearTimeout","clearInterval","err","Error","message","String","count","INDEX","heapUsedKB","HEAP_USED","durations","slice","sort","cmp","DCE_THRESHOLD_OPS","dceWarning","sum","d","avgNs","Number","BigInt","opsPerSec","Infinity","report","map","type","createReport","concat","Object","fromEntries"],"mappings":"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"}
|
package/build/executor.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report>
|
|
|
4
4
|
count: number;
|
|
5
5
|
heapUsedKB: number;
|
|
6
6
|
dceWarning: boolean;
|
|
7
|
+
error?: string;
|
|
7
8
|
};
|
|
8
9
|
export interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {
|
|
9
10
|
workers?: number;
|
package/build/executor.js
CHANGED
|
@@ -3,7 +3,7 @@ import { once } from 'node:events';
|
|
|
3
3
|
import { queue } from 'async';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
5
|
import { createReport } from "./reporter.js";
|
|
6
|
-
import { cmp } from "./utils.js";
|
|
6
|
+
import { cmp, assertNoClosure } from "./utils.js";
|
|
7
7
|
import { Control, CONTROL_SLOTS, COMPLETE_VALUE } from "./types.js";
|
|
8
8
|
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
9
9
|
export const createExecutor = (options)=>{
|
|
@@ -16,6 +16,11 @@ export const createExecutor = (options)=>{
|
|
|
16
16
|
const preCode = pre?.toString();
|
|
17
17
|
const runCode = run.toString();
|
|
18
18
|
const postCode = post?.toString();
|
|
19
|
+
if (setupCode) assertNoClosure(setupCode, 'setup');
|
|
20
|
+
if (teardownCode) assertNoClosure(teardownCode, 'teardown');
|
|
21
|
+
if (preCode) assertNoClosure(preCode, 'pre');
|
|
22
|
+
assertNoClosure(runCode, 'run');
|
|
23
|
+
if (postCode) assertNoClosure(postCode, 'post');
|
|
19
24
|
const controlSAB = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * CONTROL_SLOTS);
|
|
20
25
|
const durationsSAB = new SharedArrayBuffer(BigUint64Array.BYTES_PER_ELEMENT * maxCycles);
|
|
21
26
|
const workerFile = new URL('./worker.js', import.meta.url);
|
|
@@ -52,17 +57,18 @@ export const createExecutor = (options)=>{
|
|
|
52
57
|
const WORKER_TIMEOUT_MS = 300_000;
|
|
53
58
|
const exitPromise = once(worker, 'exit');
|
|
54
59
|
const timeoutId = setTimeout(()=>worker.terminate(), WORKER_TIMEOUT_MS);
|
|
60
|
+
let workerError;
|
|
55
61
|
try {
|
|
56
62
|
const [exitCode] = await exitPromise;
|
|
57
63
|
clearTimeout(timeoutId);
|
|
58
64
|
if (progressIntervalId) clearInterval(progressIntervalId);
|
|
59
65
|
if (exitCode !== 0) {
|
|
60
|
-
|
|
66
|
+
workerError = `worker exited with code ${exitCode}`;
|
|
61
67
|
}
|
|
62
68
|
} catch (err) {
|
|
63
69
|
clearTimeout(timeoutId);
|
|
64
70
|
if (progressIntervalId) clearInterval(progressIntervalId);
|
|
65
|
-
|
|
71
|
+
workerError = err instanceof Error ? err.message : String(err);
|
|
66
72
|
}
|
|
67
73
|
const count = control[Control.INDEX];
|
|
68
74
|
const heapUsedKB = control[Control.HEAP_USED];
|
|
@@ -93,13 +99,14 @@ export const createExecutor = (options)=>{
|
|
|
93
99
|
[
|
|
94
100
|
'dceWarning',
|
|
95
101
|
dceWarning
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
'error',
|
|
105
|
+
workerError
|
|
96
106
|
]
|
|
97
107
|
]);
|
|
98
108
|
return Object.fromEntries(report);
|
|
99
109
|
}, workers);
|
|
100
|
-
executor.error((err)=>{
|
|
101
|
-
console.error(err);
|
|
102
|
-
});
|
|
103
110
|
return executor;
|
|
104
111
|
};
|
|
105
112
|
|
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 { pathToFileURL } from 'node:url';\nimport { createReport, Report } from './reporter.js';\nimport { cmp } from './utils.js';\nimport {\n ExecutorRunOptions,\n ReportOptions,\n WorkerOptions,\n BenchmarkOptions,\n Control,\n ReportType,\n ReportTypeList,\n CONTROL_SLOTS,\n COMPLETE_VALUE,\n ProgressCallback,\n} from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {\n count: number;\n heapUsedKB: number;\n dceWarning: boolean;\n};\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n workers?: number;\n maxCycles?: number;\n onProgress?: ProgressCallback;\n progressInterval?: number;\n}\n\nconst BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>(options: Required<ExecutorOptions<R>>) => {\n const { workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes, onProgress, progressInterval = 100 } = options;\n const benchmarkUrl = (options as Record<symbol, unknown>)[BENCHMARK_URL];\n const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\n\n const executor = queue<ExecutorRunOptions<TContext, TInput>>(async ({ id, 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 benchmarkUrl: resolvedBenchmarkUrl,\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\n const control = new Int32Array(controlSAB);\n let progressIntervalId: ReturnType<typeof setInterval> | undefined;\n if (onProgress && id) {\n progressIntervalId = setInterval(() => {\n const progress = control[Control.PROGRESS] / COMPLETE_VALUE;\n onProgress({ id, progress });\n }, progressInterval);\n }\n\n const WORKER_TIMEOUT_MS = 300_000;\n const exitPromise = once(worker, 'exit');\n const timeoutId = setTimeout(() => worker.terminate(), WORKER_TIMEOUT_MS);\n try {\n const [exitCode] = await exitPromise;\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n if (exitCode !== 0) {\n
|
|
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, assertNoClosure } from './utils.js';\nimport {\n ExecutorRunOptions,\n ReportOptions,\n WorkerOptions,\n BenchmarkOptions,\n Control,\n ReportType,\n ReportTypeList,\n CONTROL_SLOTS,\n COMPLETE_VALUE,\n ProgressCallback,\n} from './types.js';\n\nexport type ExecutorReport<R extends ReportTypeList> = Record<R[number], Report> & {\n count: number;\n heapUsedKB: number;\n dceWarning: boolean;\n error?: string;\n};\n\nexport interface ExecutorOptions<R extends ReportTypeList> extends BenchmarkOptions, ReportOptions<R> {\n workers?: number;\n maxCycles?: number;\n onProgress?: ProgressCallback;\n progressInterval?: number;\n}\n\nconst BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');\n\nexport const createExecutor = <TContext, TInput, R extends ReportTypeList>(options: Required<ExecutorOptions<R>>) => {\n const { workers, warmupCycles, maxCycles, minCycles, absThreshold, relThreshold, gcObserver = true, reportTypes, onProgress, progressInterval = 100 } = options;\n const benchmarkUrl = (options as Record<symbol, unknown>)[BENCHMARK_URL];\n const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\n\n const executor = queue<ExecutorRunOptions<TContext, TInput>>(async ({ id, 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 if (setupCode) assertNoClosure(setupCode, 'setup');\n if (teardownCode) assertNoClosure(teardownCode, 'teardown');\n if (preCode) assertNoClosure(preCode, 'pre');\n assertNoClosure(runCode, 'run');\n if (postCode) assertNoClosure(postCode, 'post');\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 benchmarkUrl: resolvedBenchmarkUrl,\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\n const control = new Int32Array(controlSAB);\n let progressIntervalId: ReturnType<typeof setInterval> | undefined;\n if (onProgress && id) {\n progressIntervalId = setInterval(() => {\n const progress = control[Control.PROGRESS] / COMPLETE_VALUE;\n onProgress({ id, progress });\n }, progressInterval);\n }\n\n const WORKER_TIMEOUT_MS = 300_000;\n const exitPromise = once(worker, 'exit');\n const timeoutId = setTimeout(() => worker.terminate(), WORKER_TIMEOUT_MS);\n let workerError: string | undefined;\n try {\n const [exitCode] = await exitPromise;\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n if (exitCode !== 0) {\n workerError = `worker exited with code ${exitCode}`;\n }\n } catch (err) {\n clearTimeout(timeoutId);\n if (progressIntervalId) clearInterval(progressIntervalId);\n workerError = err instanceof Error ? err.message : String(err);\n }\n\n const count = control[Control.INDEX];\n const heapUsedKB = control[Control.HEAP_USED];\n const durations = new BigUint64Array(durationsSAB).slice(0, count).sort(cmp);\n\n const DCE_THRESHOLD_OPS = 5_000_000_000;\n let dceWarning = false;\n if (count > 0) {\n let sum = 0n;\n for (const d of durations) sum += d;\n const avgNs = Number(sum / BigInt(count)) / 1000;\n const opsPerSec = avgNs > 0 ? 1_000_000_000 / avgNs : Infinity;\n if (opsPerSec > DCE_THRESHOLD_OPS) {\n dceWarning = true;\n }\n }\n\n const report = reportTypes\n .map<[string, unknown]>((type) => [type, createReport(durations, type)] as [ReportType, Report])\n .concat([\n ['count', count],\n ['heapUsedKB', heapUsedKB],\n ['dceWarning', dceWarning],\n ['error', workerError],\n ]);\n return Object.fromEntries(report);\n }, workers);\n\n return executor;\n};\n"],"names":["Worker","once","queue","pathToFileURL","createReport","cmp","assertNoClosure","Control","CONTROL_SLOTS","COMPLETE_VALUE","BENCHMARK_URL","Symbol","for","createExecutor","options","workers","warmupCycles","maxCycles","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","onProgress","progressInterval","benchmarkUrl","resolvedBenchmarkUrl","process","cwd","href","executor","id","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","control","progressIntervalId","setInterval","progress","PROGRESS","WORKER_TIMEOUT_MS","exitPromise","timeoutId","setTimeout","terminate","workerError","exitCode","clearTimeout","clearInterval","err","Error","message","String","count","INDEX","heapUsedKB","HEAP_USED","durations","slice","sort","DCE_THRESHOLD_OPS","dceWarning","sum","d","avgNs","Number","BigInt","opsPerSec","Infinity","report","map","type","concat","Object","fromEntries"],"mappings":"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"}
|
package/build/index.cjs
CHANGED
|
@@ -67,7 +67,7 @@ function _interop_require_default(obj) {
|
|
|
67
67
|
default: obj
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
const DEFAULT_WORKERS = (0, _nodeos.cpus)().length;
|
|
70
|
+
const DEFAULT_WORKERS = Math.max(1, Math.ceil((0, _nodeos.cpus)().length / 4));
|
|
71
71
|
const AsyncFunction = (async ()=>{}).constructor;
|
|
72
72
|
const BENCHMARK_URL = Symbol.for('overtake.benchmarkUrl');
|
|
73
73
|
const DEFAULT_REPORT_TYPES = [
|
|
@@ -251,7 +251,11 @@ const printSimpleReports = (reports)=>{
|
|
|
251
251
|
for (const { measure, feeds } of report.measures){
|
|
252
252
|
console.group('\n', report.target, measure);
|
|
253
253
|
for (const { feed, data } of feeds){
|
|
254
|
-
const { count, heapUsedKB, dceWarning, ...metrics } = data;
|
|
254
|
+
const { count, heapUsedKB, dceWarning, error: benchError, ...metrics } = data;
|
|
255
|
+
if (benchError) {
|
|
256
|
+
console.log(feed, `\x1b[31m[error: ${benchError}]\x1b[0m`);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
255
259
|
const output = Object.entries(metrics).map(([key, report])=>`${key}: ${report.toString()}`).join('; ');
|
|
256
260
|
const extras = [];
|
|
257
261
|
if (heapUsedKB) extras.push(`heap: ${heapUsedKB}KB`);
|
|
@@ -269,10 +273,17 @@ const printTableReports = (reports)=>{
|
|
|
269
273
|
console.log('\n', report.target, measure);
|
|
270
274
|
const table = {};
|
|
271
275
|
for (const { feed, data } of feeds){
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
+
const { error: benchError } = data;
|
|
277
|
+
if (benchError) {
|
|
278
|
+
table[feed] = {
|
|
279
|
+
error: benchError
|
|
280
|
+
};
|
|
281
|
+
} else {
|
|
282
|
+
table[feed] = Object.fromEntries(Object.entries(data).map(([key, report])=>[
|
|
283
|
+
key,
|
|
284
|
+
report.toString()
|
|
285
|
+
]));
|
|
286
|
+
}
|
|
276
287
|
}
|
|
277
288
|
console.table(table);
|
|
278
289
|
}
|
|
@@ -284,10 +295,17 @@ const printJSONReports = (reports, padding)=>{
|
|
|
284
295
|
for (const { measure, feeds } of report.measures){
|
|
285
296
|
const row = {};
|
|
286
297
|
for (const { feed, data } of feeds){
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
298
|
+
const { error: benchError } = data;
|
|
299
|
+
if (benchError) {
|
|
300
|
+
row[feed] = {
|
|
301
|
+
error: String(benchError)
|
|
302
|
+
};
|
|
303
|
+
} else {
|
|
304
|
+
row[feed] = Object.fromEntries(Object.entries(data).map(([key, report])=>[
|
|
305
|
+
key,
|
|
306
|
+
report.toString()
|
|
307
|
+
]));
|
|
308
|
+
}
|
|
291
309
|
}
|
|
292
310
|
output[`${report.target} ${measure}`] = row;
|
|
293
311
|
}
|
|
@@ -299,7 +317,14 @@ const printMarkdownReports = (reports)=>{
|
|
|
299
317
|
for (const { measure, feeds } of report.measures){
|
|
300
318
|
console.log(`\n## ${report.target} - ${measure}\n`);
|
|
301
319
|
if (feeds.length === 0) continue;
|
|
302
|
-
const
|
|
320
|
+
const firstValid = feeds.find((f)=>!f.data.error);
|
|
321
|
+
if (!firstValid) {
|
|
322
|
+
for (const { feed, data } of feeds){
|
|
323
|
+
console.log(`| ${feed} | error: ${data.error} |`);
|
|
324
|
+
}
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const keys = Object.keys(firstValid.data).filter((k)=>k !== 'count' && k !== 'error');
|
|
303
328
|
const header = [
|
|
304
329
|
'Feed',
|
|
305
330
|
...keys
|
|
@@ -311,6 +336,10 @@ const printMarkdownReports = (reports)=>{
|
|
|
311
336
|
console.log(`| ${header} |`);
|
|
312
337
|
console.log(`| ${separator} |`);
|
|
313
338
|
for (const { feed, data } of feeds){
|
|
339
|
+
if (data.error) {
|
|
340
|
+
console.log(`| ${feed} | error: ${data.error} |`);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
314
343
|
const values = keys.map((k)=>data[k]?.toString() ?? '-');
|
|
315
344
|
console.log(`| ${[
|
|
316
345
|
feed,
|
|
@@ -325,16 +354,24 @@ const printHistogramReports = (reports, width = 40)=>{
|
|
|
325
354
|
for (const { measure, feeds } of report.measures){
|
|
326
355
|
console.log(`\n${report.target} - ${measure}\n`);
|
|
327
356
|
const opsKey = 'ops';
|
|
328
|
-
const values = feeds.map((f)=>
|
|
357
|
+
const values = feeds.map((f)=>{
|
|
358
|
+
const { error: benchError } = f.data;
|
|
359
|
+
return {
|
|
329
360
|
feed: f.feed,
|
|
330
|
-
value: f.data[opsKey]?.valueOf() ?? 0
|
|
331
|
-
|
|
361
|
+
value: benchError ? 0 : f.data[opsKey]?.valueOf() ?? 0,
|
|
362
|
+
error: benchError
|
|
363
|
+
};
|
|
364
|
+
});
|
|
332
365
|
const maxValue = Math.max(...values.map((v)=>v.value));
|
|
333
366
|
const maxLabelLen = Math.max(...values.map((v)=>v.feed.length));
|
|
334
|
-
for (const { feed, value } of values){
|
|
367
|
+
for (const { feed, value, error } of values){
|
|
368
|
+
const label = feed.padEnd(maxLabelLen);
|
|
369
|
+
if (error) {
|
|
370
|
+
console.log(` ${label} | \x1b[31m[error: ${error}]\x1b[0m`);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
335
373
|
const barLen = maxValue > 0 ? Math.round(value / maxValue * width) : 0;
|
|
336
374
|
const bar = '\u2588'.repeat(barLen);
|
|
337
|
-
const label = feed.padEnd(maxLabelLen);
|
|
338
375
|
const formatted = value.toLocaleString('en-US', {
|
|
339
376
|
maximumFractionDigits: 2
|
|
340
377
|
});
|
|
@@ -348,6 +385,7 @@ const reportsToBaseline = (reports)=>{
|
|
|
348
385
|
for (const report of reports){
|
|
349
386
|
for (const { measure, feeds } of report.measures){
|
|
350
387
|
for (const { feed, data } of feeds){
|
|
388
|
+
if (data.error) continue;
|
|
351
389
|
const key = `${report.target}/${measure}/${feed}`;
|
|
352
390
|
results[key] = {};
|
|
353
391
|
for (const [metric, value] of Object.entries(data)){
|
|
@@ -372,6 +410,10 @@ const printComparisonReports = (reports, baseline, threshold = 5)=>{
|
|
|
372
410
|
const key = `${report.target}/${measure}/${feed}`;
|
|
373
411
|
const baselineData = baseline.results[key];
|
|
374
412
|
console.log(` ${feed}:`);
|
|
413
|
+
if (data.error) {
|
|
414
|
+
console.log(` \x1b[31m[error: ${data.error}]\x1b[0m`);
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
375
417
|
for (const [metric, value] of Object.entries(data)){
|
|
376
418
|
if (metric === 'count') continue;
|
|
377
419
|
const current = value.valueOf();
|
package/build/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport Progress from 'progress';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES, ProgressInfo } 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> & { progress?: boolean }): 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 progress = false,\n progressInterval = 100,\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 totalBenchmarks = this.#targets.reduce((acc, t) => acc + t.measures.length * this.#feeds.length, 0);\n const progressMap = new Map<string, number>();\n let completedBenchmarks = 0;\n let bar: Progress | null = null;\n\n if (progress && totalBenchmarks > 0) {\n bar = new Progress(' [:bar] :percent :current/:total :label', {\n total: totalBenchmarks * 100,\n width: 30,\n complete: '=',\n incomplete: ' ',\n });\n }\n\n const onProgress = progress\n ? (info: ProgressInfo) => {\n progressMap.set(info.id, info.progress);\n const totalProgress = (completedBenchmarks + [...progressMap.values()].reduce((a, b) => a + b, 0)) * 100;\n const label = info.id.length > 30 ? info.id.slice(0, 27) + '...' : info.id;\n bar?.update(totalProgress / (totalBenchmarks * 100), { label });\n }\n : undefined;\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 onProgress,\n progressInterval,\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 id = `${target.title}/${measure.title}/${feed.title}`;\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n id,\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 progressMap.delete(id);\n completedBenchmarks++;\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 if (bar) {\n bar.update(1, { label: 'done' });\n bar.terminate();\n }\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 { count, heapUsedKB, dceWarning, ...metrics } = data as Record<string, unknown>;\n const output = Object.entries(metrics)\n .map(([key, report]) => `${key}: ${(report as { toString(): string }).toString()}`)\n .join('; ');\n const extras: string[] = [];\n if (heapUsedKB) extras.push(`heap: ${heapUsedKB}KB`);\n if (dceWarning) extras.push('\\x1b[33m[DCE warning]\\x1b[0m');\n const extrasStr = extras.length > 0 ? ` (${extras.join(', ')})` : '';\n console.log(feed, output + extrasStr);\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\nexport const printMarkdownReports = <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`);\n if (feeds.length === 0) continue;\n\n const keys = Object.keys(feeds[0].data).filter((k) => k !== 'count');\n const header = ['Feed', ...keys].join(' | ');\n const separator = ['---', ...keys.map(() => '---')].join(' | ');\n\n console.log(`| ${header} |`);\n console.log(`| ${separator} |`);\n\n for (const { feed, data } of feeds) {\n const values = keys.map((k) => (data as Record<string, { toString(): string }>)[k]?.toString() ?? '-');\n console.log(`| ${[feed, ...values].join(' | ')} |`);\n }\n }\n }\n};\n\nexport const printHistogramReports = <R extends ReportTypeList>(reports: TargetReport<R>[], width = 40) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log(`\\n${report.target} - ${measure}\\n`);\n\n const opsKey = 'ops';\n const values = feeds.map((f) => ({\n feed: f.feed,\n value: (f.data as Record<string, { valueOf(): number }>)[opsKey]?.valueOf() ?? 0,\n }));\n\n const maxValue = Math.max(...values.map((v) => v.value));\n const maxLabelLen = Math.max(...values.map((v) => v.feed.length));\n\n for (const { feed, value } of values) {\n const barLen = maxValue > 0 ? Math.round((value / maxValue) * width) : 0;\n const bar = '\\u2588'.repeat(barLen);\n const label = feed.padEnd(maxLabelLen);\n const formatted = value.toLocaleString('en-US', { maximumFractionDigits: 2 });\n console.log(` ${label} | ${bar} ${formatted} ops/s`);\n }\n }\n }\n};\n\nexport interface BaselineData {\n version: number;\n timestamp: string;\n results: Record<string, Record<string, number>>;\n}\n\nexport const reportsToBaseline = <R extends ReportTypeList>(reports: TargetReport<R>[]): BaselineData => {\n const results: Record<string, Record<string, number>> = {};\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n for (const { feed, data } of feeds) {\n const key = `${report.target}/${measure}/${feed}`;\n results[key] = {};\n for (const [metric, value] of Object.entries(data)) {\n if (metric !== 'count' && typeof (value as { valueOf(): number }).valueOf === 'function') {\n results[key][metric] = (value as { valueOf(): number }).valueOf();\n }\n }\n }\n }\n }\n return {\n version: 1,\n timestamp: new Date().toISOString(),\n results,\n };\n};\n\nexport const printComparisonReports = <R extends ReportTypeList>(reports: TargetReport<R>[], baseline: BaselineData, threshold = 5) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log(`\\n${report.target} - ${measure}\\n`);\n\n for (const { feed, data } of feeds) {\n const key = `${report.target}/${measure}/${feed}`;\n const baselineData = baseline.results[key];\n\n console.log(` ${feed}:`);\n\n for (const [metric, value] of Object.entries(data)) {\n if (metric === 'count') continue;\n const current = (value as { valueOf(): number }).valueOf();\n const baselineValue = baselineData?.[metric];\n\n if (baselineValue !== undefined && baselineValue !== 0) {\n const change = ((current - baselineValue) / baselineValue) * 100;\n const isOps = metric === 'ops';\n const improved = isOps ? change > threshold : change < -threshold;\n const regressed = isOps ? change < -threshold : change > threshold;\n\n let indicator = ' ';\n if (improved) indicator = '\\x1b[32m+\\x1b[0m';\n else if (regressed) indicator = '\\x1b[31m!\\x1b[0m';\n\n const changeStr = change >= 0 ? `+${change.toFixed(1)}%` : `${change.toFixed(1)}%`;\n const coloredChange = regressed ? `\\x1b[31m${changeStr}\\x1b[0m` : improved ? `\\x1b[32m${changeStr}\\x1b[0m` : changeStr;\n\n console.log(` ${indicator} ${metric}: ${(value as { toString(): string }).toString()} (${coloredChange})`);\n } else {\n console.log(` * ${metric}: ${(value as { toString(): string }).toString()} (new)`);\n }\n }\n }\n }\n }\n\n console.log(`\\nBaseline from: ${baseline.timestamp}`);\n};\n"],"names":["AsyncFunction","Benchmark","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","FeedContext","Measure","MeasureContext","Target","TargetContext","printComparisonReports","printHistogramReports","printJSONReports","printMarkdownReports","printSimpleReports","printTableReports","reportsToBaseline","cpus","length","BENCHMARK_URL","Symbol","for","pre","post","title","run","ctx","fn","teardown","measures","setup","measure","push","create","feed","self","target","execute","options","workers","warmupCycles","maxCycles","DEFAULT_CYCLES","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","progress","progressInterval","Error","benchmarkUrl","totalBenchmarks","reduce","acc","t","progressMap","Map","completedBenchmarks","bar","Progress","total","width","complete","incomplete","onProgress","info","set","id","totalProgress","values","a","b","label","slice","update","undefined","executor","createExecutor","reports","targetReport","measureReport","feeds","data","then","delete","drain","kill","terminate","report","console","group","count","heapUsedKB","dceWarning","metrics","output","Object","entries","map","key","toString","join","extras","extrasStr","log","groupEnd","table","fromEntries","padding","row","JSON","stringify","keys","filter","k","header","separator","opsKey","f","value","valueOf","maxValue","Math","max","v","maxLabelLen","barLen","round","repeat","padEnd","formatted","toLocaleString","maximumFractionDigits","results","metric","version","timestamp","Date","toISOString","baseline","threshold","baselineData","current","baselineValue","change","isOps","improved","regressed","indicator","changeStr","toFixed","coloredChange"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cpus } from 'node:os';\nimport Progress from 'progress';\nimport { createExecutor, ExecutorOptions, ExecutorReport } from './executor.js';\nimport { MaybePromise, StepFn, SetupFn, TeardownFn, FeedFn, ReportType, ReportTypeList, DEFAULT_CYCLES, ProgressInfo } from './types.js';\n\ndeclare global {\n const benchmark: typeof Benchmark.create;\n}\n\nexport const DEFAULT_WORKERS = Math.max(1, Math.ceil(cpus().length / 4));\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> & { progress?: boolean }): 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 progress = false,\n progressInterval = 100,\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 totalBenchmarks = this.#targets.reduce((acc, t) => acc + t.measures.length * this.#feeds.length, 0);\n const progressMap = new Map<string, number>();\n let completedBenchmarks = 0;\n let bar: Progress | null = null;\n\n if (progress && totalBenchmarks > 0) {\n bar = new Progress(' [:bar] :percent :current/:total :label', {\n total: totalBenchmarks * 100,\n width: 30,\n complete: '=',\n incomplete: ' ',\n });\n }\n\n const onProgress = progress\n ? (info: ProgressInfo) => {\n progressMap.set(info.id, info.progress);\n const totalProgress = (completedBenchmarks + [...progressMap.values()].reduce((a, b) => a + b, 0)) * 100;\n const label = info.id.length > 30 ? info.id.slice(0, 27) + '...' : info.id;\n bar?.update(totalProgress / (totalBenchmarks * 100), { label });\n }\n : undefined;\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 onProgress,\n progressInterval,\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 id = `${target.title}/${measure.title}/${feed.title}`;\n const data = await feed.fn?.();\n executor\n .push<ExecutorReport<R>>({\n id,\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 progressMap.delete(id);\n completedBenchmarks++;\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 if (bar) {\n bar.update(1, { label: 'done' });\n bar.terminate();\n }\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 { count, heapUsedKB, dceWarning, error: benchError, ...metrics } = data as Record<string, unknown>;\n if (benchError) {\n console.log(feed, `\\x1b[31m[error: ${benchError}]\\x1b[0m`);\n continue;\n }\n const output = Object.entries(metrics)\n .map(([key, report]) => `${key}: ${(report as { toString(): string }).toString()}`)\n .join('; ');\n const extras: string[] = [];\n if (heapUsedKB) extras.push(`heap: ${heapUsedKB}KB`);\n if (dceWarning) extras.push('\\x1b[33m[DCE warning]\\x1b[0m');\n const extrasStr = extras.length > 0 ? ` (${extras.join(', ')})` : '';\n console.log(feed, output + extrasStr);\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 const { error: benchError } = data as Record<string, unknown>;\n if (benchError) {\n table[feed] = { error: benchError };\n } else {\n table[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\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 const { error: benchError } = data as Record<string, unknown>;\n if (benchError) {\n row[feed] = { error: String(benchError) };\n } else {\n row[feed] = Object.fromEntries(Object.entries(data).map(([key, report]) => [key, report.toString()]));\n }\n }\n output[`${report.target} ${measure}`] = row;\n }\n }\n console.log(JSON.stringify(output, null, padding));\n};\n\nexport const printMarkdownReports = <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`);\n if (feeds.length === 0) continue;\n\n const firstValid = feeds.find((f) => !(f.data as Record<string, unknown>).error);\n if (!firstValid) {\n for (const { feed, data } of feeds) {\n console.log(`| ${feed} | error: ${(data as Record<string, unknown>).error} |`);\n }\n continue;\n }\n const keys = Object.keys(firstValid.data).filter((k) => k !== 'count' && k !== 'error');\n const header = ['Feed', ...keys].join(' | ');\n const separator = ['---', ...keys.map(() => '---')].join(' | ');\n\n console.log(`| ${header} |`);\n console.log(`| ${separator} |`);\n\n for (const { feed, data } of feeds) {\n if ((data as Record<string, unknown>).error) {\n console.log(`| ${feed} | error: ${(data as Record<string, unknown>).error} |`);\n continue;\n }\n const values = keys.map((k) => (data as Record<string, { toString(): string }>)[k]?.toString() ?? '-');\n console.log(`| ${[feed, ...values].join(' | ')} |`);\n }\n }\n }\n};\n\nexport const printHistogramReports = <R extends ReportTypeList>(reports: TargetReport<R>[], width = 40) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log(`\\n${report.target} - ${measure}\\n`);\n\n const opsKey = 'ops';\n const values = feeds.map((f) => {\n const { error: benchError } = f.data as Record<string, unknown>;\n return {\n feed: f.feed,\n value: benchError ? 0 : ((f.data as Record<string, { valueOf(): number }>)[opsKey]?.valueOf() ?? 0),\n error: benchError as string | undefined,\n };\n });\n\n const maxValue = Math.max(...values.map((v) => v.value));\n const maxLabelLen = Math.max(...values.map((v) => v.feed.length));\n\n for (const { feed, value, error } of values) {\n const label = feed.padEnd(maxLabelLen);\n if (error) {\n console.log(` ${label} | \\x1b[31m[error: ${error}]\\x1b[0m`);\n continue;\n }\n const barLen = maxValue > 0 ? Math.round((value / maxValue) * width) : 0;\n const bar = '\\u2588'.repeat(barLen);\n const formatted = value.toLocaleString('en-US', { maximumFractionDigits: 2 });\n console.log(` ${label} | ${bar} ${formatted} ops/s`);\n }\n }\n }\n};\n\nexport interface BaselineData {\n version: number;\n timestamp: string;\n results: Record<string, Record<string, number>>;\n}\n\nexport const reportsToBaseline = <R extends ReportTypeList>(reports: TargetReport<R>[]): BaselineData => {\n const results: Record<string, Record<string, number>> = {};\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n for (const { feed, data } of feeds) {\n if ((data as Record<string, unknown>).error) continue;\n const key = `${report.target}/${measure}/${feed}`;\n results[key] = {};\n for (const [metric, value] of Object.entries(data)) {\n if (metric !== 'count' && typeof (value as { valueOf(): number }).valueOf === 'function') {\n results[key][metric] = (value as { valueOf(): number }).valueOf();\n }\n }\n }\n }\n }\n return {\n version: 1,\n timestamp: new Date().toISOString(),\n results,\n };\n};\n\nexport const printComparisonReports = <R extends ReportTypeList>(reports: TargetReport<R>[], baseline: BaselineData, threshold = 5) => {\n for (const report of reports) {\n for (const { measure, feeds } of report.measures) {\n console.log(`\\n${report.target} - ${measure}\\n`);\n\n for (const { feed, data } of feeds) {\n const key = `${report.target}/${measure}/${feed}`;\n const baselineData = baseline.results[key];\n\n console.log(` ${feed}:`);\n\n if ((data as Record<string, unknown>).error) {\n console.log(` \\x1b[31m[error: ${(data as Record<string, unknown>).error}]\\x1b[0m`);\n continue;\n }\n\n for (const [metric, value] of Object.entries(data)) {\n if (metric === 'count') continue;\n const current = (value as { valueOf(): number }).valueOf();\n const baselineValue = baselineData?.[metric];\n\n if (baselineValue !== undefined && baselineValue !== 0) {\n const change = ((current - baselineValue) / baselineValue) * 100;\n const isOps = metric === 'ops';\n const improved = isOps ? change > threshold : change < -threshold;\n const regressed = isOps ? change < -threshold : change > threshold;\n\n let indicator = ' ';\n if (improved) indicator = '\\x1b[32m+\\x1b[0m';\n else if (regressed) indicator = '\\x1b[31m!\\x1b[0m';\n\n const changeStr = change >= 0 ? `+${change.toFixed(1)}%` : `${change.toFixed(1)}%`;\n const coloredChange = regressed ? `\\x1b[31m${changeStr}\\x1b[0m` : improved ? `\\x1b[32m${changeStr}\\x1b[0m` : changeStr;\n\n console.log(` ${indicator} ${metric}: ${(value as { toString(): string }).toString()} (${coloredChange})`);\n } else {\n console.log(` * ${metric}: ${(value as { toString(): string }).toString()} (new)`);\n }\n }\n }\n }\n }\n\n console.log(`\\nBaseline from: ${baseline.timestamp}`);\n};\n"],"names":["AsyncFunction","Benchmark","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","FeedContext","Measure","MeasureContext","Target","TargetContext","printComparisonReports","printHistogramReports","printJSONReports","printMarkdownReports","printSimpleReports","printTableReports","reportsToBaseline","Math","max","ceil","cpus","length","BENCHMARK_URL","Symbol","for","pre","post","title","run","ctx","fn","teardown","measures","setup","measure","push","create","feed","self","target","execute","options","workers","warmupCycles","maxCycles","DEFAULT_CYCLES","minCycles","absThreshold","relThreshold","gcObserver","reportTypes","progress","progressInterval","Error","benchmarkUrl","totalBenchmarks","reduce","acc","t","progressMap","Map","completedBenchmarks","bar","Progress","total","width","complete","incomplete","onProgress","info","set","id","totalProgress","values","a","b","label","slice","update","undefined","executor","createExecutor","reports","targetReport","measureReport","feeds","data","then","delete","drain","kill","terminate","report","console","group","count","heapUsedKB","dceWarning","error","benchError","metrics","log","output","Object","entries","map","key","toString","join","extras","extrasStr","groupEnd","table","fromEntries","padding","row","String","JSON","stringify","firstValid","find","f","keys","filter","k","header","separator","opsKey","value","valueOf","maxValue","v","maxLabelLen","padEnd","barLen","round","repeat","formatted","toLocaleString","maximumFractionDigits","results","metric","version","timestamp","Date","toISOString","baseline","threshold","baselineData","current","baselineValue","change","isOps","improved","regressed","indicator","changeStr","toFixed","coloredChange"],"mappings":";;;;;;;;;;;QAWaA;eAAAA;;QAoFAC;eAAAA;;QAlEAC;eAAAA;;QApBAC;eAAAA;;QA+EAC;eAAAA;;QA9CAC;eAAAA;;QAVAC;eAAAA;;QAqCAC;eAAAA;;QAVAC;eAAAA;;QA6UAC;eAAAA;;QA9DAC;eAAAA;;QAnDAC;eAAAA;;QAmBAC;eAAAA;;QA7DAC;eAAAA;;QAwBAC;eAAAA;;QA4GAC;eAAAA;;;wBAjXQ;iEACA;6BAC2C;0BAC4D;;;;;;AAMrH,MAAMZ,kBAAkBa,KAAKC,GAAG,CAAC,GAAGD,KAAKE,IAAI,CAACC,IAAAA,YAAI,IAAGC,MAAM,GAAG;AAE9D,MAAMpB,gBAAgB,AAAC,CAAA,WAAa,CAAA,EAAG,WAAW;AACzD,MAAMqB,gBAAgBC,OAAOC,GAAG,CAAC;AAiB1B,MAAMrB,uBAAuB;IAAC;CAAM;AAGpC,MAAMI;;;IACJkB,IAA+B;IAC/BC,KAAgC;IAEvC,YACE,AAAOC,KAAa,EACpB,AAAOC,GAA6B,CACpC;aAFOD,QAAAA;aACAC,MAAAA;IACN;AACL;AAEO,MAAMtB;IACX,CAAA,GAAI,CAAmC;IAEvC,YAAYuB,GAAqC,CAAE;QACjD,IAAI,CAAC,CAAA,GAAI,GAAGA;IACd;IAEAJ,IAAIK,EAA4B,EAA6B;QAC3D,IAAI,CAAC,CAAA,GAAI,CAACL,GAAG,GAAGK;QAChB,OAAO,IAAI;IACb;IACAJ,KAAKI,EAA4B,EAA6B;QAC5D,IAAI,CAAC,CAAA,GAAI,CAACJ,IAAI,GAAGI;QACjB,OAAO,IAAI;IACb;AACF;AAEO,MAAMrB;;;IACJsB,SAAgC;IAChCC,WAA+C,EAAE,CAAC;IAEzD,YACE,AAASL,KAAa,EACtB,AAASM,KAAuC,CAChD;aAFSN,QAAAA;aACAM,QAAAA;IACR;AACL;AAEO,MAAMzB;IACX,CAAA,GAAI,CAAkC;IAEtC,YAAYqB,GAAoC,CAAE;QAChD,IAAI,CAAC,CAAA,GAAI,GAAGA;IACd;IACAE,SAASD,EAAwB,EAA4B;QAC3D,IAAI,CAAC,CAAA,GAAI,CAACC,QAAQ,GAAGD;QAErB,OAAO,IAAI;IACb;IACAI,QAAQP,KAAa,EAAEC,GAA6B,EAA6B;QAC/E,MAAMM,UAAU,IAAI3B,eAAeoB,OAAOC;QAC1C,IAAI,CAAC,CAAA,GAAI,CAACI,QAAQ,CAACG,IAAI,CAACD;QAExB,OAAO,IAAI5B,QAAQ4B;IACrB;AACF;AAEO,MAAM7B;;;IACX,YACE,AAASsB,KAAa,EACtB,AAASG,EAAmB,CAC5B;aAFSH,QAAAA;aACAG,KAAAA;IACR;AACL;AAEO,MAAM5B;IACX,CAAA,OAAQ,GAAqC,EAAE,CAAC;IAChD,CAAA,KAAM,GAA0B,EAAE,CAAC;IACnC,CAAA,QAAS,GAAG,MAAM;IAIlB,OAAOkC,OAAUT,KAAa,EAAEG,EAA0B,EAAgB;QACxE,IAAIA,IAAI;YACN,OAAO,IAAI5B,UAAUyB,OAAOG;QAC9B,OAAO;YACL,OAAO,IAAI5B,UAAUyB;QACvB;IACF;IAIA,YAAYA,KAAa,EAAEG,EAA+B,CAAE;QAC1D,IAAIA,IAAI;YACN,IAAI,CAACO,IAAI,CAACV,OAAOG;QACnB,OAAO;YACL,IAAI,CAACO,IAAI,CAACV;QACZ;IACF;IAIAU,KAAQV,KAAa,EAAEG,EAA0B,EAAyB;QACxE,MAAMQ,OAAO,IAAI;QACjBA,KAAK,CAAA,KAAM,CAACH,IAAI,CAACL,KAAK,IAAIzB,YAAYsB,OAAOG,MAAM,IAAIzB,YAAYsB;QAEnE,OAAOW;IACT;IAIAC,OAAiBZ,KAAa,EAAEM,KAA8C,EAA4B;QACxG,MAAMM,SAAS,IAAI9B,cAAgCkB,OAAOM;QAC1D,IAAI,CAAC,CAAA,OAAQ,CAACE,IAAI,CAACI;QAEnB,OAAO,IAAI/B,OAAyB+B;IACtC;IAEA,MAAMC,QAAuEC,OAAoD,EAA8B;QAC7J,MAAM,EACJC,UAAUtC,eAAe,EACzBuC,eAAe,EAAE,EACjBC,YAAYC,wBAAc,EAC1BC,YAAY,EAAE,EACdC,eAAe,KAAK,EACpBC,eAAe,IAAI,EACnBC,aAAa,IAAI,EACjBC,cAAc/C,oBAAoC,EAClDgD,WAAW,KAAK,EAChBC,mBAAmB,GAAG,EACvB,GAAGX;QACJ,IAAI,IAAI,CAAC,CAAA,QAAS,EAAE;YAClB,MAAM,IAAIY,MAAM;QAClB;QACA,IAAI,CAAC,CAAA,QAAS,GAAG;QACjB,MAAMC,eAAe,AAACb,OAA8C,CAACnB,cAAc;QAEnF,MAAMiC,kBAAkB,IAAI,CAAC,CAAA,OAAQ,CAACC,MAAM,CAAC,CAACC,KAAKC,IAAMD,MAAMC,EAAE1B,QAAQ,CAACX,MAAM,GAAG,IAAI,CAAC,CAAA,KAAM,CAACA,MAAM,EAAE;QACvG,MAAMsC,cAAc,IAAIC;QACxB,IAAIC,sBAAsB;QAC1B,IAAIC,MAAuB;QAE3B,IAAIX,YAAYI,kBAAkB,GAAG;YACnCO,MAAM,IAAIC,iBAAQ,CAAC,4CAA4C;gBAC7DC,OAAOT,kBAAkB;gBACzBU,OAAO;gBACPC,UAAU;gBACVC,YAAY;YACd;QACF;QAEA,MAAMC,aAAajB,WACf,CAACkB;YACCV,YAAYW,GAAG,CAACD,KAAKE,EAAE,EAAEF,KAAKlB,QAAQ;YACtC,MAAMqB,gBAAgB,AAACX,CAAAA,sBAAsB;mBAAIF,YAAYc,MAAM;aAAG,CAACjB,MAAM,CAAC,CAACkB,GAAGC,IAAMD,IAAIC,GAAG,EAAC,IAAK;YACrG,MAAMC,QAAQP,KAAKE,EAAE,CAAClD,MAAM,GAAG,KAAKgD,KAAKE,EAAE,CAACM,KAAK,CAAC,GAAG,MAAM,QAAQR,KAAKE,EAAE;YAC1ET,KAAKgB,OAAON,gBAAiBjB,CAAAA,kBAAkB,GAAE,GAAI;gBAAEqB;YAAM;QAC/D,IACAG;QAEJ,MAAMC,WAAWC,IAAAA,2BAAc,EAAqB;YAClDvC;YACAC;YACAC;YACAE;YACAC;YACAC;YACAC;YACAC;YACAkB;YACAhB;YACA,CAAC9B,cAAc,EAAEgC;QACnB;QAEA,MAAM4B,UAA6B,EAAE;QACrC,KAAK,MAAM3C,UAAU,IAAI,CAAC,CAAA,OAAQ,CAAE;YAClC,MAAM4C,eAAgC;gBAAE5C,QAAQA,OAAOZ,KAAK;gBAAEK,UAAU,EAAE;YAAC;YAC3E,KAAK,MAAME,WAAWK,OAAOP,QAAQ,CAAE;gBACrC,MAAMoD,gBAAkC;oBAAElD,SAASA,QAAQP,KAAK;oBAAE0D,OAAO,EAAE;gBAAC;gBAC5E,KAAK,MAAMhD,QAAQ,IAAI,CAAC,CAAA,KAAM,CAAE;oBAC9B,MAAMkC,KAAK,GAAGhC,OAAOZ,KAAK,CAAC,CAAC,EAAEO,QAAQP,KAAK,CAAC,CAAC,EAAEU,KAAKV,KAAK,EAAE;oBAC3D,MAAM2D,OAAO,MAAMjD,KAAKP,EAAE;oBAC1BkD,SACG7C,IAAI,CAAoB;wBACvBoC;wBACAtC,OAAOM,OAAON,KAAK;wBACnBF,UAAUQ,OAAOR,QAAQ;wBACzBN,KAAKS,QAAQT,GAAG;wBAChBG,KAAKM,QAAQN,GAAG;wBAChBF,MAAMQ,QAAQR,IAAI;wBAClB4D;oBACF,GACCC,IAAI,CAAC,CAACD;wBACL3B,YAAY6B,MAAM,CAACjB;wBACnBV;wBACAuB,cAAcC,KAAK,CAAClD,IAAI,CAAC;4BACvBE,MAAMA,KAAKV,KAAK;4BAChB2D;wBACF;oBACF;gBACJ;gBACAH,aAAanD,QAAQ,CAACG,IAAI,CAACiD;YAC7B;YACAF,QAAQ/C,IAAI,CAACgD;QACf;QACA,MAAMH,SAASS,KAAK;QACpBT,SAASU,IAAI;QAEb,IAAI5B,KAAK;YACPA,IAAIgB,MAAM,CAAC,GAAG;gBAAEF,OAAO;YAAO;YAC9Bd,IAAI6B,SAAS;QACf;QAEA,OAAOT;IACT;AACF;AAEO,MAAMpE,qBAAqB,CAA2BoE;IAC3D,KAAK,MAAMU,UAAUV,QAAS;QAC5B,KAAK,MAAM,EAAEhD,OAAO,EAAEmD,KAAK,EAAE,IAAIO,OAAO5D,QAAQ,CAAE;YAChD6D,QAAQC,KAAK,CAAC,MAAMF,OAAOrD,MAAM,EAAEL;YACnC,KAAK,MAAM,EAAEG,IAAI,EAAEiD,IAAI,EAAE,IAAID,MAAO;gBAClC,MAAM,EAAEU,KAAK,EAAEC,UAAU,EAAEC,UAAU,EAAEC,OAAOC,UAAU,EAAE,GAAGC,SAAS,GAAGd;gBACzE,IAAIa,YAAY;oBACdN,QAAQQ,GAAG,CAAChE,MAAM,CAAC,gBAAgB,EAAE8D,WAAW,QAAQ,CAAC;oBACzD;gBACF;gBACA,MAAMG,SAASC,OAAOC,OAAO,CAACJ,SAC3BK,GAAG,CAAC,CAAC,CAACC,KAAKd,OAAO,GAAK,GAAGc,IAAI,EAAE,EAAE,AAACd,OAAkCe,QAAQ,IAAI,EACjFC,IAAI,CAAC;gBACR,MAAMC,SAAmB,EAAE;gBAC3B,IAAIb,YAAYa,OAAO1E,IAAI,CAAC,CAAC,MAAM,EAAE6D,WAAW,EAAE,CAAC;gBACnD,IAAIC,YAAYY,OAAO1E,IAAI,CAAC;gBAC5B,MAAM2E,YAAYD,OAAOxF,MAAM,GAAG,IAAI,CAAC,EAAE,EAAEwF,OAAOD,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG;gBAClEf,QAAQQ,GAAG,CAAChE,MAAMiE,SAASQ;YAC7B;YACAjB,QAAQkB,QAAQ;QAClB;IACF;AACF;AAEO,MAAMhG,oBAAoB,CAA2BmE;IAC1D,KAAK,MAAMU,UAAUV,QAAS;QAC5B,KAAK,MAAM,EAAEhD,OAAO,EAAEmD,KAAK,EAAE,IAAIO,OAAO5D,QAAQ,CAAE;YAChD6D,QAAQQ,GAAG,CAAC,MAAMT,OAAOrD,MAAM,EAAEL;YACjC,MAAM8E,QAAiC,CAAC;YACxC,KAAK,MAAM,EAAE3E,IAAI,EAAEiD,IAAI,EAAE,IAAID,MAAO;gBAClC,MAAM,EAAEa,OAAOC,UAAU,EAAE,GAAGb;gBAC9B,IAAIa,YAAY;oBACda,KAAK,CAAC3E,KAAK,GAAG;wBAAE6D,OAAOC;oBAAW;gBACpC,OAAO;oBACLa,KAAK,CAAC3E,KAAK,GAAGkE,OAAOU,WAAW,CAACV,OAAOC,OAAO,CAAClB,MAAMmB,GAAG,CAAC,CAAC,CAACC,KAAKd,OAAO,GAAK;4BAACc;4BAAKd,OAAOe,QAAQ;yBAAG;gBACvG;YACF;YACAd,QAAQmB,KAAK,CAACA;QAChB;IACF;AACF;AAEO,MAAMpG,mBAAmB,CAA2BsE,SAA4BgC;IACrF,MAAMZ,SAAS,CAAC;IAChB,KAAK,MAAMV,UAAUV,QAAS;QAC5B,KAAK,MAAM,EAAEhD,OAAO,EAAEmD,KAAK,EAAE,IAAIO,OAAO5D,QAAQ,CAAE;YAChD,MAAMmF,MAAM,CAAC;YACb,KAAK,MAAM,EAAE9E,IAAI,EAAEiD,IAAI,EAAE,IAAID,MAAO;gBAClC,MAAM,EAAEa,OAAOC,UAAU,EAAE,GAAGb;gBAC9B,IAAIa,YAAY;oBACdgB,GAAG,CAAC9E,KAAK,GAAG;wBAAE6D,OAAOkB,OAAOjB;oBAAY;gBAC1C,OAAO;oBACLgB,GAAG,CAAC9E,KAAK,GAAGkE,OAAOU,WAAW,CAACV,OAAOC,OAAO,CAAClB,MAAMmB,GAAG,CAAC,CAAC,CAACC,KAAKd,OAAO,GAAK;4BAACc;4BAAKd,OAAOe,QAAQ;yBAAG;gBACrG;YACF;YACAL,MAAM,CAAC,GAAGV,OAAOrD,MAAM,CAAC,CAAC,EAAEL,SAAS,CAAC,GAAGiF;QAC1C;IACF;IACAtB,QAAQQ,GAAG,CAACgB,KAAKC,SAAS,CAAChB,QAAQ,MAAMY;AAC3C;AAEO,MAAMrG,uBAAuB,CAA2BqE;IAC7D,KAAK,MAAMU,UAAUV,QAAS;QAC5B,KAAK,MAAM,EAAEhD,OAAO,EAAEmD,KAAK,EAAE,IAAIO,OAAO5D,QAAQ,CAAE;YAChD6D,QAAQQ,GAAG,CAAC,CAAC,KAAK,EAAET,OAAOrD,MAAM,CAAC,GAAG,EAAEL,QAAQ,EAAE,CAAC;YAClD,IAAImD,MAAMhE,MAAM,KAAK,GAAG;YAExB,MAAMkG,aAAalC,MAAMmC,IAAI,CAAC,CAACC,IAAM,CAAC,AAACA,EAAEnC,IAAI,CAA6BY,KAAK;YAC/E,IAAI,CAACqB,YAAY;gBACf,KAAK,MAAM,EAAElF,IAAI,EAAEiD,IAAI,EAAE,IAAID,MAAO;oBAClCQ,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAEhE,KAAK,UAAU,EAAE,AAACiD,KAAiCY,KAAK,CAAC,EAAE,CAAC;gBAC/E;gBACA;YACF;YACA,MAAMwB,OAAOnB,OAAOmB,IAAI,CAACH,WAAWjC,IAAI,EAAEqC,MAAM,CAAC,CAACC,IAAMA,MAAM,WAAWA,MAAM;YAC/E,MAAMC,SAAS;gBAAC;mBAAWH;aAAK,CAACd,IAAI,CAAC;YACtC,MAAMkB,YAAY;gBAAC;mBAAUJ,KAAKjB,GAAG,CAAC,IAAM;aAAO,CAACG,IAAI,CAAC;YAEzDf,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAEwB,OAAO,EAAE,CAAC;YAC3BhC,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAEyB,UAAU,EAAE,CAAC;YAE9B,KAAK,MAAM,EAAEzF,IAAI,EAAEiD,IAAI,EAAE,IAAID,MAAO;gBAClC,IAAI,AAACC,KAAiCY,KAAK,EAAE;oBAC3CL,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAEhE,KAAK,UAAU,EAAE,AAACiD,KAAiCY,KAAK,CAAC,EAAE,CAAC;oBAC7E;gBACF;gBACA,MAAMzB,SAASiD,KAAKjB,GAAG,CAAC,CAACmB,IAAM,AAACtC,IAA+C,CAACsC,EAAE,EAAEjB,cAAc;gBAClGd,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAE;oBAAChE;uBAASoC;iBAAO,CAACmC,IAAI,CAAC,OAAO,EAAE,CAAC;YACpD;QACF;IACF;AACF;AAEO,MAAMjG,wBAAwB,CAA2BuE,SAA4BjB,QAAQ,EAAE;IACpG,KAAK,MAAM2B,UAAUV,QAAS;QAC5B,KAAK,MAAM,EAAEhD,OAAO,EAAEmD,KAAK,EAAE,IAAIO,OAAO5D,QAAQ,CAAE;YAChD6D,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAET,OAAOrD,MAAM,CAAC,GAAG,EAAEL,QAAQ,EAAE,CAAC;YAE/C,MAAM6F,SAAS;YACf,MAAMtD,SAASY,MAAMoB,GAAG,CAAC,CAACgB;gBACxB,MAAM,EAAEvB,OAAOC,UAAU,EAAE,GAAGsB,EAAEnC,IAAI;gBACpC,OAAO;oBACLjD,MAAMoF,EAAEpF,IAAI;oBACZ2F,OAAO7B,aAAa,IAAK,AAACsB,EAAEnC,IAAI,AAA0C,CAACyC,OAAO,EAAEE,aAAa;oBACjG/B,OAAOC;gBACT;YACF;YAEA,MAAM+B,WAAWjH,KAAKC,GAAG,IAAIuD,OAAOgC,GAAG,CAAC,CAAC0B,IAAMA,EAAEH,KAAK;YACtD,MAAMI,cAAcnH,KAAKC,GAAG,IAAIuD,OAAOgC,GAAG,CAAC,CAAC0B,IAAMA,EAAE9F,IAAI,CAAChB,MAAM;YAE/D,KAAK,MAAM,EAAEgB,IAAI,EAAE2F,KAAK,EAAE9B,KAAK,EAAE,IAAIzB,OAAQ;gBAC3C,MAAMG,QAAQvC,KAAKgG,MAAM,CAACD;gBAC1B,IAAIlC,OAAO;oBACTL,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAEzB,MAAM,mBAAmB,EAAEsB,MAAM,QAAQ,CAAC;oBAC3D;gBACF;gBACA,MAAMoC,SAASJ,WAAW,IAAIjH,KAAKsH,KAAK,CAAC,AAACP,QAAQE,WAAYjE,SAAS;gBACvE,MAAMH,MAAM,SAAS0E,MAAM,CAACF;gBAC5B,MAAMG,YAAYT,MAAMU,cAAc,CAAC,SAAS;oBAAEC,uBAAuB;gBAAE;gBAC3E9C,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAEzB,MAAM,GAAG,EAAEd,IAAI,CAAC,EAAE2E,UAAU,MAAM,CAAC;YACtD;QACF;IACF;AACF;AAQO,MAAMzH,oBAAoB,CAA2BkE;IAC1D,MAAM0D,UAAkD,CAAC;IACzD,KAAK,MAAMhD,UAAUV,QAAS;QAC5B,KAAK,MAAM,EAAEhD,OAAO,EAAEmD,KAAK,EAAE,IAAIO,OAAO5D,QAAQ,CAAE;YAChD,KAAK,MAAM,EAAEK,IAAI,EAAEiD,IAAI,EAAE,IAAID,MAAO;gBAClC,IAAI,AAACC,KAAiCY,KAAK,EAAE;gBAC7C,MAAMQ,MAAM,GAAGd,OAAOrD,MAAM,CAAC,CAAC,EAAEL,QAAQ,CAAC,EAAEG,MAAM;gBACjDuG,OAAO,CAAClC,IAAI,GAAG,CAAC;gBAChB,KAAK,MAAM,CAACmC,QAAQb,MAAM,IAAIzB,OAAOC,OAAO,CAAClB,MAAO;oBAClD,IAAIuD,WAAW,WAAW,OAAO,AAACb,MAAgCC,OAAO,KAAK,YAAY;wBACxFW,OAAO,CAAClC,IAAI,CAACmC,OAAO,GAAG,AAACb,MAAgCC,OAAO;oBACjE;gBACF;YACF;QACF;IACF;IACA,OAAO;QACLa,SAAS;QACTC,WAAW,IAAIC,OAAOC,WAAW;QACjCL;IACF;AACF;AAEO,MAAMlI,yBAAyB,CAA2BwE,SAA4BgE,UAAwBC,YAAY,CAAC;IAChI,KAAK,MAAMvD,UAAUV,QAAS;QAC5B,KAAK,MAAM,EAAEhD,OAAO,EAAEmD,KAAK,EAAE,IAAIO,OAAO5D,QAAQ,CAAE;YAChD6D,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAET,OAAOrD,MAAM,CAAC,GAAG,EAAEL,QAAQ,EAAE,CAAC;YAE/C,KAAK,MAAM,EAAEG,IAAI,EAAEiD,IAAI,EAAE,IAAID,MAAO;gBAClC,MAAMqB,MAAM,GAAGd,OAAOrD,MAAM,CAAC,CAAC,EAAEL,QAAQ,CAAC,EAAEG,MAAM;gBACjD,MAAM+G,eAAeF,SAASN,OAAO,CAAClC,IAAI;gBAE1Cb,QAAQQ,GAAG,CAAC,CAAC,EAAE,EAAEhE,KAAK,CAAC,CAAC;gBAExB,IAAI,AAACiD,KAAiCY,KAAK,EAAE;oBAC3CL,QAAQQ,GAAG,CAAC,CAAC,oBAAoB,EAAE,AAACf,KAAiCY,KAAK,CAAC,QAAQ,CAAC;oBACpF;gBACF;gBAEA,KAAK,MAAM,CAAC2C,QAAQb,MAAM,IAAIzB,OAAOC,OAAO,CAAClB,MAAO;oBAClD,IAAIuD,WAAW,SAAS;oBACxB,MAAMQ,UAAU,AAACrB,MAAgCC,OAAO;oBACxD,MAAMqB,gBAAgBF,cAAc,CAACP,OAAO;oBAE5C,IAAIS,kBAAkBvE,aAAauE,kBAAkB,GAAG;wBACtD,MAAMC,SAAS,AAAEF,CAAAA,UAAUC,aAAY,IAAKA,gBAAiB;wBAC7D,MAAME,QAAQX,WAAW;wBACzB,MAAMY,WAAWD,QAAQD,SAASJ,YAAYI,SAAS,CAACJ;wBACxD,MAAMO,YAAYF,QAAQD,SAAS,CAACJ,YAAYI,SAASJ;wBAEzD,IAAIQ,YAAY;wBAChB,IAAIF,UAAUE,YAAY;6BACrB,IAAID,WAAWC,YAAY;wBAEhC,MAAMC,YAAYL,UAAU,IAAI,CAAC,CAAC,EAAEA,OAAOM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,GAAGN,OAAOM,OAAO,CAAC,GAAG,CAAC,CAAC;wBAClF,MAAMC,gBAAgBJ,YAAY,CAAC,QAAQ,EAAEE,UAAU,OAAO,CAAC,GAAGH,WAAW,CAAC,QAAQ,EAAEG,UAAU,OAAO,CAAC,GAAGA;wBAE7G/D,QAAQQ,GAAG,CAAC,CAAC,IAAI,EAAEsD,UAAU,CAAC,EAAEd,OAAO,EAAE,EAAE,AAACb,MAAiCrB,QAAQ,GAAG,EAAE,EAAEmD,cAAc,CAAC,CAAC;oBAC9G,OAAO;wBACLjE,QAAQQ,GAAG,CAAC,CAAC,MAAM,EAAEwC,OAAO,EAAE,EAAE,AAACb,MAAiCrB,QAAQ,GAAG,MAAM,CAAC;oBACtF;gBACF;YACF;QACF;IACF;IAEAd,QAAQQ,GAAG,CAAC,CAAC,iBAAiB,EAAE6C,SAASH,SAAS,EAAE;AACtD"}
|