overtake 2.0.0 → 2.0.1

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.
@@ -0,0 +1,15 @@
1
+ import { register } from 'node:module';
2
+ async function resolve(s, c, n) {
3
+ try {
4
+ return await n(s, c);
5
+ }
6
+ catch (e) {
7
+ if (s.endsWith('.js'))
8
+ try {
9
+ return await n(s.slice(0, -3) + '.ts', c);
10
+ }
11
+ catch { }
12
+ throw e;
13
+ }
14
+ }
15
+ register('data:text/javascript,' + encodeURIComponent(`export ${resolve.toString()}`));
@@ -1,4 +1,4 @@
1
- import { type ReportType } from './types.ts';
1
+ import { type ReportType } from './types.js';
2
2
  export declare class Report {
3
3
  readonly type: ReportType;
4
4
  readonly value: bigint;
@@ -0,0 +1,255 @@
1
+ import { div, max, divs, isqrt } from './utils.js';
2
+ import { DURATION_SCALE } from './types.js';
3
+ const units = [
4
+ { unit: 'ns', factor: 1 },
5
+ { unit: 'µs', factor: 1e3 },
6
+ { unit: 'ms', factor: 1e6 },
7
+ { unit: 's', factor: 1e9 },
8
+ { unit: 'm', factor: 60 * 1e9 },
9
+ { unit: 'h', factor: 3600 * 1e9 },
10
+ ];
11
+ function smartFixed(n) {
12
+ return n.toLocaleString(undefined, {
13
+ minimumFractionDigits: 0,
14
+ maximumFractionDigits: 2,
15
+ useGrouping: true,
16
+ });
17
+ }
18
+ export class Report {
19
+ type;
20
+ value;
21
+ uncertainty;
22
+ scale;
23
+ constructor(type, value, uncertainty = 0, scale = 1n) {
24
+ this.type = type;
25
+ this.value = value;
26
+ this.uncertainty = uncertainty;
27
+ this.scale = scale;
28
+ }
29
+ valueOf() {
30
+ return Number(div(this.value, this.scale));
31
+ }
32
+ toString() {
33
+ const uncertainty = this.uncertainty ? ` ± ${smartFixed(this.uncertainty)}%` : '';
34
+ const value = this.valueOf();
35
+ if (this.type === 'ops') {
36
+ return `${smartFixed(value)} ops/s${uncertainty}`;
37
+ }
38
+ if (this.type === 'rme') {
39
+ return `${smartFixed(value)}%`;
40
+ }
41
+ if (this.type === 'variance') {
42
+ let display = value;
43
+ let unit = 'ns²';
44
+ const varianceUnits = [
45
+ { unit: 'ns²', factor: 1 },
46
+ { unit: 'µs²', factor: 1e6 },
47
+ { unit: 'ms²', factor: 1e12 },
48
+ ];
49
+ for (const { unit: u, factor } of varianceUnits) {
50
+ const candidate = value / factor;
51
+ if (candidate < 1000) {
52
+ display = candidate;
53
+ unit = u;
54
+ break;
55
+ }
56
+ }
57
+ return `${smartFixed(display)} ${unit}`;
58
+ }
59
+ let display = value;
60
+ let unit = 'ns';
61
+ for (const { unit: u, factor } of units) {
62
+ const candidate = value / factor;
63
+ if (candidate < 1000) {
64
+ display = candidate;
65
+ unit = u;
66
+ break;
67
+ }
68
+ }
69
+ return `${smartFixed(display)} ${unit}${uncertainty}`;
70
+ }
71
+ }
72
+ const SQRT_SCALE = 1000000n;
73
+ const SQRT_SCALE_SQ = SQRT_SCALE * SQRT_SCALE;
74
+ const Z95_NUM = 196n;
75
+ const Z95_DENOM = 100n;
76
+ export const computeStats = (durations) => {
77
+ let sum = 0n;
78
+ for (const d of durations)
79
+ sum += d;
80
+ const n = BigInt(durations.length);
81
+ const mean = sum / n;
82
+ let ssd = 0n;
83
+ for (const d of durations) {
84
+ const diff = d - mean;
85
+ ssd += diff * diff;
86
+ }
87
+ return { sum, mean, ssd, n };
88
+ };
89
+ export const createReport = (durations, type, stats) => {
90
+ const n = durations.length;
91
+ if (n === 0) {
92
+ return new Report(type, 0n);
93
+ }
94
+ const st = stats ?? computeStats(durations);
95
+ switch (type) {
96
+ case 'min': {
97
+ return new Report(type, durations[0], 0, DURATION_SCALE);
98
+ }
99
+ case 'max': {
100
+ return new Report(type, durations[n - 1], 0, DURATION_SCALE);
101
+ }
102
+ case 'median': {
103
+ const mid = Math.floor(n / 2);
104
+ const med = n % 2 === 0 ? (durations[mid - 1] + durations[mid]) / 2n : durations[mid];
105
+ return new Report(type, med, 0, DURATION_SCALE);
106
+ }
107
+ case 'mode': {
108
+ const freq = new Map();
109
+ let maxCount = 0n;
110
+ let modeVal = durations[0];
111
+ for (const d of durations) {
112
+ const count = (freq.get(d) || 0n) + 1n;
113
+ freq.set(d, count);
114
+ if (count > maxCount) {
115
+ maxCount = count;
116
+ modeVal = d;
117
+ }
118
+ }
119
+ let lower = modeVal;
120
+ let upper = modeVal;
121
+ const firstIdx = durations.indexOf(modeVal);
122
+ const lastIdx = durations.lastIndexOf(modeVal);
123
+ if (firstIdx > 0)
124
+ lower = durations[firstIdx - 1];
125
+ if (lastIdx < n - 1)
126
+ upper = durations[lastIdx + 1];
127
+ const gap = max(modeVal - lower, upper - modeVal);
128
+ const uncertainty = modeVal > 0 ? Number(((gap / 2n) * 100n) / modeVal) : 0;
129
+ return new Report(type, modeVal, uncertainty, DURATION_SCALE);
130
+ }
131
+ case 'ops': {
132
+ const { mean: avgScaled, ssd, n: nBig } = st;
133
+ const nsPerSecScaled = 1000000000n * DURATION_SCALE;
134
+ const raw = Number(nsPerSecScaled) / Number(avgScaled);
135
+ const extra = raw < 1 ? Math.ceil(-Math.log10(raw)) : 0;
136
+ const exp = raw > 100 ? 0 : 2 + extra;
137
+ const scale = 10n ** BigInt(exp);
138
+ const value = avgScaled > 0n ? (nsPerSecScaled * scale) / avgScaled : 0n;
139
+ let uncertainty = 0;
140
+ if (n >= 2 && avgScaled > 0n) {
141
+ const RME_PRECISION = 1000000n;
142
+ const semOverMeanSqScaled = (ssd * RME_PRECISION * RME_PRECISION) / (BigInt(n - 1) * nBig * avgScaled * avgScaled);
143
+ const semOverMeanScaled = isqrt(semOverMeanSqScaled);
144
+ uncertainty = Number(Z95_NUM * semOverMeanScaled) / Number(RME_PRECISION);
145
+ }
146
+ return new Report(type, value, uncertainty, scale);
147
+ }
148
+ case 'mean': {
149
+ const { sum } = st;
150
+ const value = divs(sum, BigInt(n), 1n);
151
+ return new Report(type, value, 0, DURATION_SCALE);
152
+ }
153
+ case 'variance': {
154
+ if (n < 2)
155
+ return new Report(type, 0n, 0, DURATION_SCALE * DURATION_SCALE);
156
+ const { ssd } = st;
157
+ const variance = ssd / BigInt(n - 1);
158
+ return new Report(type, variance, 0, DURATION_SCALE * DURATION_SCALE);
159
+ }
160
+ case 'sd': {
161
+ if (n < 2)
162
+ return new Report(type, 0n, 0, DURATION_SCALE);
163
+ const { ssd } = st;
164
+ const scaledVariance = (ssd * SQRT_SCALE_SQ) / BigInt(n - 1);
165
+ const sdScaled = isqrt(scaledVariance);
166
+ return new Report(type, sdScaled, 0, DURATION_SCALE * SQRT_SCALE);
167
+ }
168
+ case 'sem': {
169
+ if (n < 2)
170
+ return new Report(type, 0n, 0, DURATION_SCALE);
171
+ const { ssd, n: nBig } = st;
172
+ const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
173
+ const semScaled = isqrt(semSqScaled);
174
+ return new Report(type, semScaled, 0, DURATION_SCALE * SQRT_SCALE);
175
+ }
176
+ case 'moe': {
177
+ if (n < 2)
178
+ return new Report(type, 0n, 0, DURATION_SCALE);
179
+ const { ssd, n: nBig } = st;
180
+ const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
181
+ const semScaled = isqrt(semSqScaled);
182
+ const moeScaled = (Z95_NUM * semScaled) / Z95_DENOM;
183
+ return new Report(type, moeScaled, 0, DURATION_SCALE * SQRT_SCALE);
184
+ }
185
+ case 'rme': {
186
+ if (n < 2)
187
+ return new Report(type, 0n);
188
+ const { mean, ssd, n: nBig } = st;
189
+ if (mean === 0n)
190
+ return new Report(type, 0n);
191
+ const RME_PRECISION = 1000000n;
192
+ const semOverMeanSqScaled = (ssd * RME_PRECISION * RME_PRECISION) / (BigInt(n - 1) * nBig * mean * mean);
193
+ const semOverMeanScaled = isqrt(semOverMeanSqScaled);
194
+ const rmeScaled = (Z95_NUM * semOverMeanScaled * 100n) / RME_PRECISION;
195
+ return new Report(type, rmeScaled, 0, 100n);
196
+ }
197
+ case 'mad': {
198
+ const medianIdx = Math.floor(n / 2);
199
+ const median = n % 2 === 1 ? durations[medianIdx] : (durations[medianIdx - 1] + durations[medianIdx]) / 2n;
200
+ const deviations = new BigUint64Array(n);
201
+ for (let i = 0; i < n; i++) {
202
+ const diff = durations[i] > median ? durations[i] - median : median - durations[i];
203
+ deviations[i] = diff;
204
+ }
205
+ deviations.sort();
206
+ const madIdx = Math.floor(n / 2);
207
+ const mad = n % 2 === 1 ? deviations[madIdx] : (deviations[madIdx - 1] + deviations[madIdx]) / 2n;
208
+ return new Report(type, mad, 0, DURATION_SCALE);
209
+ }
210
+ case 'iqr': {
211
+ const q1Idx = Math.floor(n * 0.25);
212
+ const q3Idx = Math.floor(n * 0.75);
213
+ const q1 = durations[q1Idx];
214
+ const q3 = durations[q3Idx];
215
+ const iqr = q3 - q1;
216
+ return new Report(type, iqr, 0, DURATION_SCALE);
217
+ }
218
+ case 'ci_lower': {
219
+ if (n < 2)
220
+ return new Report(type, 0n, 0, DURATION_SCALE);
221
+ const { mean, ssd, n: nBig } = st;
222
+ const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
223
+ const semScaled = isqrt(semSqScaled);
224
+ const moeScaled = (Z95_NUM * semScaled) / Z95_DENOM;
225
+ const ciLowerScaled = mean * SQRT_SCALE - moeScaled;
226
+ return new Report(type, ciLowerScaled > 0n ? ciLowerScaled : 0n, 0, DURATION_SCALE * SQRT_SCALE);
227
+ }
228
+ case 'ci_upper': {
229
+ if (n < 2)
230
+ return new Report(type, 0n, 0, DURATION_SCALE);
231
+ const { mean, ssd, n: nBig } = st;
232
+ const semSqScaled = (ssd * SQRT_SCALE_SQ) / (BigInt(n - 1) * nBig);
233
+ const semScaled = isqrt(semSqScaled);
234
+ const moeScaled = (Z95_NUM * semScaled) / Z95_DENOM;
235
+ const ciUpperScaled = mean * SQRT_SCALE + moeScaled;
236
+ return new Report(type, ciUpperScaled, 0, DURATION_SCALE * SQRT_SCALE);
237
+ }
238
+ default: {
239
+ const p = Number(type.slice(1));
240
+ if (p === 0) {
241
+ return new Report(type, durations[0], 0, DURATION_SCALE);
242
+ }
243
+ if (p === 100) {
244
+ return new Report(type, durations[n - 1], 0, DURATION_SCALE);
245
+ }
246
+ const idx = Math.ceil((p / 100) * n) - 1;
247
+ const value = durations[Math.min(Math.max(idx, 0), n - 1)];
248
+ const prev = idx > 0 ? durations[idx - 1] : value;
249
+ const next = idx < n - 1 ? durations[idx + 1] : value;
250
+ const gap = max(value - prev, next - value);
251
+ const uncertainty = value > 0 ? Number(div(divs(gap, 2n, 10000n), value)) / 100 : 0;
252
+ return new Report(type, value, uncertainty, DURATION_SCALE);
253
+ }
254
+ }
255
+ };
package/build/runner.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { type Options } from './types.ts';
1
+ import { type Options } from './types.js';
2
2
  export declare const benchmark: <TContext, TInput>({ setup, teardown, pre, run: runRaw, post, data, warmupCycles, minCycles, absThreshold, relThreshold, gcObserver, durationsSAB, controlSAB, }: Required<Options<TContext, TInput>>) => Promise<number>;