@@ -8,7 +8,9 @@ const { styleText } = require('node:util'); |
8 | 8 | const DEFAULT_RUNS = 30; // Number of runs for each n value |
9 | 9 | const CV_THRESHOLD = 0.05; // 5% coefficient of variation threshold |
10 | 10 | const MAX_N_INCREASE = 6; // Maximum number of times to increase n (10**6) |
| 11 | +const MAX_CV_THRESHOLD = 0.10; // 10% coefficient of variation threshold for individual configurations |
11 | 12 | const INCREASE_FACTOR = 10; // Factor by which to increase n |
| 13 | +const START_N = 10; // Starting n value (10 iterations) |
12 | 14 |
|
13 | 15 | const args = process.argv.slice(2); |
14 | 16 | if (args.length === 0) { |
@@ -19,7 +21,7 @@ Options: |
19 | 21 | --runs=N Number of runs for each n value (default: ${DEFAULT_RUNS}) |
20 | 22 | --cv-threshold=N Target coefficient of variation threshold (default: ${CV_THRESHOLD}) |
21 | 23 | --max-increases=N Maximum number of n increases to try (default: ${MAX_N_INCREASE}) |
22 | | - --start-n=N Initial n value to start with (default: autodetect) |
| 24 | + --start-n=N Initial n value to start with (default: ${START_N}) |
23 | 25 | --increase=N Factor by which to increase n (default: ${INCREASE_FACTOR}) |
24 | 26 |
|
25 | 27 | Example: |
@@ -34,25 +36,33 @@ let benchmarkPath; |
34 | 36 | let runs = DEFAULT_RUNS; |
35 | 37 | let cvThreshold = CV_THRESHOLD; |
36 | 38 | let maxIncreases = MAX_N_INCREASE; |
37 | | -let startN = 10; |
| 39 | +let startN = START_N; |
38 | 40 | let increaseFactor = INCREASE_FACTOR; |
39 | 41 |
|
40 | 42 | for (const arg of args) { |
41 | 43 | if (arg.startsWith('--runs=')) { |
42 | 44 | runs = parseInt(arg.substring(7), 10); |
| 45 | + if (isNaN(runs)) { |
| 46 | + console.error(`Error: Invalid value for --runs. Using default: ${DEFAULT_RUNS}`); |
| 47 | + runs = DEFAULT_RUNS; |
| 48 | + } |
43 | 49 | } else if (arg.startsWith('--cv-threshold=')) { |
44 | | - cvThreshold = parseFloat(arg.substring(14)); |
| 50 | + cvThreshold = parseFloat(arg.substring(15)); |
| 51 | + if (isNaN(cvThreshold)) { |
| 52 | + console.error(`Error: Invalid value for --cv-threshold. Using default: ${CV_THRESHOLD}`); |
| 53 | + cvThreshold = CV_THRESHOLD; |
| 54 | + } |
45 | 55 | } else if (arg.startsWith('--max-increases=')) { |
46 | | - maxIncreases = parseInt(arg.substring(15), 10); |
| 56 | + maxIncreases = parseInt(arg.substring(16), 10); |
47 | 57 | if (isNaN(maxIncreases)) { |
48 | 58 | console.error(`Error: Invalid value for --max-increases. Using default: ${MAX_N_INCREASE}`); |
49 | 59 | maxIncreases = MAX_N_INCREASE; |
50 | 60 | } |
51 | 61 | } else if (arg.startsWith('--start-n=')) { |
52 | 62 | startN = parseInt(arg.substring(10), 10); |
53 | 63 | if (isNaN(startN)) { |
54 | | - console.error(`Error: Invalid value for --start-n. Using default: 10`); |
55 | | - startN = 10; |
| 64 | + console.error(`Error: Invalid value for --start-n. Using default: ${START_N}`); |
| 65 | + startN = START_N; |
56 | 66 | } |
57 | 67 | } else if (arg.startsWith('--increase=')) { |
58 | 68 | increaseFactor = parseInt(arg.substring(11), 10); |
@@ -125,6 +135,7 @@ async function main(n = startN) { |
125 | 135 | let bestN = n; |
126 | 136 | let bestCV = Infinity; |
127 | 137 | let bestGroupStats = null; |
| 138 | + const cvThresholdPercentage = (cvThreshold * 100).toFixed(2); |
128 | 139 |
|
129 | 140 | console.log(` |
130 | 141 | -------------------------------------------------------- |
@@ -136,12 +147,12 @@ that produces consistent benchmark results without wasting time. |
136 | 147 | How it works: |
137 | 148 | 1. Run the benchmark multiple times with a specific n value |
138 | 149 | 2. Group results by configuration |
139 | | -3. If overall CV is above 5% or any configuration has CV above 10%, increase n and try again |
| 150 | +3. If overall CV is above ${cvThresholdPercentage}% or any configuration has CV above ${MAX_CV_THRESHOLD * 100}%, increase n and try again |
140 | 151 |
|
141 | 152 | Configuration: |
142 | 153 | - Starting n: ${n.toLocaleString()} iterations |
143 | 154 | - Runs per n value: ${runs} |
144 | | -- Target CV threshold: ${cvThreshold * 100}% (lower CV = more stable results) |
| 155 | +- Target CV threshold: ${cvThresholdPercentage}% (lower CV = more stable results) |
145 | 156 | - Max increases: ${maxIncreases} |
146 | 157 | - Increase factor: ${increaseFactor}x`); |
147 | 158 |
|
@@ -195,23 +206,23 @@ Configuration: |
195 | 206 |
|
196 | 207 | if (groupStats.length > 0) { |
197 | 208 | // Check if any configuration has CV > 10% (too unstable) |
198 | | - const tooUnstableConfigs = groupStats.filter((g) => g.stats.cv > 0.10); |
| 209 | + const tooUnstableConfigs = groupStats.filter((g) => g.stats.cv > MAX_CV_THRESHOLD); |
199 | 210 |
|
200 | 211 | const avgCV = groupStats.reduce((sum, g) => sum + g.stats.cv, 0) / groupStats.length; |
201 | 212 | console.log(`\nOverall average CV: ${(avgCV * 100).toFixed(2)}%`); |
202 | 213 |
|
203 | | - const isOverallStable = avgCV < CV_THRESHOLD; |
| 214 | + const isOverallStable = avgCV < cvThreshold; |
204 | 215 | const hasVeryUnstableConfigs = tooUnstableConfigs.length > 0; |
205 | 216 |
|
206 | | - // Check if overall CV is below CV_THRESHOLD and no configuration has CV > 10% |
| 217 | + // Check if overall CV is below cvThreshold and no configuration has CV > MAX_CV_THRESHOLD |
207 | 218 | if (isOverallStable && !hasVeryUnstableConfigs) { |
208 | | - console.log(styleText(['bold', 'green'], ` ✓ Overall CV is below 5% and no configuration has CV above 10%`)); |
| 219 | + console.log(styleText(['bold', 'green'], ` ✓ Overall CV is below ${cvThresholdPercentage}% and no configuration has CV above ${MAX_CV_THRESHOLD * 100}%`)); |
209 | 220 | } else { |
210 | 221 | if (!isOverallStable) { |
211 | | - console.log(styleText(['bold', 'red'], ` ✗ Overall CV (${(avgCV * 100).toFixed(2)}%) is above 5%`)); |
| 222 | + console.log(styleText(['bold', 'red'], ` ✗ Overall CV (${(avgCV * 100).toFixed(2)}%) is above ${cvThresholdPercentage}%`)); |
212 | 223 | } |
213 | 224 | if (hasVeryUnstableConfigs) { |
214 | | - console.log(styleText(['bold', 'red'], ` ✗ ${tooUnstableConfigs.length} configuration(s) have CV above 10%`)); |
| 225 | + console.log(styleText(['bold', 'red'], ` ✗ ${tooUnstableConfigs.length} configuration(s) have CV above ${MAX_CV_THRESHOLD * 100}%`)); |
215 | 226 | } |
216 | 227 | } |
217 | 228 |
|
@@ -226,7 +237,7 @@ Configuration: |
226 | 237 | bestGroupStats.push({ |
227 | 238 | conf: group.conf, |
228 | 239 | stats: stats, |
229 | | - isStable: stats.cv <= 0.10, |
| 240 | + isStable: stats.cv <= MAX_CV_THRESHOLD, |
230 | 241 | }); |
231 | 242 | } |
232 | 243 | } |
@@ -237,15 +248,15 @@ Configuration: |
237 | 248 | } |
238 | 249 |
|
239 | 250 | // Check if we've reached acceptable stability based on new criteria |
240 | | - // 1. Overall CV should be below CV_THRESHOLD |
241 | | - // 2. No configuration should have a CV greater than 10% |
| 251 | + // 1. Overall CV should be below cvThreshold |
| 252 | + // 2. No configuration should have a CV greater than MAX_CV_THRESHOLD |
242 | 253 | const avgCV = groupStats.length > 0 ? |
243 | 254 | groupStats.reduce((sum, g) => sum + g.stats.cv, 0) / groupStats.length : Infinity; |
244 | | - const hasUnstableConfig = groupStats.some((g) => g.stats.cv > 0.10); |
245 | | - const isOverallStable = avgCV < CV_THRESHOLD; |
| 255 | + const hasUnstableConfig = groupStats.some((g) => g.stats.cv > MAX_CV_THRESHOLD); |
| 256 | + const isOverallStable = avgCV < cvThreshold; |
246 | 257 |
|
247 | 258 | if (isOverallStable && !hasUnstableConfig) { |
248 | | - console.log(`\n✓ Found optimal n=${n} (Overall CV=${(avgCV * 100).toFixed(2)}% < 5% and no configuration has CV > 10%)`); |
| 259 | + console.log(`\n✓ Found optimal n=${n} (Overall CV=${(avgCV * 100).toFixed(2)}% < ${cvThresholdPercentage}% and no configuration has CV > ${MAX_CV_THRESHOLD * 100}%)`); |
249 | 260 | console.log('\nFinal CV for each configuration:'); |
250 | 261 | groupStats.forEach((g) => { |
251 | 262 | console.log(` ${JSON.stringify(groupedResults[g.confKey].conf)}: ${(g.stats.cv * 100).toFixed(2)}%`); |
@@ -271,7 +282,7 @@ Configuration: |
271 | 282 | if (g.conf) { |
272 | 283 | console.log(` ${JSON.stringify(g.conf)}: ${(g.stats.cv * 100).toFixed(2)}%`); |
273 | 284 | if (g.stats.cv > cvThreshold) { |
274 | | - console.log(` ⚠️ This configuration is above the target threshold of ${cvThreshold * 100}%`); |
| 285 | + console.log(` ⚠️ This configuration is above the target threshold of ${cvThresholdPercentage}%`); |
275 | 286 | } |
276 | 287 | } |
277 | 288 | }); |
|