overtake 1.1.0 → 1.1.2

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.
@@ -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 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 = new GCWatcher();\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 = probeResult instanceof Promise;\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 = gcWatcher.seen(gcMarker) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);\n if (gcNoise) {\n continue;\n }\n\n const durationNumber = Number(sampleDuration);\n pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);\n const { median, iqr } = medianAndIqr(outlierWindow);\n const maxAllowed = median + OUTLIER_IQR_MULTIPLIER * iqr || Number.POSITIVE_INFINITY;\n if (outlierWindow.length >= 8 && durationNumber > maxAllowed) {\n continue;\n }\n\n const meanNumber = Number(mean);\n if (i >= 8 && meanNumber > 0 && durationNumber > OUTLIER_MULTIPLIER * meanNumber) {\n continue;\n }\n\n durations[i++] = sampleDuration;\n const delta = sampleDuration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (sampleDuration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n if (i % PROGRESS_STRIDE === 0) {\n control[Control.PROGRESS] = progress;\n }\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n gcTracker?.dispose?.();\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["benchmark","COMPLETE_VALUE","hr","process","hrtime","bigint","bind","runSync","run","args","start","runAsync","TARGET_SAMPLE_NS","MAX_BATCH","PROGRESS_STRIDE","GC_STRIDE","OUTLIER_MULTIPLIER","OUTLIER_IQR_MULTIPLIER","OUTLIER_WINDOW","collectSample","batchSize","pre","post","context","data","sampleDuration","b","BigInt","tuneParameters","initialBatch","minCycles","relThreshold","maxCycles","bestCv","Number","POSITIVE_INFINITY","bestBatch","attempt","samples","sampleCount","Math","min","s","duration","push","mean","reduce","acc","v","length","variance","max","stddev","sqrt","cv","tunedRel","tunedMin","ceil","createGCTracker","env","OVERTAKE_GC_OBSERVER","PerformanceObserver","events","observer","list","entry","getEntries","startTime","end","observe","entryTypes","overlaps","noisy","i","event","splice","dispose","disconnect","pushWindow","arr","value","cap","shift","medianAndIqr","median","iqr","sorted","sort","a","mid","floor","q1Idx","q3Idx","q1","q3","windowCv","setup","teardown","runRaw","warmupCycles","absThreshold","gcObserver","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","Control","INDEX","PROGRESS","COMPLETE","gcWatcher","GCWatcher","gcTracker","probeStart","probeResult","isAsync","Promise","durationProbe","durationPerRun","suggestedBatch","initialBatchSize","tuned","warmupStart","Date","now","warmupRemaining","warmupWindow","warmupCap","warmupDone","global","gc","m2","outlierWindow","gcMarker","sampleStart","performance","sampleEnd","gcNoise","seen","durationNumber","maxAllowed","meanNumber","delta","progress","meanNum","cov","e","console","error","stack"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { performance, PerformanceObserver } from 'node:perf_hooks';\nimport { Options, Control } from './types.js';\nimport { GCWatcher } from './gc-watcher.js';\nimport { StepFn, MaybePromise } from './types.js';\n\nconst COMPLETE_VALUE = 100_00;\n\nconst hr = process.hrtime.bigint.bind(process.hrtime);\n\nconst runSync = (run: Function) => {\n return (...args: unknown[]) => {\n const start = hr();\n run(...args);\n return hr() - start;\n };\n};\n\nconst runAsync = (run: Function) => {\n return async (...args: unknown[]) => {\n const start = hr();\n await run(...args);\n return hr() - start;\n };\n};\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> => {\n return value !== null && (typeof value === 'object' || typeof value === 'function') && typeof (value as PromiseLike<unknown>).then === 'function';\n};\n\nconst TARGET_SAMPLE_NS = 1_000_000n; // aim for ~1ms per measured sample\nconst MAX_BATCH = 1_048_576;\nconst PROGRESS_STRIDE = 16;\nconst GC_STRIDE = 32;\nconst OUTLIER_MULTIPLIER = 4;\nconst OUTLIER_IQR_MULTIPLIER = 3;\nconst OUTLIER_WINDOW = 64;\n\ntype GCEvent = { start: number; end: number };\n\nconst collectSample = async <TContext, TInput>(\n batchSize: number,\n run: (ctx: TContext, data: TInput) => MaybePromise<bigint>,\n pre: StepFn<TContext, TInput> | undefined,\n post: StepFn<TContext, TInput> | undefined,\n context: TContext,\n data: TInput,\n) => {\n let sampleDuration = 0n;\n for (let b = 0; b < batchSize; b++) {\n await pre?.(context, data);\n sampleDuration += await run(context, data);\n await post?.(context, data);\n }\n return sampleDuration / BigInt(batchSize);\n};\n\nconst tuneParameters = async <TContext, TInput>({\n initialBatch,\n run,\n pre,\n post,\n context,\n data,\n minCycles,\n relThreshold,\n maxCycles,\n}: {\n initialBatch: number;\n run: (ctx: TContext, data: TInput) => MaybePromise<bigint>;\n pre?: StepFn<TContext, TInput>;\n post?: StepFn<TContext, TInput>;\n context: TContext;\n data: TInput;\n minCycles: number;\n relThreshold: number;\n maxCycles: number;\n}) => {\n let batchSize = initialBatch;\n let bestCv = Number.POSITIVE_INFINITY;\n let bestBatch = batchSize;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const samples: number[] = [];\n const sampleCount = Math.min(8, maxCycles);\n for (let s = 0; s < sampleCount; s++) {\n const duration = await collectSample(batchSize, run, pre, post, context, data);\n samples.push(Number(duration));\n }\n const mean = samples.reduce((acc, v) => acc + v, 0) / samples.length;\n const variance = samples.reduce((acc, v) => acc + (v - mean) * (v - mean), 0) / Math.max(1, samples.length - 1);\n const stddev = Math.sqrt(variance);\n const cv = mean === 0 ? Number.POSITIVE_INFINITY : stddev / mean;\n\n if (cv < bestCv) {\n bestCv = cv;\n bestBatch = batchSize;\n }\n\n if (cv <= relThreshold || batchSize >= MAX_BATCH) {\n break;\n }\n batchSize = Math.min(MAX_BATCH, batchSize * 2);\n }\n\n const tunedRel = bestCv < relThreshold ? Math.max(bestCv * 1.5, relThreshold * 0.5) : relThreshold;\n const tunedMin = Math.min(maxCycles, Math.max(minCycles, Math.ceil(minCycles * Math.max(1, bestCv / (relThreshold || 1e-6)))));\n\n return { batchSize: bestBatch, relThreshold: tunedRel, minCycles: tunedMin };\n};\n\nconst createGCTracker = () => {\n if (process.env.OVERTAKE_GC_OBSERVER !== '1') {\n return null;\n }\n if (typeof PerformanceObserver === 'undefined') {\n return null;\n }\n\n const events: GCEvent[] = [];\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n events.push({ start: entry.startTime, end: entry.startTime + entry.duration });\n }\n });\n\n try {\n observer.observe({ entryTypes: ['gc'] });\n } catch {\n return null;\n }\n\n const overlaps = (start: number, end: number) => {\n let noisy = false;\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i];\n if (event.end < start - 5_000) {\n events.splice(i, 1);\n continue;\n }\n if (event.start <= end && event.end >= start) {\n noisy = true;\n }\n }\n return noisy;\n };\n\n const dispose = () => observer.disconnect();\n\n return { overlaps, dispose };\n};\n\nconst pushWindow = (arr: number[], value: number, cap: number) => {\n if (arr.length === cap) {\n arr.shift();\n }\n arr.push(value);\n};\n\nconst medianAndIqr = (arr: number[]) => {\n if (arr.length === 0) return { median: 0, iqr: 0 };\n const sorted = [...arr].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n const median = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];\n const q1Idx = Math.floor(sorted.length * 0.25);\n const q3Idx = Math.floor(sorted.length * 0.75);\n const q1 = sorted[q1Idx];\n const q3 = sorted[q3Idx];\n return { median, iqr: q3 - q1 };\n};\n\nconst windowCv = (arr: number[]) => {\n if (arr.length < 2) return Number.POSITIVE_INFINITY;\n const mean = arr.reduce((a, v) => a + v, 0) / arr.length;\n const variance = arr.reduce((a, v) => a + (v - mean) * (v - mean), 0) / (arr.length - 1);\n const stddev = Math.sqrt(variance);\n return mean === 0 ? Number.POSITIVE_INFINITY : stddev / mean;\n};\n\nexport const benchmark = async <TContext, TInput>({\n setup,\n teardown,\n pre,\n run: runRaw,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = false,\n\n durationsSAB,\n controlSAB,\n}: Required<Options<TContext, TInput>>) => {\n const durations = new BigUint64Array(durationsSAB);\n const control = new Int32Array(controlSAB);\n\n control[Control.INDEX] = 0;\n control[Control.PROGRESS] = 0;\n control[Control.COMPLETE] = 255;\n\n const context = (await setup?.()) as TContext;\n const maxCycles = durations.length;\n const gcWatcher = gcObserver ? new GCWatcher() : null;\n const gcTracker = gcObserver ? createGCTracker() : null;\n\n try {\n // classify sync/async and capture initial duration\n await pre?.(context, data!);\n const probeStart = hr();\n const probeResult = runRaw(context, data!);\n const isAsync = isThenable(probeResult);\n if (isAsync) {\n await probeResult;\n }\n const durationProbe = hr() - probeStart;\n await post?.(context, data!);\n\n const run = isAsync ? runAsync(runRaw) : runSync(runRaw);\n\n // choose batch size to amortize timer overhead\n const durationPerRun = durationProbe === 0n ? 1n : durationProbe;\n const suggestedBatch = Number(TARGET_SAMPLE_NS / durationPerRun);\n const initialBatchSize = Math.min(MAX_BATCH, Math.max(1, suggestedBatch));\n\n // auto-tune based on warmup samples\n const tuned = await tuneParameters({\n initialBatch: initialBatchSize,\n run,\n pre,\n post,\n context,\n data: data as TInput,\n minCycles,\n relThreshold,\n maxCycles,\n });\n let batchSize = tuned.batchSize;\n minCycles = tuned.minCycles;\n relThreshold = tuned.relThreshold;\n\n // warmup: run until requested cycles, adapt if unstable\n const warmupStart = Date.now();\n let warmupRemaining = warmupCycles;\n const warmupWindow: number[] = [];\n const warmupCap = Math.max(warmupCycles, Math.min(maxCycles, warmupCycles * 4 || 1000));\n\n while (Date.now() - warmupStart < 1_000 && warmupRemaining > 0) {\n const start = hr();\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n pushWindow(warmupWindow, Number(hr() - start), warmupCap);\n warmupRemaining--;\n }\n let warmupDone = 0;\n while (warmupDone < warmupRemaining) {\n const start = hr();\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n pushWindow(warmupWindow, Number(hr() - start), warmupCap);\n warmupDone++;\n if (global.gc && warmupDone % GC_STRIDE === 0) {\n global.gc();\n }\n }\n while (warmupWindow.length >= 8 && warmupWindow.length < warmupCap) {\n const cv = windowCv(warmupWindow);\n if (cv <= relThreshold * 2) {\n break;\n }\n const start = hr();\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n pushWindow(warmupWindow, Number(hr() - start), warmupCap);\n }\n\n let i = 0;\n let mean = 0n;\n let m2 = 0n;\n const outlierWindow: number[] = [];\n\n while (true) {\n if (i >= maxCycles) break;\n\n const gcMarker = gcWatcher?.start();\n const sampleStart = performance.now();\n let sampleDuration = 0n;\n for (let b = 0; b < batchSize; b++) {\n await pre?.(context, data!);\n sampleDuration += await run(context, data);\n await post?.(context, data!);\n if (global.gc && (i + b) % GC_STRIDE === 0) {\n global.gc();\n }\n }\n\n // normalize by batch size\n sampleDuration /= BigInt(batchSize);\n\n const sampleEnd = performance.now();\n const gcNoise = (gcMarker ? gcWatcher!.seen(gcMarker) : false) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);\n if (gcNoise) {\n continue;\n }\n\n const durationNumber = Number(sampleDuration);\n pushWindow(outlierWindow, durationNumber, OUTLIER_WINDOW);\n const { median, iqr } = medianAndIqr(outlierWindow);\n const maxAllowed = median + OUTLIER_IQR_MULTIPLIER * iqr || Number.POSITIVE_INFINITY;\n if (outlierWindow.length >= 8 && durationNumber > maxAllowed) {\n continue;\n }\n\n const meanNumber = Number(mean);\n if (i >= 8 && meanNumber > 0 && durationNumber > OUTLIER_MULTIPLIER * meanNumber) {\n continue;\n }\n\n durations[i++] = sampleDuration;\n const delta = sampleDuration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (sampleDuration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n if (i % PROGRESS_STRIDE === 0) {\n control[Control.PROGRESS] = progress;\n }\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n gcTracker?.dispose?.();\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["benchmark","COMPLETE_VALUE","hr","process","hrtime","bigint","bind","runSync","run","args","start","runAsync","isThenable","value","then","TARGET_SAMPLE_NS","MAX_BATCH","PROGRESS_STRIDE","GC_STRIDE","OUTLIER_MULTIPLIER","OUTLIER_IQR_MULTIPLIER","OUTLIER_WINDOW","collectSample","batchSize","pre","post","context","data","sampleDuration","b","BigInt","tuneParameters","initialBatch","minCycles","relThreshold","maxCycles","bestCv","Number","POSITIVE_INFINITY","bestBatch","attempt","samples","sampleCount","Math","min","s","duration","push","mean","reduce","acc","v","length","variance","max","stddev","sqrt","cv","tunedRel","tunedMin","ceil","createGCTracker","env","OVERTAKE_GC_OBSERVER","PerformanceObserver","events","observer","list","entry","getEntries","startTime","end","observe","entryTypes","overlaps","noisy","i","event","splice","dispose","disconnect","pushWindow","arr","cap","shift","medianAndIqr","median","iqr","sorted","sort","a","mid","floor","q1Idx","q3Idx","q1","q3","windowCv","setup","teardown","runRaw","warmupCycles","absThreshold","gcObserver","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","Control","INDEX","PROGRESS","COMPLETE","gcWatcher","GCWatcher","gcTracker","probeStart","probeResult","isAsync","durationProbe","durationPerRun","suggestedBatch","initialBatchSize","tuned","warmupStart","Date","now","warmupRemaining","warmupWindow","warmupCap","warmupDone","global","gc","m2","outlierWindow","gcMarker","sampleStart","performance","sampleEnd","gcNoise","seen","durationNumber","maxAllowed","meanNumber","delta","progress","meanNum","cov","e","console","error","stack"],"mappings":"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"}
package/build/runner.js CHANGED
@@ -17,6 +17,9 @@ const runAsync = (run)=>{
17
17
  return hr() - start;
18
18
  };
19
19
  };
20
+ const isThenable = (value)=>{
21
+ return value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function';
22
+ };
20
23
  const TARGET_SAMPLE_NS = 1_000_000n;
21
24
  const MAX_BATCH = 1_048_576;
22
25
  const PROGRESS_STRIDE = 16;
@@ -150,13 +153,13 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
150
153
  control[Control.COMPLETE] = 255;
151
154
  const context = await setup?.();
152
155
  const maxCycles = durations.length;
153
- const gcWatcher = new GCWatcher();
156
+ const gcWatcher = gcObserver ? new GCWatcher() : null;
154
157
  const gcTracker = gcObserver ? createGCTracker() : null;
155
158
  try {
156
159
  await pre?.(context, data);
157
160
  const probeStart = hr();
158
161
  const probeResult = runRaw(context, data);
159
- const isAsync = probeResult instanceof Promise;
162
+ const isAsync = isThenable(probeResult);
160
163
  if (isAsync) {
161
164
  await probeResult;
162
165
  }
@@ -221,7 +224,7 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
221
224
  const outlierWindow = [];
222
225
  while(true){
223
226
  if (i >= maxCycles) break;
224
- const gcMarker = gcWatcher.start();
227
+ const gcMarker = gcWatcher?.start();
225
228
  const sampleStart = performance.now();
226
229
  let sampleDuration = 0n;
227
230
  for(let b = 0; b < batchSize; b++){
@@ -234,7 +237,7 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
234
237
  }
235
238
  sampleDuration /= BigInt(batchSize);
236
239
  const sampleEnd = performance.now();
237
- const gcNoise = gcWatcher.seen(gcMarker) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);
240
+ const gcNoise = (gcMarker ? gcWatcher.seen(gcMarker) : false) || (gcTracker?.overlaps(sampleStart, sampleEnd) ?? false);
238
241
  if (gcNoise) {
239
242
  continue;
240
243
  }
@@ -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 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 = new GCWatcher();\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 = probeResult instanceof Promise;\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 = gcWatcher.seen(gcMarker) || (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","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","value","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","Promise","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 } 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 +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<void>;\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 baseUrl?: string;\n}\n\nexport interface RunOptions<TContext, TInput> {\n baseUrl?: string;\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 baseUrl: 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;\n"],"names":["CONTROL_SLOTS","Control","DEFAULT_CYCLES","REPORT_TYPES","Z95","Array","from","length","_","idx","concat","Object","values"],"mappings":";;;;;;;;;;;QAyEaA;eAAAA;;QANDC;eAAAA;;QAOCC;eAAAA;;QAlDAC;eAAAA;;QAmDAC;eAAAA;;;AAnDN,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;AA2C/J,IAAA,AAAKT,iCAAAA;;;;WAAAA;;AAML,MAAMD,gBAAgBW,OAAOC,MAAM,CAACX,SAASM,MAAM,GAAG;AACtD,MAAML,iBAAiB;AACvB,MAAME,MAAM"}
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<void>;\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;\n"],"names":["CONTROL_SLOTS","Control","DEFAULT_CYCLES","REPORT_TYPES","Z95","Array","from","length","_","idx","concat","Object","values"],"mappings":";;;;;;;;;;;QAuEaA;eAAAA;;QANDC;eAAAA;;QAOCC;eAAAA;;QAhDAC;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,AAAKT,iCAAAA;;;;WAAAA;;AAML,MAAMD,gBAAgBW,OAAOC,MAAM,CAACX,SAASM,MAAM,GAAG;AACtD,MAAML,iBAAiB;AACvB,MAAME,MAAM"}
package/build/types.d.ts CHANGED
@@ -26,10 +26,8 @@ export interface BenchmarkOptions {
26
26
  absThreshold?: number;
27
27
  relThreshold?: number;
28
28
  gcObserver?: boolean;
29
- baseUrl?: string;
30
29
  }
31
30
  export interface RunOptions<TContext, TInput> {
32
- baseUrl?: string;
33
31
  setup?: SetupFn<TContext>;
34
32
  teardown?: TeardownFn<TContext>;
35
33
  pre?: StepFn<TContext, TInput>;
@@ -38,7 +36,7 @@ export interface RunOptions<TContext, TInput> {
38
36
  data?: TInput;
39
37
  }
40
38
  export interface WorkerOptions extends Required<BenchmarkOptions> {
41
- baseUrl: string;
39
+ benchmarkUrl?: string;
42
40
  setupCode?: string;
43
41
  teardownCode?: string;
44
42
  preCode?: string;
@@ -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<void>;\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 baseUrl?: string;\n}\n\nexport interface RunOptions<TContext, TInput> {\n baseUrl?: string;\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 baseUrl: 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;\n"],"names":["REPORT_TYPES","Array","from","length","_","idx","concat","Control","CONTROL_SLOTS","Object","values","DEFAULT_CYCLES","Z95"],"mappings":"AAwBA,OAAO,MAAMA,eAA+BC,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,EAAE;AA2CxK,OAAO,IAAA,AAAKC,iCAAAA;;;;WAAAA;MAIX;AAED,OAAO,MAAMC,gBAAgBC,OAAOC,MAAM,CAACH,SAASJ,MAAM,GAAG,EAAE;AAC/D,OAAO,MAAMQ,iBAAiB,MAAM;AACpC,OAAO,MAAMC,MAAM,KAAK"}
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<void>;\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;\n"],"names":["REPORT_TYPES","Array","from","length","_","idx","concat","Control","CONTROL_SLOTS","Object","values","DEFAULT_CYCLES","Z95"],"mappings":"AAwBA,OAAO,MAAMA,eAA+BC,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,EAAE;AAyCxK,OAAO,IAAA,AAAKC,iCAAAA;;;;WAAAA;MAIX;AAED,OAAO,MAAMC,gBAAgBC,OAAOC,MAAM,CAACH,SAASJ,MAAM,GAAG,EAAE;AAC/D,OAAO,MAAMQ,iBAAiB,MAAM;AACpC,OAAO,MAAMC,MAAM,KAAK"}
package/build/worker.cjs CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  const _nodeworker_threads = require("node:worker_threads");
6
6
  const _nodevm = require("node:vm");
7
7
  const _nodemodule = require("node:module");
8
+ const _nodepath = require("node:path");
8
9
  const _nodeurl = require("node:url");
9
10
  const _runnercjs = require("./runner.cjs");
10
11
  function _getRequireWildcardCache(nodeInterop) {
@@ -48,8 +49,23 @@ function _interop_require_wildcard(obj, nodeInterop) {
48
49
  }
49
50
  return newObj;
50
51
  }
51
- const { baseUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = _nodeworker_threads.workerData;
52
+ const { benchmarkUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = _nodeworker_threads.workerData;
52
53
  const serialize = (code)=>code ? code : '() => {}';
54
+ const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : (0, _nodeurl.pathToFileURL)(process.cwd()).href;
55
+ const benchmarkDirUrl = new URL('.', resolvedBenchmarkUrl).href;
56
+ const requireFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(new URL('benchmark.js', benchmarkDirUrl)));
57
+ const resolveSpecifier = (specifier)=>{
58
+ if (specifier.startsWith('file:')) {
59
+ return specifier;
60
+ }
61
+ if (specifier.startsWith('./') || specifier.startsWith('../')) {
62
+ return new URL(specifier, benchmarkDirUrl).href;
63
+ }
64
+ if ((0, _nodepath.isAbsolute)(specifier)) {
65
+ return (0, _nodeurl.pathToFileURL)(specifier).href;
66
+ }
67
+ return requireFrom.resolve(specifier);
68
+ };
53
69
  const source = `
54
70
  export const setup = ${serialize(setupCode)};
55
71
  export const teardown = ${serialize(teardownCode)};
@@ -62,42 +78,67 @@ const context = (0, _nodevm.createContext)({
62
78
  Buffer
63
79
  });
64
80
  const imports = new Map();
81
+ const createSyntheticModule = (moduleExports, exportNames, identifier)=>{
82
+ const mod = new _nodevm.SyntheticModule(exportNames, ()=>{
83
+ for (const name of exportNames){
84
+ if (name === 'default') {
85
+ mod.setExport(name, moduleExports);
86
+ continue;
87
+ }
88
+ mod.setExport(name, moduleExports[name]);
89
+ }
90
+ }, {
91
+ identifier,
92
+ context
93
+ });
94
+ return mod;
95
+ };
96
+ const isCjsModule = (target)=>target.endsWith('.cjs') || target.endsWith('.cts');
97
+ const toRequireTarget = (target)=>target.startsWith('file:') ? (0, _nodeurl.fileURLToPath)(target) : target;
98
+ const loadModule = async (target)=>{
99
+ const cached = imports.get(target);
100
+ if (cached) return cached;
101
+ if (isCjsModule(target)) {
102
+ const required = requireFrom(toRequireTarget(target));
103
+ const exportNames = required && (typeof required === 'object' || typeof required === 'function') ? Object.keys(required) : [];
104
+ if (!exportNames.includes('default')) {
105
+ exportNames.push('default');
106
+ }
107
+ const mod = createSyntheticModule(required, exportNames, target);
108
+ imports.set(target, mod);
109
+ return mod;
110
+ }
111
+ const importedModule = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
112
+ const exportNames = Object.keys(importedModule);
113
+ const mod = createSyntheticModule(importedModule, exportNames, target);
114
+ imports.set(target, mod);
115
+ return mod;
116
+ };
117
+ const loadDynamicModule = async (target)=>{
118
+ const mod = await loadModule(target);
119
+ if (mod.status !== 'evaluated') {
120
+ await mod.evaluate();
121
+ }
122
+ return mod;
123
+ };
65
124
  const mod = new _nodevm.SourceTextModule(source, {
66
- identifier: baseUrl,
125
+ identifier: resolvedBenchmarkUrl,
67
126
  context,
68
127
  initializeImportMeta (meta) {
69
- meta.url = baseUrl;
128
+ meta.url = resolvedBenchmarkUrl;
70
129
  },
71
- importModuleDynamically (specifier, referencingModule) {
72
- const base = referencingModule.identifier ?? baseUrl;
73
- const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(base));
74
- return Promise.resolve(resolveFrom.resolve(specifier)).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
130
+ importModuleDynamically (specifier) {
131
+ const resolved = resolveSpecifier(specifier);
132
+ return loadDynamicModule(resolved);
75
133
  }
76
134
  });
77
- await mod.link(async (specifier, referencingModule)=>{
78
- const base = referencingModule.identifier ?? baseUrl;
79
- const resolveFrom = (0, _nodemodule.createRequire)((0, _nodeurl.fileURLToPath)(base));
80
- const target = resolveFrom.resolve(specifier);
81
- const cached = imports.get(target);
82
- if (cached) return cached;
83
- const importedModule = await Promise.resolve(target).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
84
- const exportNames = Object.keys(importedModule);
85
- const imported = new _nodevm.SyntheticModule(exportNames, ()=>{
86
- exportNames.forEach((key)=>imported.setExport(key, importedModule[key]));
87
- }, {
88
- identifier: target,
89
- context: referencingModule.context
90
- });
91
- imports.set(target, imported);
92
- return imported;
93
- });
135
+ await mod.link(async (specifier)=>loadModule(resolveSpecifier(specifier)));
94
136
  await mod.evaluate();
95
137
  const { setup, teardown, pre, run, post } = mod.namespace;
96
138
  if (!run) {
97
139
  throw new Error('Benchmark run function is required');
98
140
  }
99
141
  process.exitCode = await (0, _runnercjs.benchmark)({
100
- baseUrl,
101
142
  setup,
102
143
  teardown,
103
144
  pre,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { SourceTextModule, SyntheticModule, createContext } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\nimport { benchmark } from './runner.js';\nimport { WorkerOptions } from './types.js';\n\nconst {\n baseUrl,\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = true,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst serialize = (code?: string) => (code ? code : '() => {}');\n\nconst source = `\nexport const setup = ${serialize(setupCode)};\nexport const teardown = ${serialize(teardownCode)};\nexport const pre = ${serialize(preCode)};\nexport const run = ${serialize(runCode)};\nexport const post = ${serialize(postCode)};\n `;\n\nconst context = createContext({ console, Buffer });\nconst imports = new Map<string, SyntheticModule>();\nconst mod = new SourceTextModule(source, {\n identifier: baseUrl,\n context,\n initializeImportMeta(meta) {\n meta.url = baseUrl;\n },\n importModuleDynamically(specifier, referencingModule) {\n const base = referencingModule.identifier ?? baseUrl;\n const resolveFrom = createRequire(fileURLToPath(base));\n return import(resolveFrom.resolve(specifier));\n },\n});\n\nawait mod.link(async (specifier, referencingModule) => {\n const base = referencingModule.identifier ?? baseUrl;\n const resolveFrom = createRequire(fileURLToPath(base));\n const target = resolveFrom.resolve(specifier);\n const cached = imports.get(target);\n if (cached) return cached;\n\n const importedModule = await import(target);\n const exportNames = Object.keys(importedModule);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, importedModule[key]));\n },\n { identifier: target, context: referencingModule.context },\n );\n imports.set(target, imported);\n return imported;\n});\n\nawait mod.evaluate();\nconst { setup, teardown, pre, run, post } = mod.namespace as any;\n\nif (!run) {\n throw new Error('Benchmark run function is required');\n}\n\nprocess.exitCode = await benchmark({\n baseUrl,\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n\n durationsSAB,\n controlSAB,\n});\n"],"names":["baseUrl","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","gcObserver","durationsSAB","controlSAB","workerData","serialize","code","source","context","createContext","console","Buffer","imports","Map","mod","SourceTextModule","identifier","initializeImportMeta","meta","url","importModuleDynamically","specifier","referencingModule","base","resolveFrom","createRequire","fileURLToPath","resolve","link","target","cached","get","importedModule","exportNames","Object","keys","imported","SyntheticModule","forEach","key","setExport","set","evaluate","setup","teardown","pre","run","post","namespace","Error","process","exitCode","benchmark"],"mappings":";;;;oCAA2B;wBACsC;4BACnC;yBACA;2BACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG1B,MAAM,EACJA,OAAO,EACPC,SAAS,EACTC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,QAAQ,EACRC,IAAI,EAEJC,YAAY,EACZC,SAAS,EACTC,YAAY,EACZC,YAAY,EACZC,aAAa,IAAI,EAEjBC,YAAY,EACZC,UAAU,EACX,GAAkBC,8BAAU;AAE7B,MAAMC,YAAY,CAACC,OAAmBA,OAAOA,OAAO;AAEpD,MAAMC,SAAS,CAAC;qBACK,EAAEF,UAAUd,WAAW;wBACpB,EAAEc,UAAUb,cAAc;mBAC/B,EAAEa,UAAUZ,SAAS;mBACrB,EAAEY,UAAUX,SAAS;oBACpB,EAAEW,UAAUV,UAAU;EACxC,CAAC;AAEH,MAAMa,UAAUC,IAAAA,qBAAa,EAAC;IAAEC;IAASC;AAAO;AAChD,MAAMC,UAAU,IAAIC;AACpB,MAAMC,MAAM,IAAIC,wBAAgB,CAACR,QAAQ;IACvCS,YAAY1B;IACZkB;IACAS,sBAAqBC,IAAI;QACvBA,KAAKC,GAAG,GAAG7B;IACb;IACA8B,yBAAwBC,SAAS,EAAEC,iBAAiB;QAClD,MAAMC,OAAOD,kBAAkBN,UAAU,IAAI1B;QAC7C,MAAMkC,cAAcC,IAAAA,yBAAa,EAACC,IAAAA,sBAAa,EAACH;QAChD,OAAO,gBAAOC,YAAYG,OAAO,CAACN,8DAA3B;IACT;AACF;AAEA,MAAMP,IAAIc,IAAI,CAAC,OAAOP,WAAWC;IAC/B,MAAMC,OAAOD,kBAAkBN,UAAU,IAAI1B;IAC7C,MAAMkC,cAAcC,IAAAA,yBAAa,EAACC,IAAAA,sBAAa,EAACH;IAChD,MAAMM,SAASL,YAAYG,OAAO,CAACN;IACnC,MAAMS,SAASlB,QAAQmB,GAAG,CAACF;IAC3B,IAAIC,QAAQ,OAAOA;IAEnB,MAAME,iBAAiB,MAAM,gBAAOH,0DAAP;IAC7B,MAAMI,cAAcC,OAAOC,IAAI,CAACH;IAChC,MAAMI,WAAW,IAAIC,uBAAe,CAClCJ,aACA;QACEA,YAAYK,OAAO,CAAC,CAACC,MAAQH,SAASI,SAAS,CAACD,KAAKP,cAAc,CAACO,IAAI;IAC1E,GACA;QAAEvB,YAAYa;QAAQrB,SAASc,kBAAkBd,OAAO;IAAC;IAE3DI,QAAQ6B,GAAG,CAACZ,QAAQO;IACpB,OAAOA;AACT;AAEA,MAAMtB,IAAI4B,QAAQ;AAClB,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,GAAG,EAAEC,IAAI,EAAE,GAAGjC,IAAIkC,SAAS;AAEzD,IAAI,CAACF,KAAK;IACR,MAAM,IAAIG,MAAM;AAClB;AAEAC,QAAQC,QAAQ,GAAG,MAAMC,IAAAA,oBAAS,EAAC;IACjC9D;IACAqD;IACAC;IACAC;IACAC;IACAC;IACAnD;IAEAC;IACAC;IACAC;IACAC;IACAC;IAEAC;IACAC;AACF"}
1
+ {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { SourceTextModule, SyntheticModule, createContext } from 'node:vm';\nimport { createRequire } from 'node:module';\nimport { isAbsolute } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { benchmark } from './runner.js';\nimport { WorkerOptions } from './types.js';\n\nconst {\n benchmarkUrl,\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver = true,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst serialize = (code?: string) => (code ? code : '() => {}');\n\nconst resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;\nconst benchmarkDirUrl = new URL('.', resolvedBenchmarkUrl).href;\nconst requireFrom = createRequire(fileURLToPath(new URL('benchmark.js', benchmarkDirUrl)));\n\nconst resolveSpecifier = (specifier: string) => {\n if (specifier.startsWith('file:')) {\n return specifier;\n }\n if (specifier.startsWith('./') || specifier.startsWith('../')) {\n return new URL(specifier, benchmarkDirUrl).href;\n }\n if (isAbsolute(specifier)) {\n return pathToFileURL(specifier).href;\n }\n return requireFrom.resolve(specifier);\n};\n\nconst source = `\nexport const setup = ${serialize(setupCode)};\nexport const teardown = ${serialize(teardownCode)};\nexport const pre = ${serialize(preCode)};\nexport const run = ${serialize(runCode)};\nexport const post = ${serialize(postCode)};\n `;\n\nconst context = createContext({ console, Buffer });\nconst imports = new Map<string, SyntheticModule>();\n\nconst createSyntheticModule = (moduleExports: unknown, exportNames: string[], identifier: string) => {\n const mod = new SyntheticModule(\n exportNames,\n () => {\n for (const name of exportNames) {\n if (name === 'default') {\n mod.setExport(name, moduleExports);\n continue;\n }\n mod.setExport(name, (moduleExports as Record<string, unknown>)[name]);\n }\n },\n { identifier, context },\n );\n return mod;\n};\n\nconst isCjsModule = (target: string) => target.endsWith('.cjs') || target.endsWith('.cts');\n\nconst toRequireTarget = (target: string) => (target.startsWith('file:') ? fileURLToPath(target) : target);\n\nconst loadModule = async (target: string) => {\n const cached = imports.get(target);\n if (cached) return cached;\n\n if (isCjsModule(target)) {\n const required = requireFrom(toRequireTarget(target));\n const exportNames = required && (typeof required === 'object' || typeof required === 'function') ? Object.keys(required) : [];\n if (!exportNames.includes('default')) {\n exportNames.push('default');\n }\n const mod = createSyntheticModule(required, exportNames, target);\n imports.set(target, mod);\n return mod;\n }\n\n const importedModule = await import(target);\n const exportNames = Object.keys(importedModule);\n const mod = createSyntheticModule(importedModule, exportNames, target);\n imports.set(target, mod);\n return mod;\n};\n\nconst loadDynamicModule = async (target: string) => {\n const mod = await loadModule(target);\n if (mod.status !== 'evaluated') {\n await mod.evaluate();\n }\n return mod;\n};\nconst mod = new SourceTextModule(source, {\n identifier: resolvedBenchmarkUrl,\n context,\n initializeImportMeta(meta) {\n meta.url = resolvedBenchmarkUrl;\n },\n importModuleDynamically(specifier) {\n const resolved = resolveSpecifier(specifier);\n return loadDynamicModule(resolved);\n },\n});\n\nawait mod.link(async (specifier) => loadModule(resolveSpecifier(specifier)));\n\nawait mod.evaluate();\nconst { setup, teardown, pre, run, post } = mod.namespace as any;\n\nif (!run) {\n throw new Error('Benchmark run function is required');\n}\n\nprocess.exitCode = await benchmark({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n gcObserver,\n\n durationsSAB,\n controlSAB,\n});\n"],"names":["benchmarkUrl","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","gcObserver","durationsSAB","controlSAB","workerData","serialize","code","resolvedBenchmarkUrl","pathToFileURL","process","cwd","href","benchmarkDirUrl","URL","requireFrom","createRequire","fileURLToPath","resolveSpecifier","specifier","startsWith","isAbsolute","resolve","source","context","createContext","console","Buffer","imports","Map","createSyntheticModule","moduleExports","exportNames","identifier","mod","SyntheticModule","name","setExport","isCjsModule","target","endsWith","toRequireTarget","loadModule","cached","get","required","Object","keys","includes","push","set","importedModule","loadDynamicModule","status","evaluate","SourceTextModule","initializeImportMeta","meta","url","importModuleDynamically","resolved","link","setup","teardown","pre","run","post","namespace","Error","exitCode","benchmark"],"mappings":"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"}
package/build/worker.js CHANGED
@@ -1,10 +1,26 @@
1
1
  import { workerData } from 'node:worker_threads';
2
2
  import { SourceTextModule, SyntheticModule, createContext } from 'node:vm';
3
3
  import { createRequire } from 'node:module';
4
- import { fileURLToPath } from 'node:url';
4
+ import { isAbsolute } from 'node:path';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
6
  import { benchmark } from "./runner.js";
6
- const { baseUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = workerData;
7
+ const { benchmarkUrl, setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver = true, durationsSAB, controlSAB } = workerData;
7
8
  const serialize = (code)=>code ? code : '() => {}';
9
+ const resolvedBenchmarkUrl = typeof benchmarkUrl === 'string' ? benchmarkUrl : pathToFileURL(process.cwd()).href;
10
+ const benchmarkDirUrl = new URL('.', resolvedBenchmarkUrl).href;
11
+ const requireFrom = createRequire(fileURLToPath(new URL('benchmark.js', benchmarkDirUrl)));
12
+ const resolveSpecifier = (specifier)=>{
13
+ if (specifier.startsWith('file:')) {
14
+ return specifier;
15
+ }
16
+ if (specifier.startsWith('./') || specifier.startsWith('../')) {
17
+ return new URL(specifier, benchmarkDirUrl).href;
18
+ }
19
+ if (isAbsolute(specifier)) {
20
+ return pathToFileURL(specifier).href;
21
+ }
22
+ return requireFrom.resolve(specifier);
23
+ };
8
24
  const source = `
9
25
  export const setup = ${serialize(setupCode)};
10
26
  export const teardown = ${serialize(teardownCode)};
@@ -17,42 +33,67 @@ const context = createContext({
17
33
  Buffer
18
34
  });
19
35
  const imports = new Map();
36
+ const createSyntheticModule = (moduleExports, exportNames, identifier)=>{
37
+ const mod = new SyntheticModule(exportNames, ()=>{
38
+ for (const name of exportNames){
39
+ if (name === 'default') {
40
+ mod.setExport(name, moduleExports);
41
+ continue;
42
+ }
43
+ mod.setExport(name, moduleExports[name]);
44
+ }
45
+ }, {
46
+ identifier,
47
+ context
48
+ });
49
+ return mod;
50
+ };
51
+ const isCjsModule = (target)=>target.endsWith('.cjs') || target.endsWith('.cts');
52
+ const toRequireTarget = (target)=>target.startsWith('file:') ? fileURLToPath(target) : target;
53
+ const loadModule = async (target)=>{
54
+ const cached = imports.get(target);
55
+ if (cached) return cached;
56
+ if (isCjsModule(target)) {
57
+ const required = requireFrom(toRequireTarget(target));
58
+ const exportNames = required && (typeof required === 'object' || typeof required === 'function') ? Object.keys(required) : [];
59
+ if (!exportNames.includes('default')) {
60
+ exportNames.push('default');
61
+ }
62
+ const mod = createSyntheticModule(required, exportNames, target);
63
+ imports.set(target, mod);
64
+ return mod;
65
+ }
66
+ const importedModule = await import(target);
67
+ const exportNames = Object.keys(importedModule);
68
+ const mod = createSyntheticModule(importedModule, exportNames, target);
69
+ imports.set(target, mod);
70
+ return mod;
71
+ };
72
+ const loadDynamicModule = async (target)=>{
73
+ const mod = await loadModule(target);
74
+ if (mod.status !== 'evaluated') {
75
+ await mod.evaluate();
76
+ }
77
+ return mod;
78
+ };
20
79
  const mod = new SourceTextModule(source, {
21
- identifier: baseUrl,
80
+ identifier: resolvedBenchmarkUrl,
22
81
  context,
23
82
  initializeImportMeta (meta) {
24
- meta.url = baseUrl;
83
+ meta.url = resolvedBenchmarkUrl;
25
84
  },
26
- importModuleDynamically (specifier, referencingModule) {
27
- const base = referencingModule.identifier ?? baseUrl;
28
- const resolveFrom = createRequire(fileURLToPath(base));
29
- return import(resolveFrom.resolve(specifier));
85
+ importModuleDynamically (specifier) {
86
+ const resolved = resolveSpecifier(specifier);
87
+ return loadDynamicModule(resolved);
30
88
  }
31
89
  });
32
- await mod.link(async (specifier, referencingModule)=>{
33
- const base = referencingModule.identifier ?? baseUrl;
34
- const resolveFrom = createRequire(fileURLToPath(base));
35
- const target = resolveFrom.resolve(specifier);
36
- const cached = imports.get(target);
37
- if (cached) return cached;
38
- const importedModule = await import(target);
39
- const exportNames = Object.keys(importedModule);
40
- const imported = new SyntheticModule(exportNames, ()=>{
41
- exportNames.forEach((key)=>imported.setExport(key, importedModule[key]));
42
- }, {
43
- identifier: target,
44
- context: referencingModule.context
45
- });
46
- imports.set(target, imported);
47
- return imported;
48
- });
90
+ await mod.link(async (specifier)=>loadModule(resolveSpecifier(specifier)));
49
91
  await mod.evaluate();
50
92
  const { setup, teardown, pre, run, post } = mod.namespace;
51
93
  if (!run) {
52
94
  throw new Error('Benchmark run function is required');
53
95
  }
54
96
  process.exitCode = await benchmark({
55
- baseUrl,
56
97
  setup,
57
98
  teardown,
58
99
  pre,