modestbench 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +45 -2
- package/dist/adapters/ava-adapter.cjs +421 -0
- package/dist/adapters/ava-adapter.cjs.map +1 -0
- package/dist/adapters/ava-adapter.d.cts +39 -0
- package/dist/adapters/ava-adapter.d.cts.map +1 -0
- package/dist/adapters/ava-adapter.d.ts +39 -0
- package/dist/adapters/ava-adapter.d.ts.map +1 -0
- package/dist/adapters/ava-adapter.js +384 -0
- package/dist/adapters/ava-adapter.js.map +1 -0
- package/dist/adapters/ava-hooks.cjs +66 -0
- package/dist/adapters/ava-hooks.cjs.map +1 -0
- package/dist/adapters/ava-hooks.d.cts +24 -0
- package/dist/adapters/ava-hooks.d.cts.map +1 -0
- package/dist/adapters/ava-hooks.d.ts +24 -0
- package/dist/adapters/ava-hooks.d.ts.map +1 -0
- package/dist/adapters/ava-hooks.js +61 -0
- package/dist/adapters/ava-hooks.js.map +1 -0
- package/dist/adapters/ava-register.cjs +16 -0
- package/dist/adapters/ava-register.cjs.map +1 -0
- package/dist/adapters/ava-register.d.cts +11 -0
- package/dist/adapters/ava-register.d.cts.map +1 -0
- package/dist/adapters/ava-register.d.ts +11 -0
- package/dist/adapters/ava-register.d.ts.map +1 -0
- package/dist/adapters/ava-register.js +14 -0
- package/dist/adapters/ava-register.js.map +1 -0
- package/dist/adapters/mocha-adapter.cjs +254 -0
- package/dist/adapters/mocha-adapter.cjs.map +1 -0
- package/dist/adapters/mocha-adapter.d.cts +26 -0
- package/dist/adapters/mocha-adapter.d.cts.map +1 -0
- package/dist/adapters/mocha-adapter.d.ts +26 -0
- package/dist/adapters/mocha-adapter.d.ts.map +1 -0
- package/dist/adapters/mocha-adapter.js +217 -0
- package/dist/adapters/mocha-adapter.js.map +1 -0
- package/dist/adapters/node-test-adapter.cjs +335 -0
- package/dist/adapters/node-test-adapter.cjs.map +1 -0
- package/dist/adapters/node-test-adapter.d.cts +41 -0
- package/dist/adapters/node-test-adapter.d.cts.map +1 -0
- package/dist/adapters/node-test-adapter.d.ts +41 -0
- package/dist/adapters/node-test-adapter.d.ts.map +1 -0
- package/dist/adapters/node-test-adapter.js +298 -0
- package/dist/adapters/node-test-adapter.js.map +1 -0
- package/dist/adapters/node-test-hooks.cjs +72 -0
- package/dist/adapters/node-test-hooks.cjs.map +1 -0
- package/dist/adapters/node-test-hooks.d.cts +24 -0
- package/dist/adapters/node-test-hooks.d.cts.map +1 -0
- package/dist/adapters/node-test-hooks.d.ts +24 -0
- package/dist/adapters/node-test-hooks.d.ts.map +1 -0
- package/dist/adapters/node-test-hooks.js +67 -0
- package/dist/adapters/node-test-hooks.js.map +1 -0
- package/dist/adapters/node-test-register.cjs +7 -0
- package/dist/adapters/node-test-register.cjs.map +1 -0
- package/dist/adapters/node-test-register.d.cts +2 -0
- package/dist/adapters/node-test-register.d.cts.map +1 -0
- package/dist/adapters/node-test-register.d.ts +2 -0
- package/dist/adapters/node-test-register.d.ts.map +1 -0
- package/dist/adapters/node-test-register.js +5 -0
- package/dist/adapters/node-test-register.js.map +1 -0
- package/dist/adapters/types.cjs +152 -0
- package/dist/adapters/types.cjs.map +1 -0
- package/dist/adapters/types.d.cts +112 -0
- package/dist/adapters/types.d.cts.map +1 -0
- package/dist/adapters/types.d.ts +112 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +148 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/commands/init.cjs +21 -17
- package/dist/cli/commands/init.cjs.map +1 -1
- package/dist/cli/commands/init.d.cts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +21 -17
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.cjs +6 -2
- package/dist/cli/commands/run.cjs.map +1 -1
- package/dist/cli/commands/run.js +6 -2
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/test.cjs +392 -0
- package/dist/cli/commands/test.cjs.map +1 -0
- package/dist/cli/commands/test.d.cts +38 -0
- package/dist/cli/commands/test.d.cts.map +1 -0
- package/dist/cli/commands/test.d.ts +38 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/test.js +388 -0
- package/dist/cli/commands/test.js.map +1 -0
- package/dist/cli/index.cjs +72 -1
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +73 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/constants.cjs +13 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +12 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +12 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/engine.cjs +4 -0
- package/dist/core/engine.cjs.map +1 -1
- package/dist/core/engine.d.cts.map +1 -1
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +4 -0
- package/dist/core/engine.js.map +1 -1
- package/dist/core/engines/tinybench-engine.cjs +163 -131
- package/dist/core/engines/tinybench-engine.cjs.map +1 -1
- package/dist/core/engines/tinybench-engine.d.cts +6 -0
- package/dist/core/engines/tinybench-engine.d.cts.map +1 -1
- package/dist/core/engines/tinybench-engine.d.ts +6 -0
- package/dist/core/engines/tinybench-engine.d.ts.map +1 -1
- package/dist/core/engines/tinybench-engine.js +163 -131
- package/dist/core/engines/tinybench-engine.js.map +1 -1
- package/dist/errors/base.cjs +2 -1
- package/dist/errors/base.cjs.map +1 -1
- package/dist/errors/base.d.cts.map +1 -1
- package/dist/errors/base.d.ts.map +1 -1
- package/dist/errors/base.js +2 -1
- package/dist/errors/base.js.map +1 -1
- package/dist/reporters/human.cjs +83 -27
- package/dist/reporters/human.cjs.map +1 -1
- package/dist/reporters/human.d.cts +1 -0
- package/dist/reporters/human.d.cts.map +1 -1
- package/dist/reporters/human.d.ts +1 -0
- package/dist/reporters/human.d.ts.map +1 -1
- package/dist/reporters/human.js +83 -27
- package/dist/reporters/human.js.map +1 -1
- package/dist/reporters/simple.cjs +68 -21
- package/dist/reporters/simple.cjs.map +1 -1
- package/dist/reporters/simple.d.cts +1 -0
- package/dist/reporters/simple.d.cts.map +1 -1
- package/dist/reporters/simple.d.ts +1 -0
- package/dist/reporters/simple.d.ts.map +1 -1
- package/dist/reporters/simple.js +68 -21
- package/dist/reporters/simple.js.map +1 -1
- package/dist/services/config-manager.cjs +1 -1
- package/dist/services/config-manager.cjs.map +1 -1
- package/dist/services/config-manager.js +2 -2
- package/dist/services/config-manager.js.map +1 -1
- package/package.json +60 -31
- package/src/adapters/ava-adapter.ts +553 -0
- package/src/adapters/ava-hooks.ts +65 -0
- package/src/adapters/ava-register.ts +15 -0
- package/src/adapters/mocha-adapter.ts +284 -0
- package/src/adapters/node-test-adapter.ts +391 -0
- package/src/adapters/node-test-hooks.ts +71 -0
- package/src/adapters/node-test-register.ts +5 -0
- package/src/adapters/types.ts +281 -0
- package/src/cli/commands/init.ts +25 -17
- package/src/cli/commands/run.ts +9 -2
- package/src/cli/commands/test.ts +546 -0
- package/src/cli/index.ts +81 -1
- package/src/constants.ts +15 -0
- package/src/core/engine.ts +5 -0
- package/src/core/engines/tinybench-engine.ts +213 -141
- package/src/errors/base.ts +3 -2
- package/src/reporters/human.ts +107 -36
- package/src/reporters/simple.ts +81 -22
- package/src/services/config-manager.ts +2 -2
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* benchmark execution and measurement.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Bench } from 'tinybench';
|
|
8
|
+
import { Bench, type TaskResult as TinybenchTaskResult } from 'tinybench';
|
|
9
9
|
|
|
10
10
|
import type {
|
|
11
11
|
BenchmarkTask,
|
|
@@ -83,6 +83,7 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
83
83
|
|
|
84
84
|
const bench = new Bench({
|
|
85
85
|
iterations: effectiveIterations,
|
|
86
|
+
retainSamples: true, // Required in tinybench v6+ to access samples for custom stats
|
|
86
87
|
time: effectiveTime,
|
|
87
88
|
warmupIterations: config.warmup,
|
|
88
89
|
warmupTime: config.warmup > 0 ? Math.min(config.warmup || 0, 500) : 0,
|
|
@@ -129,6 +130,7 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
129
130
|
|
|
130
131
|
const minimalBench = new Bench({
|
|
131
132
|
iterations: config.iterations,
|
|
133
|
+
retainSamples: true,
|
|
132
134
|
time: retryTime,
|
|
133
135
|
warmupIterations: config.warmup,
|
|
134
136
|
warmupTime: 0,
|
|
@@ -136,8 +138,6 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
136
138
|
minimalBench.add(
|
|
137
139
|
taskName,
|
|
138
140
|
taskData.fn,
|
|
139
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
140
|
-
// @ts-ignore - Pending https://github.com/tinylibs/tinybench/pull/364
|
|
141
141
|
signal ? { signal } : undefined,
|
|
142
142
|
);
|
|
143
143
|
try {
|
|
@@ -149,34 +149,29 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
151
|
const minimalResults = minimalBench.results[0];
|
|
152
|
-
|
|
152
|
+
// Handle discriminated union: check state for error/completion
|
|
153
|
+
if (!minimalResults || minimalResults.state === 'errored') {
|
|
154
|
+
const errorMsg =
|
|
155
|
+
minimalResults?.state === 'errored'
|
|
156
|
+
? minimalResults.error.message
|
|
157
|
+
: 'unknown error';
|
|
153
158
|
throw new OperationTooFastError(
|
|
154
|
-
`Benchmark too fast to measure reliably: ${
|
|
159
|
+
`Benchmark too fast to measure reliably: ${errorMsg}`,
|
|
155
160
|
);
|
|
156
161
|
}
|
|
157
|
-
//
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
min: minimalStats.min,
|
|
171
|
-
name: taskName,
|
|
172
|
-
opsPerSecond: minimalResults.throughput.mean || 0,
|
|
173
|
-
p95: minimalStats.p95,
|
|
174
|
-
p99: minimalStats.p99,
|
|
175
|
-
stdDev: minimalStats.stdDev,
|
|
176
|
-
...(taskData.tags ? { tags: taskData.tags } : {}),
|
|
177
|
-
variance: minimalStats.variance,
|
|
178
|
-
};
|
|
179
|
-
return taskResult;
|
|
162
|
+
// Extract stats from completed result
|
|
163
|
+
const taskResultFromMinimal =
|
|
164
|
+
this.extractTaskResultFromTinybenchResult(
|
|
165
|
+
taskName,
|
|
166
|
+
taskData,
|
|
167
|
+
minimalResults,
|
|
168
|
+
);
|
|
169
|
+
if (taskResultFromMinimal) {
|
|
170
|
+
return taskResultFromMinimal;
|
|
171
|
+
}
|
|
172
|
+
throw new OperationTooFastError(
|
|
173
|
+
`Benchmark too fast to measure reliably: no statistics available`,
|
|
174
|
+
);
|
|
180
175
|
}
|
|
181
176
|
throw error;
|
|
182
177
|
} finally {
|
|
@@ -190,132 +185,146 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
190
185
|
throw new BenchmarkExecutionError('No benchmark results returned');
|
|
191
186
|
}
|
|
192
187
|
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return taskResult;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Check if tinybench detected an error during execution
|
|
218
|
-
if (results.error) {
|
|
219
|
-
const errorMessage =
|
|
220
|
-
results.error instanceof Error
|
|
221
|
-
? results.error.message
|
|
222
|
-
: String(results.error);
|
|
188
|
+
// Handle discriminated union based on state
|
|
189
|
+
switch (results.state) {
|
|
190
|
+
case 'aborted':
|
|
191
|
+
// Task was aborted via signal - return minimal valid result marked as aborted
|
|
192
|
+
return {
|
|
193
|
+
aborted: true,
|
|
194
|
+
cv: 0,
|
|
195
|
+
iterations: 0,
|
|
196
|
+
marginOfError: 0,
|
|
197
|
+
max: 0,
|
|
198
|
+
mean: 0,
|
|
199
|
+
metadata: taskData.metadata ?? {},
|
|
200
|
+
min: 0,
|
|
201
|
+
name: taskName,
|
|
202
|
+
opsPerSecond: 0,
|
|
203
|
+
p95: 0,
|
|
204
|
+
p99: 0,
|
|
205
|
+
stdDev: 0,
|
|
206
|
+
...(taskData.tags ? { tags: taskData.tags } : {}),
|
|
207
|
+
variance: 0,
|
|
208
|
+
};
|
|
223
209
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
case 'iterations':
|
|
235
|
-
retryTime = 1;
|
|
236
|
-
break;
|
|
237
|
-
case 'time':
|
|
238
|
-
retryTime = 10;
|
|
239
|
-
break;
|
|
240
|
-
default:
|
|
241
|
-
retryTime = 1;
|
|
210
|
+
case 'aborted-with-statistics': {
|
|
211
|
+
// Aborted but has partial stats - use them
|
|
212
|
+
const taskResultFromAborted =
|
|
213
|
+
this.extractTaskResultFromTinybenchResult(
|
|
214
|
+
taskName,
|
|
215
|
+
taskData,
|
|
216
|
+
results,
|
|
217
|
+
);
|
|
218
|
+
if (taskResultFromAborted) {
|
|
219
|
+
return { ...taskResultFromAborted, aborted: true };
|
|
242
220
|
}
|
|
221
|
+
// Fall through to return minimal aborted result
|
|
222
|
+
return {
|
|
223
|
+
aborted: true,
|
|
224
|
+
cv: 0,
|
|
225
|
+
iterations: 0,
|
|
226
|
+
marginOfError: 0,
|
|
227
|
+
max: 0,
|
|
228
|
+
mean: 0,
|
|
229
|
+
metadata: taskData.metadata ?? {},
|
|
230
|
+
min: 0,
|
|
231
|
+
name: taskName,
|
|
232
|
+
opsPerSecond: 0,
|
|
233
|
+
p95: 0,
|
|
234
|
+
p99: 0,
|
|
235
|
+
stdDev: 0,
|
|
236
|
+
...(taskData.tags ? { tags: taskData.tags } : {}),
|
|
237
|
+
variance: 0,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
243
240
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
241
|
+
case 'errored': {
|
|
242
|
+
const errorMessage = results.error.message;
|
|
243
|
+
|
|
244
|
+
// Handle array length errors for extremely fast operations
|
|
245
|
+
if (errorMessage.includes('Invalid array length')) {
|
|
246
|
+
// Retry with minimal time for extremely fast operations
|
|
247
|
+
let retryTime: number;
|
|
248
|
+
switch (config.limitBy) {
|
|
249
|
+
case 'all':
|
|
250
|
+
case 'any':
|
|
251
|
+
retryTime = 10;
|
|
252
|
+
break;
|
|
253
|
+
case 'iterations':
|
|
254
|
+
retryTime = 1;
|
|
255
|
+
break;
|
|
256
|
+
case 'time':
|
|
257
|
+
retryTime = 10;
|
|
258
|
+
break;
|
|
259
|
+
default:
|
|
260
|
+
retryTime = 1;
|
|
261
|
+
}
|
|
259
262
|
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
const minimalBench = new Bench({
|
|
264
|
+
iterations: config.iterations,
|
|
265
|
+
retainSamples: true,
|
|
266
|
+
time: retryTime,
|
|
267
|
+
warmupIterations: config.warmup,
|
|
268
|
+
warmupTime: 0,
|
|
269
|
+
});
|
|
270
|
+
minimalBench.add(
|
|
271
|
+
taskName,
|
|
272
|
+
taskData.fn,
|
|
273
|
+
signal ? { signal } : undefined,
|
|
274
|
+
);
|
|
275
|
+
await minimalBench.run();
|
|
276
|
+
const minimalResults = minimalBench.results[0];
|
|
277
|
+
|
|
278
|
+
if (!minimalResults || minimalResults.state === 'errored') {
|
|
279
|
+
throw new OperationTooFastError(
|
|
280
|
+
`Benchmark operation is too fast to measure reliably`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const retryTaskResult = this.extractTaskResultFromTinybenchResult(
|
|
285
|
+
taskName,
|
|
286
|
+
taskData,
|
|
287
|
+
minimalResults,
|
|
288
|
+
);
|
|
289
|
+
if (retryTaskResult) {
|
|
290
|
+
return retryTaskResult;
|
|
291
|
+
}
|
|
262
292
|
throw new OperationTooFastError(
|
|
263
293
|
`Benchmark operation is too fast to measure reliably`,
|
|
264
294
|
);
|
|
265
295
|
}
|
|
266
296
|
|
|
267
|
-
|
|
268
|
-
const minimalRawSamples2 = minimalResults.latency.samples || [];
|
|
269
|
-
const minimalSamplesInNs2 = minimalRawSamples2.map((s) => s * 1e6);
|
|
270
|
-
const minimalCleanedSamples2 = removeOutliersIQR(minimalSamplesInNs2);
|
|
271
|
-
const minimalStats2 = calculateStatistics(minimalCleanedSamples2);
|
|
272
|
-
|
|
273
|
-
const taskResult: TaskResult = {
|
|
274
|
-
cv: minimalStats2.cv,
|
|
275
|
-
iterations: minimalCleanedSamples2.length,
|
|
276
|
-
marginOfError: minimalStats2.marginOfError,
|
|
277
|
-
max: minimalStats2.max,
|
|
278
|
-
mean: minimalStats2.mean,
|
|
279
|
-
metadata: taskData.metadata ?? {},
|
|
280
|
-
min: minimalStats2.min,
|
|
281
|
-
name: taskName,
|
|
282
|
-
opsPerSecond: minimalResults.throughput.mean || 0,
|
|
283
|
-
p95: minimalStats2.p95,
|
|
284
|
-
p99: minimalStats2.p99,
|
|
285
|
-
stdDev: minimalStats2.stdDev,
|
|
286
|
-
...(taskData.tags ? { tags: taskData.tags } : {}),
|
|
287
|
-
variance: minimalStats2.variance,
|
|
288
|
-
};
|
|
289
|
-
return taskResult;
|
|
297
|
+
throw results.error;
|
|
290
298
|
}
|
|
291
299
|
|
|
292
|
-
|
|
293
|
-
|
|
300
|
+
case 'completed': {
|
|
301
|
+
// Normal completion - extract full stats
|
|
302
|
+
const taskResultFromCompleted =
|
|
303
|
+
this.extractTaskResultFromTinybenchResult(
|
|
304
|
+
taskName,
|
|
305
|
+
taskData,
|
|
306
|
+
results,
|
|
307
|
+
);
|
|
308
|
+
if (taskResultFromCompleted) {
|
|
309
|
+
return taskResultFromCompleted;
|
|
310
|
+
}
|
|
311
|
+
throw new BenchmarkExecutionError(
|
|
312
|
+
'Completed benchmark but no statistics available',
|
|
313
|
+
);
|
|
314
|
+
}
|
|
294
315
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const taskResult: TaskResult = {
|
|
302
|
-
cv: stats.cv,
|
|
303
|
-
iterations: cleanedSamples.length,
|
|
304
|
-
marginOfError: stats.marginOfError,
|
|
305
|
-
max: stats.max,
|
|
306
|
-
mean: stats.mean,
|
|
307
|
-
metadata: taskData.metadata ?? {},
|
|
308
|
-
min: stats.min,
|
|
309
|
-
name: taskName,
|
|
310
|
-
opsPerSecond: results.throughput.mean || 0, // Keep tinybench's ops/sec
|
|
311
|
-
p95: stats.p95,
|
|
312
|
-
p99: stats.p99,
|
|
313
|
-
stdDev: stats.stdDev,
|
|
314
|
-
...(taskData.tags ? { tags: taskData.tags } : {}),
|
|
315
|
-
variance: stats.variance,
|
|
316
|
-
};
|
|
316
|
+
case 'not-started':
|
|
317
|
+
case 'started':
|
|
318
|
+
throw new BenchmarkExecutionError(
|
|
319
|
+
`Unexpected benchmark state: ${results.state}`,
|
|
320
|
+
);
|
|
317
321
|
|
|
318
|
-
|
|
322
|
+
default:
|
|
323
|
+
// Exhaustiveness check
|
|
324
|
+
throw new BenchmarkExecutionError(
|
|
325
|
+
`Unknown benchmark state: ${(results as TinybenchTaskResult).state}`,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
319
328
|
} catch (error) {
|
|
320
329
|
const executionError =
|
|
321
330
|
error instanceof Error ? error : new Error(String(error));
|
|
@@ -340,4 +349,67 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
340
349
|
return errorResult;
|
|
341
350
|
}
|
|
342
351
|
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Extract TaskResult from a tinybench result that has statistics
|
|
355
|
+
*
|
|
356
|
+
* Handles the discriminated union types from tinybench v6+
|
|
357
|
+
*/
|
|
358
|
+
private extractTaskResultFromTinybenchResult(
|
|
359
|
+
taskName: string,
|
|
360
|
+
taskData: BenchmarkTask,
|
|
361
|
+
result: TinybenchTaskResult,
|
|
362
|
+
): null | TaskResult {
|
|
363
|
+
// Only states with statistics: 'completed' and 'aborted-with-statistics'
|
|
364
|
+
if (
|
|
365
|
+
result.state !== 'completed' &&
|
|
366
|
+
result.state !== 'aborted-with-statistics'
|
|
367
|
+
) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Apply IQR outlier removal to raw samples
|
|
372
|
+
// Note: samples may be undefined if retainSamples wasn't enabled
|
|
373
|
+
const rawSamples = result.latency.samples ?? [];
|
|
374
|
+
if (rawSamples.length === 0) {
|
|
375
|
+
// Fall back to using tinybench's calculated stats directly
|
|
376
|
+
return {
|
|
377
|
+
cv: result.latency.rme, // Use relative margin of error as CV approximation
|
|
378
|
+
iterations: result.latency.samplesCount,
|
|
379
|
+
marginOfError: result.latency.moe,
|
|
380
|
+
max: result.latency.max,
|
|
381
|
+
mean: result.latency.mean,
|
|
382
|
+
metadata: taskData.metadata ?? {},
|
|
383
|
+
min: result.latency.min,
|
|
384
|
+
name: taskName,
|
|
385
|
+
opsPerSecond: result.throughput.mean || 0,
|
|
386
|
+
p95: result.latency.p99, // tinybench v6 doesn't have p95, use p99
|
|
387
|
+
p99: result.latency.p99,
|
|
388
|
+
stdDev: result.latency.sd,
|
|
389
|
+
...(taskData.tags ? { tags: taskData.tags } : {}),
|
|
390
|
+
variance: result.latency.variance,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const samplesInNs = rawSamples.map((s: number) => s * 1e6); // Convert ms to ns
|
|
395
|
+
const cleanedSamples = removeOutliersIQR(samplesInNs);
|
|
396
|
+
const stats = calculateStatistics(cleanedSamples);
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
cv: stats.cv,
|
|
400
|
+
iterations: cleanedSamples.length,
|
|
401
|
+
marginOfError: stats.marginOfError,
|
|
402
|
+
max: stats.max,
|
|
403
|
+
mean: stats.mean,
|
|
404
|
+
metadata: taskData.metadata ?? {},
|
|
405
|
+
min: stats.min,
|
|
406
|
+
name: taskName,
|
|
407
|
+
opsPerSecond: result.throughput.mean || 0,
|
|
408
|
+
p95: stats.p95,
|
|
409
|
+
p99: stats.p99,
|
|
410
|
+
stdDev: stats.stdDev,
|
|
411
|
+
...(taskData.tags ? { tags: taskData.tags } : {}),
|
|
412
|
+
variance: stats.variance,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
343
415
|
}
|
package/src/errors/base.ts
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* documentation URLs, and consistent error display.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { SITE_URL } from '../constants.js';
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Base URL for error documentation
|
|
10
12
|
*/
|
|
11
|
-
const ERROR_DOC_BASE_URL =
|
|
12
|
-
'https://boneskull.github.io/modestbench/reference/errors';
|
|
13
|
+
const ERROR_DOC_BASE_URL = `${SITE_URL}/reference/errors`;
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Abstract base class for ModestBench aggregate errors
|