overtake 1.1.2 → 1.1.4
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/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/reporter.cjs +14 -13
- package/build/reporter.cjs.map +1 -1
- package/build/reporter.js +14 -13
- package/build/reporter.js.map +1 -1
- package/build/runner.cjs +267 -66
- package/build/runner.cjs.map +1 -1
- package/build/runner.js +268 -67
- package/build/runner.js.map +1 -1
- package/build/types.cjs +4 -0
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +2 -1
- package/build/types.js +1 -0
- package/build/types.js.map +1 -1
- package/build/worker.cjs +1 -1
- package/build/worker.cjs.map +1 -1
- package/build/worker.js +1 -1
- package/build/worker.js.map +1 -1
- package/examples/accuracy.ts +30 -5
- package/package.json +1 -1
- package/src/gc-watcher.ts +5 -6
- package/src/reporter.ts +14 -14
- package/src/runner.ts +327 -74
- package/src/types.ts +2 -1
- package/src/worker.ts +1 -1
package/build/runner.js
CHANGED
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
import { performance, PerformanceObserver } from 'node:perf_hooks';
|
|
2
|
-
import { Control } from "./types.js";
|
|
2
|
+
import { Control, DURATION_SCALE } from "./types.js";
|
|
3
3
|
import { GCWatcher } from "./gc-watcher.js";
|
|
4
4
|
const COMPLETE_VALUE = 100_00;
|
|
5
5
|
const hr = process.hrtime.bigint.bind(process.hrtime);
|
|
6
|
-
const
|
|
6
|
+
const sink = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
7
|
+
const consume = (value)=>{
|
|
8
|
+
let payload = 0;
|
|
9
|
+
switch(typeof value){
|
|
10
|
+
case 'number':
|
|
11
|
+
payload = Number.isFinite(value) ? Math.trunc(value) : 0;
|
|
12
|
+
break;
|
|
13
|
+
case 'bigint':
|
|
14
|
+
payload = Number(value & 0xffff_ffffn);
|
|
15
|
+
break;
|
|
16
|
+
case 'string':
|
|
17
|
+
payload = value.length;
|
|
18
|
+
break;
|
|
19
|
+
case 'boolean':
|
|
20
|
+
payload = value ? 1 : 0;
|
|
21
|
+
break;
|
|
22
|
+
case 'object':
|
|
23
|
+
payload = value === null ? 0 : 1;
|
|
24
|
+
break;
|
|
25
|
+
case 'function':
|
|
26
|
+
payload = 1;
|
|
27
|
+
break;
|
|
28
|
+
default:
|
|
29
|
+
payload = -1;
|
|
30
|
+
}
|
|
31
|
+
Atomics.xor(sink, 0, payload);
|
|
32
|
+
};
|
|
33
|
+
const runSync = (run, overhead)=>{
|
|
7
34
|
return (...args)=>{
|
|
8
35
|
const start = hr();
|
|
9
|
-
run(...args);
|
|
10
|
-
|
|
36
|
+
const result = run(...args);
|
|
37
|
+
consume(result);
|
|
38
|
+
const duration = hr() - start;
|
|
39
|
+
return duration > overhead ? duration - overhead : 0n;
|
|
11
40
|
};
|
|
12
41
|
};
|
|
13
42
|
const runAsync = (run)=>{
|
|
14
43
|
return async (...args)=>{
|
|
15
44
|
const start = hr();
|
|
16
|
-
await run(...args);
|
|
45
|
+
const result = await run(...args);
|
|
46
|
+
consume(result);
|
|
17
47
|
return hr() - start;
|
|
18
48
|
};
|
|
19
49
|
};
|
|
@@ -27,16 +57,62 @@ const GC_STRIDE = 32;
|
|
|
27
57
|
const OUTLIER_MULTIPLIER = 4;
|
|
28
58
|
const OUTLIER_IQR_MULTIPLIER = 3;
|
|
29
59
|
const OUTLIER_WINDOW = 64;
|
|
30
|
-
const
|
|
60
|
+
const OUTLIER_ABS_THRESHOLD_NS = 10_000;
|
|
61
|
+
const BASELINE_SAMPLES = 16;
|
|
62
|
+
const OUTLIER_SCRATCH = new Float64Array(OUTLIER_WINDOW);
|
|
63
|
+
const measureTimerOverhead = ()=>{
|
|
64
|
+
let total = 0n;
|
|
65
|
+
for(let i = 0; i < BASELINE_SAMPLES; i++){
|
|
66
|
+
const start = hr();
|
|
67
|
+
consume(0);
|
|
68
|
+
total += hr() - start;
|
|
69
|
+
}
|
|
70
|
+
return total / BigInt(BASELINE_SAMPLES);
|
|
71
|
+
};
|
|
72
|
+
const collectSample = async ({ batchSize, run, runRaw, runIsAsync, pre, preIsAsync, post, postIsAsync, context, data, nextNonce })=>{
|
|
73
|
+
const canBatchTime = !runIsAsync && !pre && !post;
|
|
74
|
+
if (canBatchTime) {
|
|
75
|
+
const batchStart = hr();
|
|
76
|
+
if (nextNonce) {
|
|
77
|
+
for(let b = 0; b < batchSize; b++){
|
|
78
|
+
consume(runRaw(context, data, nextNonce()));
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
for(let b = 0; b < batchSize; b++){
|
|
82
|
+
consume(runRaw(context, data));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return (hr() - batchStart) * DURATION_SCALE / BigInt(batchSize);
|
|
86
|
+
}
|
|
31
87
|
let sampleDuration = 0n;
|
|
32
88
|
for(let b = 0; b < batchSize; b++){
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
89
|
+
if (pre) {
|
|
90
|
+
if (preIsAsync) {
|
|
91
|
+
await pre(context, data);
|
|
92
|
+
} else {
|
|
93
|
+
pre(context, data);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (runIsAsync) {
|
|
97
|
+
const runAsyncFn = run;
|
|
98
|
+
const duration = nextNonce ? await runAsyncFn(context, data, nextNonce()) : await runAsyncFn(context, data);
|
|
99
|
+
sampleDuration += duration;
|
|
100
|
+
} else {
|
|
101
|
+
const runSyncFn = run;
|
|
102
|
+
const duration = nextNonce ? runSyncFn(context, data, nextNonce()) : runSyncFn(context, data);
|
|
103
|
+
sampleDuration += duration;
|
|
104
|
+
}
|
|
105
|
+
if (post) {
|
|
106
|
+
if (postIsAsync) {
|
|
107
|
+
await post(context, data);
|
|
108
|
+
} else {
|
|
109
|
+
post(context, data);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
36
112
|
}
|
|
37
|
-
return sampleDuration / BigInt(batchSize);
|
|
113
|
+
return sampleDuration * DURATION_SCALE / BigInt(batchSize);
|
|
38
114
|
};
|
|
39
|
-
const tuneParameters = async ({ initialBatch, run, pre, post, context, data, minCycles, relThreshold, maxCycles })=>{
|
|
115
|
+
const tuneParameters = async ({ initialBatch, run, runRaw, runIsAsync, pre, preIsAsync, post, postIsAsync, context, data, minCycles, relThreshold, maxCycles, nextNonce })=>{
|
|
40
116
|
let batchSize = initialBatch;
|
|
41
117
|
let bestCv = Number.POSITIVE_INFINITY;
|
|
42
118
|
let bestBatch = batchSize;
|
|
@@ -44,7 +120,19 @@ const tuneParameters = async ({ initialBatch, run, pre, post, context, data, min
|
|
|
44
120
|
const samples = [];
|
|
45
121
|
const sampleCount = Math.min(8, maxCycles);
|
|
46
122
|
for(let s = 0; s < sampleCount; s++){
|
|
47
|
-
const duration = await collectSample(
|
|
123
|
+
const duration = await collectSample({
|
|
124
|
+
batchSize,
|
|
125
|
+
run,
|
|
126
|
+
runRaw,
|
|
127
|
+
runIsAsync,
|
|
128
|
+
pre,
|
|
129
|
+
preIsAsync,
|
|
130
|
+
post,
|
|
131
|
+
postIsAsync,
|
|
132
|
+
context,
|
|
133
|
+
data,
|
|
134
|
+
nextNonce
|
|
135
|
+
});
|
|
48
136
|
samples.push(Number(duration));
|
|
49
137
|
}
|
|
50
138
|
const mean = samples.reduce((acc, v)=>acc + v, 0) / samples.length;
|
|
@@ -124,15 +212,17 @@ const medianAndIqr = (arr)=>{
|
|
|
124
212
|
median: 0,
|
|
125
213
|
iqr: 0
|
|
126
214
|
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
215
|
+
for(let i = 0; i < arr.length; i++){
|
|
216
|
+
OUTLIER_SCRATCH[i] = arr[i];
|
|
217
|
+
}
|
|
218
|
+
const view = OUTLIER_SCRATCH.subarray(0, arr.length);
|
|
219
|
+
view.sort();
|
|
220
|
+
const mid = Math.floor(view.length / 2);
|
|
221
|
+
const median = view.length % 2 === 0 ? (view[mid - 1] + view[mid]) / 2 : view[mid];
|
|
222
|
+
const q1Idx = Math.floor(view.length * 0.25);
|
|
223
|
+
const q3Idx = Math.floor(view.length * 0.75);
|
|
224
|
+
const q1 = view[q1Idx];
|
|
225
|
+
const q3 = view[q3Idx];
|
|
136
226
|
return {
|
|
137
227
|
median,
|
|
138
228
|
iqr: q3 - q1
|
|
@@ -152,56 +242,132 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
|
|
|
152
242
|
control[Control.PROGRESS] = 0;
|
|
153
243
|
control[Control.COMPLETE] = 255;
|
|
154
244
|
const context = await setup?.();
|
|
245
|
+
const input = data;
|
|
155
246
|
const maxCycles = durations.length;
|
|
156
247
|
const gcWatcher = gcObserver ? new GCWatcher() : null;
|
|
157
248
|
const gcTracker = gcObserver ? createGCTracker() : null;
|
|
158
249
|
try {
|
|
159
|
-
|
|
250
|
+
let preIsAsync = false;
|
|
251
|
+
if (pre) {
|
|
252
|
+
const preResult = pre(context, input);
|
|
253
|
+
preIsAsync = isThenable(preResult);
|
|
254
|
+
if (preIsAsync) {
|
|
255
|
+
await preResult;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
160
258
|
const probeStart = hr();
|
|
161
|
-
const probeResult = runRaw(context,
|
|
162
|
-
const
|
|
163
|
-
if (
|
|
164
|
-
await probeResult;
|
|
259
|
+
const probeResult = runRaw(context, input);
|
|
260
|
+
const runIsAsync = isThenable(probeResult);
|
|
261
|
+
if (runIsAsync) {
|
|
262
|
+
const resolved = await probeResult;
|
|
263
|
+
consume(resolved);
|
|
264
|
+
} else {
|
|
265
|
+
consume(probeResult);
|
|
266
|
+
}
|
|
267
|
+
const durationProbeRaw = hr() - probeStart;
|
|
268
|
+
let postIsAsync = false;
|
|
269
|
+
if (post) {
|
|
270
|
+
const postResult = post(context, input);
|
|
271
|
+
postIsAsync = isThenable(postResult);
|
|
272
|
+
if (postIsAsync) {
|
|
273
|
+
await postResult;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const timerOverhead = runIsAsync ? 0n : measureTimerOverhead();
|
|
277
|
+
let durationProbe = runIsAsync ? durationProbeRaw : durationProbeRaw > timerOverhead ? durationProbeRaw - timerOverhead : 0n;
|
|
278
|
+
const shouldPerturbInput = process.env.OVERTAKE_PERTURB_INPUT === '1';
|
|
279
|
+
let nonce = 0;
|
|
280
|
+
const nextNonce = shouldPerturbInput ? ()=>{
|
|
281
|
+
nonce = nonce + 1 | 0;
|
|
282
|
+
return nonce;
|
|
283
|
+
} : null;
|
|
284
|
+
if (!runIsAsync && !pre && !post) {
|
|
285
|
+
const batchProbeSize = 10_000;
|
|
286
|
+
const batchProbeStart = hr();
|
|
287
|
+
if (nextNonce) {
|
|
288
|
+
for(let i = 0; i < batchProbeSize; i++){
|
|
289
|
+
consume(runRaw(context, input, nextNonce()));
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
for(let i = 0; i < batchProbeSize; i++){
|
|
293
|
+
consume(runRaw(context, input));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
durationProbe = (hr() - batchProbeStart) / BigInt(batchProbeSize);
|
|
165
297
|
}
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
const run =
|
|
298
|
+
const runTimedSync = runIsAsync ? null : runSync(runRaw, timerOverhead);
|
|
299
|
+
const runTimedAsync = runIsAsync ? runAsync(runRaw) : null;
|
|
300
|
+
const run = runIsAsync ? runTimedAsync : runTimedSync;
|
|
301
|
+
const runOnceSync = runIsAsync ? null : nextNonce ? (ctx, dataValue)=>runTimedSync(ctx, dataValue, nextNonce()) : runTimedSync;
|
|
302
|
+
const runOnceAsync = runIsAsync ? nextNonce ? (ctx, dataValue)=>runTimedAsync(ctx, dataValue, nextNonce()) : runTimedAsync : null;
|
|
303
|
+
const preSync = preIsAsync ? null : pre;
|
|
304
|
+
const preAsync = preIsAsync ? pre : null;
|
|
305
|
+
const postSync = postIsAsync ? null : post;
|
|
306
|
+
const postAsync = postIsAsync ? post : null;
|
|
169
307
|
const durationPerRun = durationProbe === 0n ? 1n : durationProbe;
|
|
170
308
|
const suggestedBatch = Number(TARGET_SAMPLE_NS / durationPerRun);
|
|
171
|
-
const
|
|
309
|
+
const minBatchForFastOps = durationProbe < 100n ? 100_000 : 1;
|
|
310
|
+
const initialBatchSize = Math.min(MAX_BATCH, Math.max(minBatchForFastOps, suggestedBatch));
|
|
172
311
|
const tuned = await tuneParameters({
|
|
173
312
|
initialBatch: initialBatchSize,
|
|
174
313
|
run,
|
|
314
|
+
runRaw,
|
|
315
|
+
runIsAsync,
|
|
175
316
|
pre,
|
|
317
|
+
preIsAsync,
|
|
176
318
|
post,
|
|
319
|
+
postIsAsync,
|
|
177
320
|
context,
|
|
178
|
-
data:
|
|
321
|
+
data: input,
|
|
179
322
|
minCycles,
|
|
180
323
|
relThreshold,
|
|
181
|
-
maxCycles
|
|
324
|
+
maxCycles,
|
|
325
|
+
nextNonce
|
|
182
326
|
});
|
|
183
327
|
let batchSize = tuned.batchSize;
|
|
184
328
|
minCycles = tuned.minCycles;
|
|
185
329
|
relThreshold = tuned.relThreshold;
|
|
186
|
-
const warmupStart =
|
|
330
|
+
const warmupStart = performance.now();
|
|
187
331
|
let warmupRemaining = warmupCycles;
|
|
188
332
|
const warmupWindow = [];
|
|
189
333
|
const warmupCap = Math.max(warmupCycles, Math.min(maxCycles, warmupCycles * 4 || 1000));
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
334
|
+
const canBatchTime = !runIsAsync && !preSync && !preAsync && !postSync && !postAsync;
|
|
335
|
+
const runWarmup = async ()=>{
|
|
336
|
+
if (canBatchTime) {
|
|
337
|
+
const batchStart = hr();
|
|
338
|
+
if (nextNonce) {
|
|
339
|
+
for(let b = 0; b < batchSize; b++){
|
|
340
|
+
consume(runRaw(context, input, nextNonce()));
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
for(let b = 0; b < batchSize; b++){
|
|
344
|
+
consume(runRaw(context, input));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return (hr() - batchStart) * DURATION_SCALE / BigInt(batchSize);
|
|
348
|
+
}
|
|
349
|
+
if (preSync) {
|
|
350
|
+
preSync(context, input);
|
|
351
|
+
} else if (preAsync) {
|
|
352
|
+
await preAsync(context, input);
|
|
353
|
+
}
|
|
354
|
+
const duration = runIsAsync ? await runOnceAsync(context, input) : runOnceSync(context, input);
|
|
355
|
+
if (postSync) {
|
|
356
|
+
postSync(context, input);
|
|
357
|
+
} else if (postAsync) {
|
|
358
|
+
await postAsync(context, input);
|
|
359
|
+
}
|
|
360
|
+
return duration;
|
|
361
|
+
};
|
|
362
|
+
while(performance.now() - warmupStart < 1_000 && warmupRemaining > 0){
|
|
363
|
+
const duration = await runWarmup();
|
|
364
|
+
pushWindow(warmupWindow, Number(duration), warmupCap);
|
|
196
365
|
warmupRemaining--;
|
|
197
366
|
}
|
|
198
367
|
let warmupDone = 0;
|
|
199
368
|
while(warmupDone < warmupRemaining){
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
await run(context, data);
|
|
203
|
-
await post?.(context, data);
|
|
204
|
-
pushWindow(warmupWindow, Number(hr() - start), warmupCap);
|
|
369
|
+
const duration = await runWarmup();
|
|
370
|
+
pushWindow(warmupWindow, Number(duration), warmupCap);
|
|
205
371
|
warmupDone++;
|
|
206
372
|
if (global.gc && warmupDone % GC_STRIDE === 0) {
|
|
207
373
|
global.gc();
|
|
@@ -212,45 +378,80 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
|
|
|
212
378
|
if (cv <= relThreshold * 2) {
|
|
213
379
|
break;
|
|
214
380
|
}
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
await run(context, data);
|
|
218
|
-
await post?.(context, data);
|
|
219
|
-
pushWindow(warmupWindow, Number(hr() - start), warmupCap);
|
|
381
|
+
const duration = await runWarmup();
|
|
382
|
+
pushWindow(warmupWindow, Number(duration), warmupCap);
|
|
220
383
|
}
|
|
221
384
|
let i = 0;
|
|
222
385
|
let mean = 0n;
|
|
223
386
|
let m2 = 0n;
|
|
224
387
|
const outlierWindow = [];
|
|
388
|
+
let skipped = 0;
|
|
389
|
+
const maxSkipped = maxCycles * 10;
|
|
390
|
+
let disableFiltering = false;
|
|
225
391
|
while(true){
|
|
226
392
|
if (i >= maxCycles) break;
|
|
393
|
+
if (!disableFiltering && skipped >= maxSkipped) {
|
|
394
|
+
console.error(`Warning: ${skipped} samples skipped due to noise/outlier detection. ` + `Disabling filtering for remaining samples. Results may have higher variance.`);
|
|
395
|
+
disableFiltering = true;
|
|
396
|
+
}
|
|
397
|
+
if (global.gc && i > 0 && i % GC_STRIDE === 0) {
|
|
398
|
+
global.gc();
|
|
399
|
+
}
|
|
227
400
|
const gcMarker = gcWatcher?.start();
|
|
228
401
|
const sampleStart = performance.now();
|
|
229
402
|
let sampleDuration = 0n;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
403
|
+
if (canBatchTime) {
|
|
404
|
+
const batchStart = hr();
|
|
405
|
+
if (nextNonce) {
|
|
406
|
+
for(let b = 0; b < batchSize; b++){
|
|
407
|
+
consume(runRaw(context, input, nextNonce()));
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
for(let b = 0; b < batchSize; b++){
|
|
411
|
+
consume(runRaw(context, input));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const batchDuration = hr() - batchStart;
|
|
415
|
+
sampleDuration = batchDuration * DURATION_SCALE / BigInt(batchSize);
|
|
416
|
+
} else {
|
|
417
|
+
for(let b = 0; b < batchSize; b++){
|
|
418
|
+
if (preSync) {
|
|
419
|
+
preSync(context, input);
|
|
420
|
+
} else if (preAsync) {
|
|
421
|
+
await preAsync(context, input);
|
|
422
|
+
}
|
|
423
|
+
const duration = runIsAsync ? await runOnceAsync(context, input) : runOnceSync(context, input);
|
|
424
|
+
sampleDuration += duration;
|
|
425
|
+
if (postSync) {
|
|
426
|
+
postSync(context, input);
|
|
427
|
+
} else if (postAsync) {
|
|
428
|
+
await postAsync(context, input);
|
|
429
|
+
}
|
|
236
430
|
}
|
|
431
|
+
sampleDuration = sampleDuration * DURATION_SCALE / BigInt(batchSize);
|
|
237
432
|
}
|
|
238
|
-
sampleDuration /= BigInt(batchSize);
|
|
239
433
|
const sampleEnd = performance.now();
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
434
|
+
if (!disableFiltering) {
|
|
435
|
+
const gcNoise = (gcMarker ? gcWatcher.seen(gcMarker) : false) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);
|
|
436
|
+
if (gcNoise) {
|
|
437
|
+
skipped++;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
243
440
|
}
|
|
244
441
|
const durationNumber = Number(sampleDuration);
|
|
245
442
|
pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
443
|
+
if (!disableFiltering) {
|
|
444
|
+
const { median, iqr } = medianAndIqr(outlierWindow);
|
|
445
|
+
const maxAllowed = median + OUTLIER_IQR_MULTIPLIER * iqr || Number.POSITIVE_INFINITY;
|
|
446
|
+
if (outlierWindow.length >= 8 && durationNumber > maxAllowed && durationNumber - median > OUTLIER_ABS_THRESHOLD_NS) {
|
|
447
|
+
skipped++;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
const meanNumber = Number(mean);
|
|
451
|
+
if (i >= 8 && meanNumber > 0 && durationNumber > OUTLIER_MULTIPLIER * meanNumber && durationNumber - meanNumber > OUTLIER_ABS_THRESHOLD_NS) {
|
|
452
|
+
skipped++;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
254
455
|
}
|
|
255
456
|
durations[i++] = sampleDuration;
|
|
256
457
|
const delta = sampleDuration - mean;
|
package/build/runner.js.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":["performance","PerformanceObserver","Control","GCWatcher","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","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","benchmark","setup","teardown","runRaw","warmupCycles","absThreshold","gcObserver","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","INDEX","PROGRESS","COMPLETE","gcWatcher","gcTracker","probeStart","probeResult","isAsync","durationProbe","durationPerRun","suggestedBatch","initialBatchSize","tuned","warmupStart","Date","now","warmupRemaining","warmupWindow","warmupCap","warmupDone","global","gc","m2","outlierWindow","gcMarker","sampleStart","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, DURATION_SCALE } 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) * DURATION_SCALE) / 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 * DURATION_SCALE) / 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) * DURATION_SCALE) / 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 * DURATION_SCALE) / 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 = (sampleDuration * DURATION_SCALE) / 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":["performance","PerformanceObserver","Control","DURATION_SCALE","GCWatcher","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","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","benchmark","setup","teardown","warmupCycles","absThreshold","gcObserver","durationsSAB","controlSAB","durations","BigUint64Array","control","INDEX","PROGRESS","COMPLETE","input","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","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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"}
|
package/build/types.cjs
CHANGED
|
@@ -18,6 +18,9 @@ _export(exports, {
|
|
|
18
18
|
get DEFAULT_CYCLES () {
|
|
19
19
|
return DEFAULT_CYCLES;
|
|
20
20
|
},
|
|
21
|
+
get DURATION_SCALE () {
|
|
22
|
+
return DURATION_SCALE;
|
|
23
|
+
},
|
|
21
24
|
get REPORT_TYPES () {
|
|
22
25
|
return REPORT_TYPES;
|
|
23
26
|
},
|
|
@@ -44,5 +47,6 @@ var Control = /*#__PURE__*/ function(Control) {
|
|
|
44
47
|
const CONTROL_SLOTS = Object.values(Control).length / 2;
|
|
45
48
|
const DEFAULT_CYCLES = 1_000;
|
|
46
49
|
const Z95 = 1.96;
|
|
50
|
+
const DURATION_SCALE = 1000n;
|
|
47
51
|
|
|
48
52
|
//# sourceMappingURL=types.cjs.map
|
package/build/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface SetupFn<TContext> {\n (): MaybePromise<TContext>;\n}\n\nexport interface TeardownFn<TContext> {\n (ctx: TContext): MaybePromise<void>;\n}\n\nexport interface StepFn<TContext, TInput> {\n (ctx: TContext, input: TInput): MaybePromise<
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type MaybePromise<T> = Promise<T> | PromiseLike<T> | T;\n\nexport interface SetupFn<TContext> {\n (): MaybePromise<TContext>;\n}\n\nexport interface TeardownFn<TContext> {\n (ctx: TContext): MaybePromise<void>;\n}\n\nexport interface StepFn<TContext, TInput> {\n (ctx: TContext, input: TInput): MaybePromise<unknown>;\n}\n\nexport interface FeedFn<TInput> {\n (): MaybePromise<TInput>;\n}\n\ntype _Sequence<To extends number, R extends unknown[]> = R['length'] extends To ? R[number] : _Sequence<To, [R['length'], ...R]>;\nexport type Sequence<To extends number> = number extends To ? number : _Sequence<To, []>;\nexport type Between<From extends number, To extends number> = Exclude<Sequence<To>, Sequence<From>>;\n\nexport type ReportType = 'ops' | 'min' | 'max' | 'mean' | 'median' | 'mode' | `p${Between<1, 100>}`;\nexport type ReportTypeList = readonly ReportType[];\nexport const REPORT_TYPES: ReportTypeList = Array.from({ length: 99 }, (_, idx) => `p${idx + 1}` as ReportType).concat(['ops', 'mean', 'min', 'max', 'median', 'mode']);\n\nexport interface ReportOptions<R extends ReportTypeList> {\n reportTypes: R;\n}\n\nexport interface BenchmarkOptions {\n warmupCycles?: number;\n minCycles?: number;\n absThreshold?: number; // ns\n relThreshold?: number; // %\n gcObserver?: boolean;\n}\n\nexport interface RunOptions<TContext, TInput> {\n setup?: SetupFn<TContext>;\n teardown?: TeardownFn<TContext>;\n pre?: StepFn<TContext, TInput>;\n run: StepFn<TContext, TInput>;\n post?: StepFn<TContext, TInput>;\n data?: TInput;\n}\n\nexport interface WorkerOptions extends Required<BenchmarkOptions> {\n benchmarkUrl?: string;\n setupCode?: string;\n teardownCode?: string;\n preCode?: string;\n runCode: string;\n postCode?: string;\n data?: unknown;\n\n durationsSAB: SharedArrayBuffer;\n controlSAB: SharedArrayBuffer;\n}\n\nexport interface Options<TContext, TInput> extends RunOptions<TContext, TInput>, BenchmarkOptions {\n durationsSAB: SharedArrayBuffer;\n controlSAB: SharedArrayBuffer;\n}\n\nexport enum Control {\n INDEX,\n PROGRESS,\n COMPLETE,\n}\n\nexport const CONTROL_SLOTS = Object.values(Control).length / 2;\nexport const DEFAULT_CYCLES = 1_000;\nexport const Z95 = 1.96;\nexport const DURATION_SCALE = 1000n;\n"],"names":["CONTROL_SLOTS","Control","DEFAULT_CYCLES","DURATION_SCALE","REPORT_TYPES","Z95","Array","from","length","_","idx","concat","Object","values"],"mappings":";;;;;;;;;;;QAuEaA;eAAAA;;QANDC;eAAAA;;QAOCC;eAAAA;;QAEAC;eAAAA;;QAlDAC;eAAAA;;QAiDAC;eAAAA;;;AAjDN,MAAMD,eAA+BE,MAAMC,IAAI,CAAC;IAAEC,QAAQ;AAAG,GAAG,CAACC,GAAGC,MAAQ,CAAC,CAAC,EAAEA,MAAM,GAAG,EAAgBC,MAAM,CAAC;IAAC;IAAO;IAAQ;IAAO;IAAO;IAAU;CAAO;AAyC/J,IAAA,AAAKV,iCAAAA;;;;WAAAA;;AAML,MAAMD,gBAAgBY,OAAOC,MAAM,CAACZ,SAASO,MAAM,GAAG;AACtD,MAAMN,iBAAiB;AACvB,MAAMG,MAAM;AACZ,MAAMF,iBAAiB,KAAK"}
|
package/build/types.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface TeardownFn<TContext> {
|
|
|
6
6
|
(ctx: TContext): MaybePromise<void>;
|
|
7
7
|
}
|
|
8
8
|
export interface StepFn<TContext, TInput> {
|
|
9
|
-
(ctx: TContext, input: TInput): MaybePromise<
|
|
9
|
+
(ctx: TContext, input: TInput): MaybePromise<unknown>;
|
|
10
10
|
}
|
|
11
11
|
export interface FeedFn<TInput> {
|
|
12
12
|
(): MaybePromise<TInput>;
|
|
@@ -58,4 +58,5 @@ export declare enum Control {
|
|
|
58
58
|
export declare const CONTROL_SLOTS: number;
|
|
59
59
|
export declare const DEFAULT_CYCLES = 1000;
|
|
60
60
|
export declare const Z95 = 1.96;
|
|
61
|
+
export declare const DURATION_SCALE = 1000n;
|
|
61
62
|
export {};
|
package/build/types.js
CHANGED
|
@@ -17,5 +17,6 @@ export var Control = /*#__PURE__*/ function(Control) {
|
|
|
17
17
|
export const CONTROL_SLOTS = Object.values(Control).length / 2;
|
|
18
18
|
export const DEFAULT_CYCLES = 1_000;
|
|
19
19
|
export const Z95 = 1.96;
|
|
20
|
+
export const DURATION_SCALE = 1000n;
|
|
20
21
|
|
|
21
22
|
//# sourceMappingURL=types.js.map
|