modestbench 0.2.0 → 0.3.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.
- package/CHANGELOG.md +27 -0
- package/README.md +131 -34
- package/dist/cli/commands/analyze.cjs +60 -0
- package/dist/cli/commands/analyze.cjs.map +1 -0
- package/dist/cli/commands/analyze.d.cts +35 -0
- package/dist/cli/commands/analyze.d.cts.map +1 -0
- package/dist/cli/commands/analyze.d.ts +35 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +56 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/baseline.cjs +404 -0
- package/dist/cli/commands/baseline.cjs.map +1 -0
- package/dist/cli/commands/baseline.d.cts +72 -0
- package/dist/cli/commands/baseline.d.cts.map +1 -0
- package/dist/cli/commands/baseline.d.ts +72 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +396 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/history.d.cts +1 -1
- package/dist/cli/commands/history.d.cts.map +1 -1
- package/dist/cli/commands/history.d.ts +1 -1
- package/dist/cli/commands/history.d.ts.map +1 -1
- package/dist/cli/commands/init.cjs +99 -166
- package/dist/cli/commands/init.cjs.map +1 -1
- package/dist/cli/commands/init.d.cts +4 -4
- package/dist/cli/commands/init.d.cts.map +1 -1
- package/dist/cli/commands/init.d.ts +4 -4
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +99 -166
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.cjs +146 -127
- package/dist/cli/commands/run.cjs.map +1 -1
- package/dist/cli/commands/run.d.cts +16 -3
- package/dist/cli/commands/run.d.cts.map +1 -1
- package/dist/cli/commands/run.d.ts +16 -3
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +145 -93
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/index.cjs +583 -394
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +4 -16
- package/dist/cli/index.d.cts.map +1 -1
- package/dist/cli/index.d.ts +4 -16
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +575 -386
- package/dist/cli/index.js.map +1 -1
- package/dist/config/budget-schema.cjs +172 -0
- package/dist/config/budget-schema.cjs.map +1 -0
- package/dist/config/budget-schema.d.cts +59 -0
- package/dist/config/budget-schema.d.cts.map +1 -0
- package/dist/config/budget-schema.d.ts +59 -0
- package/dist/config/budget-schema.d.ts.map +1 -0
- package/dist/config/budget-schema.js +166 -0
- package/dist/config/budget-schema.js.map +1 -0
- package/dist/config/schema.cjs +182 -2
- package/dist/config/schema.cjs.map +1 -1
- package/dist/config/schema.d.cts +122 -3
- package/dist/config/schema.d.cts.map +1 -1
- package/dist/config/schema.d.ts +122 -3
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +180 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/constants.cjs +45 -2
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +41 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts +41 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +44 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/engine.cjs +114 -23
- package/dist/core/engine.cjs.map +1 -1
- package/dist/core/engine.d.cts +7 -7
- package/dist/core/engine.d.cts.map +1 -1
- package/dist/core/engine.d.ts +7 -7
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +115 -24
- package/dist/core/engine.js.map +1 -1
- package/dist/core/engines/accurate-engine.cjs +171 -36
- package/dist/core/engines/accurate-engine.cjs.map +1 -1
- package/dist/core/engines/accurate-engine.d.cts +5 -0
- package/dist/core/engines/accurate-engine.d.cts.map +1 -1
- package/dist/core/engines/accurate-engine.d.ts +5 -0
- package/dist/core/engines/accurate-engine.d.ts.map +1 -1
- package/dist/core/engines/accurate-engine.js +171 -36
- package/dist/core/engines/accurate-engine.js.map +1 -1
- package/dist/core/engines/tinybench-engine.cjs +3 -2
- package/dist/core/engines/tinybench-engine.cjs.map +1 -1
- package/dist/core/engines/tinybench-engine.d.cts.map +1 -1
- package/dist/core/engines/tinybench-engine.d.ts.map +1 -1
- package/dist/core/engines/tinybench-engine.js +3 -2
- package/dist/core/engines/tinybench-engine.js.map +1 -1
- package/dist/core/output-path-resolver.cjs +8 -1
- package/dist/core/output-path-resolver.cjs.map +1 -1
- package/dist/core/output-path-resolver.d.cts.map +1 -1
- package/dist/core/output-path-resolver.d.ts.map +1 -1
- package/dist/core/output-path-resolver.js +9 -2
- package/dist/core/output-path-resolver.js.map +1 -1
- package/dist/errors/base.cjs +12 -3
- package/dist/errors/base.cjs.map +1 -1
- package/dist/errors/base.d.cts +7 -0
- package/dist/errors/base.d.cts.map +1 -1
- package/dist/errors/base.d.ts +7 -0
- package/dist/errors/base.d.ts.map +1 -1
- package/dist/errors/base.js +10 -2
- package/dist/errors/base.js.map +1 -1
- package/dist/errors/budget.cjs +37 -0
- package/dist/errors/budget.cjs.map +1 -0
- package/dist/errors/budget.d.cts +31 -0
- package/dist/errors/budget.d.cts.map +1 -0
- package/dist/errors/budget.d.ts +31 -0
- package/dist/errors/budget.d.ts.map +1 -0
- package/dist/errors/budget.js +33 -0
- package/dist/errors/budget.js.map +1 -0
- package/dist/errors/index.cjs +4 -1
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +1 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/index.cjs +13 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/reporters/csv.cjs +37 -17
- package/dist/reporters/csv.cjs.map +1 -1
- package/dist/reporters/csv.d.cts +3 -6
- package/dist/reporters/csv.d.cts.map +1 -1
- package/dist/reporters/csv.d.ts +3 -6
- package/dist/reporters/csv.d.ts.map +1 -1
- package/dist/reporters/csv.js +37 -17
- package/dist/reporters/csv.js.map +1 -1
- package/dist/reporters/human.cjs +290 -67
- package/dist/reporters/human.cjs.map +1 -1
- package/dist/reporters/human.d.cts +25 -13
- package/dist/reporters/human.d.cts.map +1 -1
- package/dist/reporters/human.d.ts +25 -13
- package/dist/reporters/human.d.ts.map +1 -1
- package/dist/reporters/human.js +290 -67
- package/dist/reporters/human.js.map +1 -1
- package/dist/reporters/json.cjs +23 -48
- package/dist/reporters/json.cjs.map +1 -1
- package/dist/reporters/json.d.cts +2 -28
- package/dist/reporters/json.d.cts.map +1 -1
- package/dist/reporters/json.d.ts +2 -28
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +25 -50
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/profile-human.cjs +154 -0
- package/dist/reporters/profile-human.cjs.map +1 -0
- package/dist/reporters/profile-human.d.cts +44 -0
- package/dist/reporters/profile-human.d.cts.map +1 -0
- package/dist/reporters/profile-human.d.ts +44 -0
- package/dist/reporters/profile-human.d.ts.map +1 -0
- package/dist/reporters/profile-human.js +147 -0
- package/dist/reporters/profile-human.js.map +1 -0
- package/dist/reporters/simple.cjs +67 -45
- package/dist/reporters/simple.cjs.map +1 -1
- package/dist/reporters/simple.d.cts +14 -14
- package/dist/reporters/simple.d.cts.map +1 -1
- package/dist/reporters/simple.d.ts +14 -14
- package/dist/reporters/simple.d.ts.map +1 -1
- package/dist/reporters/simple.js +67 -45
- package/dist/reporters/simple.js.map +1 -1
- package/dist/schema/modestbench-config.schema.json +153 -0
- package/dist/services/baseline-storage.cjs +151 -0
- package/dist/services/baseline-storage.cjs.map +1 -0
- package/dist/services/baseline-storage.d.cts +55 -0
- package/dist/services/baseline-storage.d.cts.map +1 -0
- package/dist/services/baseline-storage.d.ts +55 -0
- package/dist/services/baseline-storage.d.ts.map +1 -0
- package/dist/services/baseline-storage.js +147 -0
- package/dist/services/baseline-storage.js.map +1 -0
- package/dist/services/budget-evaluator.cjs +146 -0
- package/dist/services/budget-evaluator.cjs.map +1 -0
- package/dist/services/budget-evaluator.d.cts +29 -0
- package/dist/services/budget-evaluator.d.cts.map +1 -0
- package/dist/services/budget-evaluator.d.ts +29 -0
- package/dist/services/budget-evaluator.d.ts.map +1 -0
- package/dist/services/budget-evaluator.js +142 -0
- package/dist/services/budget-evaluator.js.map +1 -0
- package/dist/services/config-manager.cjs +24 -10
- package/dist/services/config-manager.cjs.map +1 -1
- package/dist/services/config-manager.d.cts +6 -1
- package/dist/services/config-manager.d.cts.map +1 -1
- package/dist/services/config-manager.d.ts +6 -1
- package/dist/services/config-manager.d.ts.map +1 -1
- package/dist/services/config-manager.js +24 -10
- package/dist/services/config-manager.js.map +1 -1
- package/dist/services/file-loader.cjs +3 -6
- package/dist/services/file-loader.cjs.map +1 -1
- package/dist/services/file-loader.d.cts.map +1 -1
- package/dist/services/file-loader.d.ts.map +1 -1
- package/dist/services/file-loader.js +3 -6
- package/dist/services/file-loader.js.map +1 -1
- package/dist/services/profiler/profile-filter.cjs +116 -0
- package/dist/services/profiler/profile-filter.cjs.map +1 -0
- package/dist/services/profiler/profile-filter.d.cts +20 -0
- package/dist/services/profiler/profile-filter.d.cts.map +1 -0
- package/dist/services/profiler/profile-filter.d.ts +20 -0
- package/dist/services/profiler/profile-filter.d.ts.map +1 -0
- package/dist/services/profiler/profile-filter.js +112 -0
- package/dist/services/profiler/profile-filter.js.map +1 -0
- package/dist/services/profiler/profile-parser.cjs +139 -0
- package/dist/services/profiler/profile-parser.cjs.map +1 -0
- package/dist/services/profiler/profile-parser.d.cts +18 -0
- package/dist/services/profiler/profile-parser.d.cts.map +1 -0
- package/dist/services/profiler/profile-parser.d.ts +18 -0
- package/dist/services/profiler/profile-parser.d.ts.map +1 -0
- package/dist/services/profiler/profile-parser.js +132 -0
- package/dist/services/profiler/profile-parser.js.map +1 -0
- package/dist/services/profiler/profile-runner.cjs +90 -0
- package/dist/services/profiler/profile-runner.cjs.map +1 -0
- package/dist/services/profiler/profile-runner.d.cts +29 -0
- package/dist/services/profiler/profile-runner.d.cts.map +1 -0
- package/dist/services/profiler/profile-runner.d.ts +29 -0
- package/dist/services/profiler/profile-runner.d.ts.map +1 -0
- package/dist/services/profiler/profile-runner.js +86 -0
- package/dist/services/profiler/profile-runner.js.map +1 -0
- package/dist/services/progress-manager.cjs +10 -2
- package/dist/services/progress-manager.cjs.map +1 -1
- package/dist/services/progress-manager.d.cts +2 -0
- package/dist/services/progress-manager.d.cts.map +1 -1
- package/dist/services/progress-manager.d.ts +2 -0
- package/dist/services/progress-manager.d.ts.map +1 -1
- package/dist/services/progress-manager.js +10 -2
- package/dist/services/progress-manager.js.map +1 -1
- package/dist/services/reporter-registry.cjs +18 -24
- package/dist/services/reporter-registry.cjs.map +1 -1
- package/dist/services/reporter-registry.d.cts +18 -40
- package/dist/services/reporter-registry.d.cts.map +1 -1
- package/dist/services/reporter-registry.d.ts +18 -40
- package/dist/services/reporter-registry.d.ts.map +1 -1
- package/dist/services/reporter-registry.js +18 -24
- package/dist/services/reporter-registry.js.map +1 -1
- package/dist/types/budgets.cjs +8 -0
- package/dist/types/budgets.cjs.map +1 -0
- package/dist/types/budgets.d.cts +149 -0
- package/dist/types/budgets.d.cts.map +1 -0
- package/dist/types/budgets.d.ts +149 -0
- package/dist/types/budgets.d.ts.map +1 -0
- package/dist/types/budgets.js +7 -0
- package/dist/types/budgets.js.map +1 -0
- package/dist/types/cli.cjs +2 -11
- package/dist/types/cli.cjs.map +1 -1
- package/dist/types/cli.d.cts +3 -227
- package/dist/types/cli.d.cts.map +1 -1
- package/dist/types/cli.d.ts +3 -227
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js +2 -11
- package/dist/types/cli.js.map +1 -1
- package/dist/types/core.cjs +6 -1
- package/dist/types/core.cjs.map +1 -1
- package/dist/types/core.d.cts +15 -2
- package/dist/types/core.d.cts.map +1 -1
- package/dist/types/core.d.ts +15 -2
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js +2 -1
- package/dist/types/core.js.map +1 -1
- package/dist/types/index.cjs +5 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +2 -0
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/interfaces.d.cts +19 -8
- package/dist/types/interfaces.d.cts.map +1 -1
- package/dist/types/interfaces.d.ts +19 -8
- package/dist/types/interfaces.d.ts.map +1 -1
- package/dist/types/profiler.cjs +11 -0
- package/dist/types/profiler.cjs.map +1 -0
- package/dist/types/profiler.d.cts +102 -0
- package/dist/types/profiler.d.cts.map +1 -0
- package/dist/types/profiler.d.ts +102 -0
- package/dist/types/profiler.d.ts.map +1 -0
- package/dist/types/profiler.js +10 -0
- package/dist/types/profiler.js.map +1 -0
- package/dist/types/utility.cjs.map +1 -1
- package/dist/types/utility.d.cts +0 -8
- package/dist/types/utility.d.cts.map +1 -1
- package/dist/types/utility.d.ts +0 -8
- package/dist/types/utility.d.ts.map +1 -1
- package/dist/types/utility.js.map +1 -1
- package/dist/utils/identifiers.cjs +32 -0
- package/dist/utils/identifiers.cjs.map +1 -0
- package/dist/utils/identifiers.d.cts +32 -0
- package/dist/utils/identifiers.d.cts.map +1 -0
- package/dist/utils/identifiers.d.ts +32 -0
- package/dist/utils/identifiers.d.ts.map +1 -0
- package/dist/utils/identifiers.js +27 -0
- package/dist/utils/identifiers.js.map +1 -0
- package/dist/utils/package.cjs +40 -0
- package/dist/utils/package.cjs.map +1 -0
- package/dist/utils/package.d.cts +15 -0
- package/dist/utils/package.d.cts.map +1 -0
- package/dist/utils/package.d.ts +15 -0
- package/dist/utils/package.d.ts.map +1 -0
- package/dist/utils/package.js +33 -0
- package/dist/utils/package.js.map +1 -0
- package/dist/utils/type-guards.cjs +48 -0
- package/dist/utils/type-guards.cjs.map +1 -0
- package/dist/utils/type-guards.d.cts +22 -0
- package/dist/utils/type-guards.d.cts.map +1 -0
- package/dist/utils/type-guards.d.ts +22 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +43 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/package.json +18 -19
- package/src/cli/commands/analyze.ts +101 -0
- package/src/cli/commands/baseline.ts +577 -0
- package/src/cli/commands/history.ts +1 -1
- package/src/cli/commands/init.ts +116 -194
- package/src/cli/commands/run.ts +183 -113
- package/src/cli/index.ts +425 -183
- package/src/config/budget-schema.ts +189 -0
- package/src/config/schema.ts +260 -1
- package/src/constants.ts +53 -1
- package/src/core/engine.ts +169 -22
- package/src/core/engines/accurate-engine.ts +195 -44
- package/src/core/engines/tinybench-engine.ts +3 -2
- package/src/core/output-path-resolver.ts +10 -2
- package/src/errors/base.ts +11 -2
- package/src/errors/budget.ts +38 -0
- package/src/errors/index.ts +3 -0
- package/src/index.ts +9 -0
- package/src/reporters/csv.ts +54 -25
- package/src/reporters/human.ts +434 -115
- package/src/reporters/json.ts +26 -71
- package/src/reporters/profile-human.ts +210 -0
- package/src/reporters/simple.ts +88 -54
- package/src/services/baseline-storage.ts +199 -0
- package/src/services/budget-evaluator.ts +182 -0
- package/src/services/config-manager.ts +24 -9
- package/src/services/file-loader.ts +3 -6
- package/src/services/profiler/profile-filter.ts +147 -0
- package/src/services/profiler/profile-parser.ts +194 -0
- package/src/services/profiler/profile-runner.ts +121 -0
- package/src/services/progress-manager.ts +12 -2
- package/src/services/reporter-registry.ts +46 -81
- package/src/types/budgets.ts +180 -0
- package/src/types/cli.ts +5 -238
- package/src/types/core.ts +52 -10
- package/src/types/index.ts +5 -0
- package/src/types/interfaces.ts +24 -6
- package/src/types/profiler.ts +135 -0
- package/src/types/utility.ts +0 -10
- package/src/utils/identifiers.ts +58 -0
- package/src/utils/package.ts +35 -0
- package/src/utils/type-guards.ts +51 -0
|
@@ -40,6 +40,12 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
40
40
|
*/
|
|
41
41
|
private static readonly MAX_ITERATIONS_PER_ROUND = 10000;
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Maximum iterations per round for async functions (much lower due to
|
|
45
|
+
* sequential await overhead)
|
|
46
|
+
*/
|
|
47
|
+
private static readonly MAX_ITERATIONS_PER_ROUND_ASYNC = 100;
|
|
48
|
+
|
|
43
49
|
private hasCheckedNativeSyntax = false;
|
|
44
50
|
|
|
45
51
|
private nativeSyntaxErrorShown = false;
|
|
@@ -65,34 +71,75 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
65
71
|
);
|
|
66
72
|
}
|
|
67
73
|
|
|
74
|
+
// Detect if function is async by testing it
|
|
75
|
+
const testResult = taskData.fn();
|
|
76
|
+
const isAsync =
|
|
77
|
+
testResult != null &&
|
|
78
|
+
typeof testResult === 'object' &&
|
|
79
|
+
'then' in testResult &&
|
|
80
|
+
typeof testResult.then === 'function';
|
|
81
|
+
if (isAsync) {
|
|
82
|
+
// Clean up the test promise/thenable
|
|
83
|
+
const thenable = testResult as PromiseLike<unknown>;
|
|
84
|
+
thenable.then(
|
|
85
|
+
() => {
|
|
86
|
+
/* ignore */
|
|
87
|
+
},
|
|
88
|
+
() => {
|
|
89
|
+
/* ignore */
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// For async functions, cap iterations to prevent excessive sequential rounds
|
|
95
|
+
// Even with limitBy: 'iterations', 5000 samples = 5000 rounds of async calls
|
|
96
|
+
// which is impractically slow. Cap at 1000 for reasonable balance.
|
|
97
|
+
const effectiveConfig =
|
|
98
|
+
isAsync && config.iterations > 1000
|
|
99
|
+
? { ...config, iterations: 1000 }
|
|
100
|
+
: config;
|
|
101
|
+
|
|
68
102
|
// Check for V8 native syntax support
|
|
69
103
|
const useOptGuards = this.checkNativeSyntax();
|
|
70
104
|
|
|
71
105
|
// Show helpful error if native syntax not available
|
|
72
106
|
if (!useOptGuards && !this.nativeSyntaxErrorShown) {
|
|
73
107
|
if (!config.quiet) {
|
|
108
|
+
// Get the path to the modestbench executable from process.argv
|
|
109
|
+
const modestbenchPath = process.argv[1] || 'modestbench';
|
|
110
|
+
|
|
74
111
|
console.warn(
|
|
75
|
-
'\n⚠️ AccurateEngine
|
|
112
|
+
'\n⚠️ AccurateEngine recommends --allow-natives-syntax flag for best accuracy.',
|
|
76
113
|
'\nRunning in fallback mode (reduced accuracy).',
|
|
77
114
|
'\n\nTo enable V8 optimization guards:',
|
|
78
|
-
|
|
79
|
-
'\n or add to package.json: "test": "node --allow-natives-syntax ..."',
|
|
115
|
+
`\n node --allow-natives-syntax ${modestbenchPath}`,
|
|
80
116
|
'\n',
|
|
81
117
|
);
|
|
82
118
|
}
|
|
83
119
|
this.nativeSyntaxErrorShown = true;
|
|
84
120
|
}
|
|
85
121
|
|
|
86
|
-
// Execute benchmark with or without opt guards
|
|
122
|
+
// Execute benchmark with or without opt guards, and with async support
|
|
87
123
|
const rawSamples = useOptGuards
|
|
88
|
-
? await this.executeBenchmarkWithOptGuards(
|
|
89
|
-
|
|
124
|
+
? await this.executeBenchmarkWithOptGuards(
|
|
125
|
+
taskData.fn,
|
|
126
|
+
effectiveConfig,
|
|
127
|
+
signal,
|
|
128
|
+
isAsync,
|
|
129
|
+
)
|
|
130
|
+
: await this.executeBenchmarkBasic(
|
|
131
|
+
taskData.fn,
|
|
132
|
+
effectiveConfig,
|
|
133
|
+
signal,
|
|
134
|
+
isAsync,
|
|
135
|
+
);
|
|
90
136
|
|
|
91
|
-
// Check if aborted
|
|
137
|
+
// Check if aborted - return minimal valid result marked as aborted
|
|
138
|
+
// (abort message is shown at run level, not per-task)
|
|
92
139
|
if (signal?.aborted) {
|
|
93
140
|
return {
|
|
141
|
+
aborted: true,
|
|
94
142
|
cv: 0,
|
|
95
|
-
error: new Error('Benchmark aborted by user signal'),
|
|
96
143
|
iterations: rawSamples.length,
|
|
97
144
|
marginOfError: 0,
|
|
98
145
|
max: 0,
|
|
@@ -166,18 +213,25 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
166
213
|
private async calculateInitialIterations(
|
|
167
214
|
fn: (...args: unknown[]) => unknown,
|
|
168
215
|
targetTime: number, // in seconds
|
|
216
|
+
isAsync = false,
|
|
169
217
|
): Promise<number> {
|
|
170
218
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
171
219
|
const timer = process.hrtime.bigint;
|
|
172
220
|
const MIN_RESOLUTION = 0.5; // nanoseconds
|
|
173
221
|
const SCALE = 1e9; // ns to seconds
|
|
174
222
|
|
|
175
|
-
// Run a quick test with 30 iterations
|
|
176
|
-
const testIterations = 30;
|
|
223
|
+
// Run a quick test with 30 iterations (fewer for async to keep it fast)
|
|
224
|
+
const testIterations = isAsync ? 10 : 30;
|
|
177
225
|
const start = timer();
|
|
178
226
|
|
|
179
|
-
|
|
180
|
-
|
|
227
|
+
if (isAsync) {
|
|
228
|
+
for (let i = 0; i < testIterations; i++) {
|
|
229
|
+
await fn();
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
for (let i = 0; i < testIterations; i++) {
|
|
233
|
+
fn();
|
|
234
|
+
}
|
|
181
235
|
}
|
|
182
236
|
|
|
183
237
|
const duration = Number(timer() - start);
|
|
@@ -186,8 +240,13 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
186
240
|
// Calculate how many iterations we need for targetTime
|
|
187
241
|
const totalOpsForTargetTime = targetTime / (durationPerOp / SCALE);
|
|
188
242
|
|
|
243
|
+
// Use appropriate max based on sync/async
|
|
244
|
+
const maxIterations = isAsync
|
|
245
|
+
? AccurateEngine.MAX_ITERATIONS_PER_ROUND_ASYNC
|
|
246
|
+
: AccurateEngine.MAX_ITERATIONS_PER_ROUND;
|
|
247
|
+
|
|
189
248
|
return Math.min(
|
|
190
|
-
|
|
249
|
+
maxIterations,
|
|
191
250
|
Math.max(1, Math.round(totalOpsForTargetTime)),
|
|
192
251
|
);
|
|
193
252
|
}
|
|
@@ -224,6 +283,7 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
224
283
|
fn: (...args: unknown[]) => unknown,
|
|
225
284
|
config: ModestBenchConfig,
|
|
226
285
|
signal?: AbortSignal,
|
|
286
|
+
isAsync = false,
|
|
227
287
|
): Promise<number[]> {
|
|
228
288
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
229
289
|
const timer = process.hrtime.bigint;
|
|
@@ -234,26 +294,70 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
234
294
|
const initialIterations = await this.calculateInitialIterations(
|
|
235
295
|
fn,
|
|
236
296
|
targetTime,
|
|
297
|
+
isAsync,
|
|
237
298
|
);
|
|
238
299
|
|
|
239
300
|
if (config.warmup > 0) {
|
|
240
301
|
const warmupTime = Math.min(config.warmup / 1000, 0.05);
|
|
241
|
-
await this.runWarmup(fn, initialIterations, warmupTime);
|
|
302
|
+
await this.runWarmup(fn, initialIterations, warmupTime, isAsync);
|
|
242
303
|
}
|
|
243
304
|
|
|
244
305
|
const maxDuration = (config.time / 1000) * SCALE;
|
|
245
306
|
let timeSpent = 0;
|
|
246
307
|
let iterations = initialIterations;
|
|
247
308
|
|
|
248
|
-
|
|
309
|
+
// Use appropriate max based on sync/async
|
|
310
|
+
const maxIterations = isAsync
|
|
311
|
+
? AccurateEngine.MAX_ITERATIONS_PER_ROUND_ASYNC
|
|
312
|
+
: AccurateEngine.MAX_ITERATIONS_PER_ROUND;
|
|
313
|
+
|
|
314
|
+
// Determine loop condition based on limitBy setting
|
|
315
|
+
const shouldContinue = (): boolean => {
|
|
249
316
|
if (signal?.aborted) {
|
|
250
|
-
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const timeRemaining = timeSpent < maxDuration;
|
|
321
|
+
const samplesRemaining = samples.length < config.iterations;
|
|
322
|
+
|
|
323
|
+
switch (config.limitBy) {
|
|
324
|
+
case 'all':
|
|
325
|
+
return timeRemaining && samplesRemaining;
|
|
326
|
+
case 'any':
|
|
327
|
+
return timeRemaining || samplesRemaining;
|
|
328
|
+
case 'iterations':
|
|
329
|
+
return samplesRemaining;
|
|
330
|
+
case 'time':
|
|
331
|
+
return timeRemaining;
|
|
332
|
+
default:
|
|
333
|
+
return timeRemaining && samplesRemaining;
|
|
251
334
|
}
|
|
335
|
+
};
|
|
252
336
|
|
|
337
|
+
while (shouldContinue()) {
|
|
253
338
|
const start = timer();
|
|
254
339
|
|
|
255
|
-
|
|
256
|
-
|
|
340
|
+
if (isAsync) {
|
|
341
|
+
// For async functions, await each call individually and check for abort
|
|
342
|
+
for (let i = 0; i < iterations; i++) {
|
|
343
|
+
if (signal?.aborted) {
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
await fn();
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
// For sync functions, check for abort every 50 iterations
|
|
350
|
+
for (let i = 0; i < iterations; i++) {
|
|
351
|
+
if (i % 50 === 0 && signal?.aborted) {
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
fn();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Early exit if aborted during inner loop
|
|
359
|
+
if (signal?.aborted) {
|
|
360
|
+
break;
|
|
257
361
|
}
|
|
258
362
|
|
|
259
363
|
const duration = Number(timer() - start);
|
|
@@ -268,10 +372,7 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
268
372
|
|
|
269
373
|
const remainingTime = Math.max(0, (maxDuration - timeSpent) / SCALE);
|
|
270
374
|
iterations = Math.round(remainingTime / (durationPerOp / SCALE));
|
|
271
|
-
iterations = Math.max(
|
|
272
|
-
1,
|
|
273
|
-
Math.min(AccurateEngine.MAX_ITERATIONS_PER_ROUND, iterations),
|
|
274
|
-
);
|
|
375
|
+
iterations = Math.max(1, Math.min(maxIterations, iterations));
|
|
275
376
|
}
|
|
276
377
|
|
|
277
378
|
return samples;
|
|
@@ -284,6 +385,7 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
284
385
|
fn: (...args: unknown[]) => unknown,
|
|
285
386
|
config: ModestBenchConfig,
|
|
286
387
|
signal?: AbortSignal,
|
|
388
|
+
isAsync = false,
|
|
287
389
|
): Promise<number[]> {
|
|
288
390
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
289
391
|
const timer = process.hrtime.bigint;
|
|
@@ -295,12 +397,13 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
295
397
|
const initialIterations = await this.calculateInitialIterations(
|
|
296
398
|
fn,
|
|
297
399
|
targetTime,
|
|
400
|
+
isAsync,
|
|
298
401
|
);
|
|
299
402
|
|
|
300
403
|
// Run warmup
|
|
301
404
|
if (config.warmup > 0) {
|
|
302
405
|
const warmupTime = Math.min(config.warmup / 1000, 0.05); // Max 50ms warmup
|
|
303
|
-
await this.runWarmup(fn, initialIterations, warmupTime);
|
|
406
|
+
await this.runWarmup(fn, initialIterations, warmupTime, isAsync);
|
|
304
407
|
}
|
|
305
408
|
|
|
306
409
|
// Create DoNotOptimize wrapper using V8 intrinsics
|
|
@@ -321,17 +424,61 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
321
424
|
let timeSpent = 0;
|
|
322
425
|
let iterations = initialIterations;
|
|
323
426
|
|
|
324
|
-
//
|
|
325
|
-
|
|
427
|
+
// Use appropriate max based on sync/async
|
|
428
|
+
const maxIterations = isAsync
|
|
429
|
+
? AccurateEngine.MAX_ITERATIONS_PER_ROUND_ASYNC
|
|
430
|
+
: AccurateEngine.MAX_ITERATIONS_PER_ROUND;
|
|
431
|
+
|
|
432
|
+
// Determine loop condition based on limitBy setting
|
|
433
|
+
const shouldContinue = (): boolean => {
|
|
326
434
|
if (signal?.aborted) {
|
|
327
|
-
|
|
435
|
+
return false;
|
|
328
436
|
}
|
|
329
437
|
|
|
438
|
+
const timeRemaining = timeSpent < maxDuration;
|
|
439
|
+
const samplesRemaining = samples.length < config.iterations;
|
|
440
|
+
|
|
441
|
+
switch (config.limitBy) {
|
|
442
|
+
case 'all':
|
|
443
|
+
return timeRemaining && samplesRemaining;
|
|
444
|
+
case 'any':
|
|
445
|
+
return timeRemaining || samplesRemaining;
|
|
446
|
+
case 'iterations':
|
|
447
|
+
return samplesRemaining;
|
|
448
|
+
case 'time':
|
|
449
|
+
return timeRemaining;
|
|
450
|
+
default:
|
|
451
|
+
return timeRemaining && samplesRemaining;
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// Main benchmark loop
|
|
456
|
+
while (shouldContinue()) {
|
|
330
457
|
const start = timer();
|
|
331
458
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
459
|
+
if (isAsync) {
|
|
460
|
+
// For async functions, await each call individually and check for abort
|
|
461
|
+
for (let i = 0; i < iterations; i++) {
|
|
462
|
+
if (signal?.aborted) {
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
const result = await fn();
|
|
466
|
+
guardedDoNotOptimize(result); // Prevent optimization
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
// For sync functions, check for abort every 50 iterations
|
|
470
|
+
for (let i = 0; i < iterations; i++) {
|
|
471
|
+
if (i % 50 === 0 && signal?.aborted) {
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
const result = fn();
|
|
475
|
+
guardedDoNotOptimize(result); // Prevent optimization
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Early exit if aborted during inner loop
|
|
480
|
+
if (signal?.aborted) {
|
|
481
|
+
break;
|
|
335
482
|
}
|
|
336
483
|
|
|
337
484
|
const duration = Number(timer() - start);
|
|
@@ -348,10 +495,7 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
348
495
|
// Adjust iterations for next round
|
|
349
496
|
const remainingTime = Math.max(0, (maxDuration - timeSpent) / SCALE);
|
|
350
497
|
iterations = Math.round(remainingTime / (durationPerOp / SCALE));
|
|
351
|
-
iterations = Math.max(
|
|
352
|
-
1,
|
|
353
|
-
Math.min(AccurateEngine.MAX_ITERATIONS_PER_ROUND, iterations),
|
|
354
|
-
);
|
|
498
|
+
iterations = Math.max(1, Math.min(maxIterations, iterations));
|
|
355
499
|
}
|
|
356
500
|
|
|
357
501
|
return samples;
|
|
@@ -365,6 +509,7 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
365
509
|
fn: Function,
|
|
366
510
|
initialIterations: number,
|
|
367
511
|
warmupTime: number, // in seconds
|
|
512
|
+
isAsync = false,
|
|
368
513
|
): Promise<void> {
|
|
369
514
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
370
515
|
const timer = process.hrtime.bigint;
|
|
@@ -373,19 +518,28 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
373
518
|
const maxDuration = warmupTime * SCALE;
|
|
374
519
|
const minSamples = 10;
|
|
375
520
|
|
|
521
|
+
// Use appropriate max based on sync/async
|
|
522
|
+
const maxIterations = isAsync
|
|
523
|
+
? AccurateEngine.MAX_ITERATIONS_PER_ROUND_ASYNC
|
|
524
|
+
: AccurateEngine.MAX_ITERATIONS_PER_ROUND;
|
|
525
|
+
|
|
376
526
|
let timeSpent = 0n;
|
|
377
527
|
let samples = 0;
|
|
378
|
-
let iterations = Math.min(
|
|
379
|
-
initialIterations,
|
|
380
|
-
AccurateEngine.MAX_ITERATIONS_PER_ROUND,
|
|
381
|
-
);
|
|
528
|
+
let iterations = Math.min(initialIterations, maxIterations);
|
|
382
529
|
|
|
383
530
|
while (Number(timeSpent) < maxDuration || samples <= minSamples) {
|
|
384
531
|
const start = timer();
|
|
385
532
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
533
|
+
if (isAsync) {
|
|
534
|
+
for (let i = 0; i < iterations; i++) {
|
|
535
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
536
|
+
await fn();
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
for (let i = 0; i < iterations; i++) {
|
|
540
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
541
|
+
fn();
|
|
542
|
+
}
|
|
389
543
|
}
|
|
390
544
|
|
|
391
545
|
const duration = timer() - start;
|
|
@@ -402,10 +556,7 @@ export class AccurateEngine extends ModestBenchEngine {
|
|
|
402
556
|
(maxDuration - Number(timeSpent)) / SCALE,
|
|
403
557
|
);
|
|
404
558
|
iterations = Math.round(remainingTime / (durationPerOp / SCALE));
|
|
405
|
-
iterations = Math.max(
|
|
406
|
-
1,
|
|
407
|
-
Math.min(AccurateEngine.MAX_ITERATIONS_PER_ROUND, iterations),
|
|
408
|
-
);
|
|
559
|
+
iterations = Math.max(1, Math.min(maxIterations, iterations));
|
|
409
560
|
}
|
|
410
561
|
}
|
|
411
562
|
}
|
|
@@ -192,10 +192,11 @@ export class TinybenchEngine extends ModestBenchEngine {
|
|
|
192
192
|
|
|
193
193
|
// Check if the task was aborted
|
|
194
194
|
if (results.aborted) {
|
|
195
|
-
// Task was aborted via signal - return minimal valid result
|
|
195
|
+
// Task was aborted via signal - return minimal valid result marked as aborted
|
|
196
|
+
// (abort message is shown at run level, not per-task)
|
|
196
197
|
const taskResult: TaskResult = {
|
|
198
|
+
aborted: true,
|
|
197
199
|
cv: 0,
|
|
198
|
-
error: new Error('Benchmark aborted by user signal'),
|
|
199
200
|
iterations: results.latency?.samples?.length || 0,
|
|
200
201
|
marginOfError: 0,
|
|
201
202
|
max: 0,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isAbsolute, join, resolve } from 'node:path';
|
|
1
|
+
import { extname, isAbsolute, join, resolve } from 'node:path';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Resolves the final output path for a reporter
|
|
@@ -29,7 +29,15 @@ export const resolveOutputPath = (
|
|
|
29
29
|
return resolve(process.cwd(), outputFile);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
//
|
|
32
|
+
// If outputDir looks like a file (has extension), treat it as a file path
|
|
33
|
+
// This handles cases like: --output results.csv
|
|
34
|
+
if (outputDir && extname(outputDir)) {
|
|
35
|
+
return isAbsolute(outputDir)
|
|
36
|
+
? outputDir
|
|
37
|
+
: resolve(process.cwd(), outputDir);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fall back to default behavior (outputDir is a directory)
|
|
33
41
|
if (outputDir && defaultFilename) {
|
|
34
42
|
return join(outputDir, defaultFilename);
|
|
35
43
|
}
|
package/src/errors/base.ts
CHANGED
|
@@ -143,10 +143,19 @@ export const isModestBenchError = (
|
|
|
143
143
|
error: unknown,
|
|
144
144
|
): error is ModestBenchError => {
|
|
145
145
|
return (
|
|
146
|
-
|
|
147
|
-
error !== null &&
|
|
146
|
+
isError(error) &&
|
|
148
147
|
'code' in error &&
|
|
149
148
|
typeof (error as { code: unknown }).code === 'string' &&
|
|
150
149
|
(error as { code: string }).code.startsWith('ERR_MB_')
|
|
151
150
|
);
|
|
152
151
|
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Type guard to check if an error is a standard Error
|
|
155
|
+
*
|
|
156
|
+
* @param error - The error to check
|
|
157
|
+
* @returns `true` if the error is an `Error`
|
|
158
|
+
*/
|
|
159
|
+
export const isError = (error: unknown): error is Error => {
|
|
160
|
+
return error instanceof Error;
|
|
161
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget-related errors
|
|
3
|
+
*
|
|
4
|
+
* Errors that occur during budget evaluation and enforcement.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { BudgetSummary } from '../types/core.js';
|
|
8
|
+
|
|
9
|
+
import { ModestBenchError } from './base.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when performance budgets are exceeded
|
|
13
|
+
*
|
|
14
|
+
* Thrown when budget evaluation fails and budgetMode is set to 'fail'. Contains
|
|
15
|
+
* the full budget summary for detailed reporting.
|
|
16
|
+
*/
|
|
17
|
+
export class BudgetExceededError extends ModestBenchError {
|
|
18
|
+
/**
|
|
19
|
+
* Budget summary containing details of all violations
|
|
20
|
+
*/
|
|
21
|
+
public readonly budgetSummary: BudgetSummary;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Error code for budget exceeded errors
|
|
25
|
+
*/
|
|
26
|
+
readonly code = 'ERR_MB_BUDGET_EXCEEDED';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a new budget exceeded error
|
|
30
|
+
*
|
|
31
|
+
* @param message - Human-readable error message
|
|
32
|
+
* @param budgetSummary - Budget evaluation results
|
|
33
|
+
*/
|
|
34
|
+
constructor(message: string, budgetSummary: BudgetSummary) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.budgetSummary = budgetSummary;
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/errors/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -26,11 +26,17 @@ export * from './errors/index.js';
|
|
|
26
26
|
export { CsvReporter } from './reporters/csv.js';
|
|
27
27
|
export { HumanReporter } from './reporters/human.js';
|
|
28
28
|
export { JsonReporter } from './reporters/json.js';
|
|
29
|
+
export { ProfileHumanReporter } from './reporters/profile-human.js';
|
|
29
30
|
|
|
30
31
|
// Services
|
|
31
32
|
export { ModestBenchConfigurationManager } from './services/config-manager.js';
|
|
32
33
|
export { BenchmarkFileLoader } from './services/file-loader.js';
|
|
33
34
|
export { FileHistoryStorage } from './services/history-storage.js';
|
|
35
|
+
// Profiler services
|
|
36
|
+
export { filterProfile } from './services/profiler/profile-filter.js';
|
|
37
|
+
export { parseProfile } from './services/profiler/profile-parser.js';
|
|
38
|
+
|
|
39
|
+
export { runWithProfiling } from './services/profiler/profile-runner.js';
|
|
34
40
|
export { ModestBenchProgressManager } from './services/progress-manager.js';
|
|
35
41
|
export {
|
|
36
42
|
BaseReporter,
|
|
@@ -40,3 +46,6 @@ export {
|
|
|
40
46
|
|
|
41
47
|
// Export all types
|
|
42
48
|
export * from './types/index.js';
|
|
49
|
+
|
|
50
|
+
// Utilities
|
|
51
|
+
export { findPackageRoot } from './utils/package.js';
|