|
| 1 | +import { run, bench, do_not_optimize, lineplot } from 'mitata'; |
| 2 | +import { xSequentialFillFromStep } from 'ml-spectra-processing'; |
| 3 | + |
| 4 | +import { SparseMatrix } from '../src/index.js'; |
| 5 | + |
| 6 | +import { randomMatrix } from './utils/randomMatrix.js'; |
| 7 | +import { writeFile } from 'node:fs/promises'; |
| 8 | +import path from 'node:path'; |
| 9 | + |
| 10 | +/* eslint |
| 11 | +func-names: 0 |
| 12 | +camelcase: 0 |
| 13 | +*/ |
| 14 | +// Prepare matrices once |
| 15 | +const cardinalities = Array.from( |
| 16 | + xSequentialFillFromStep({ from: 10, step: 5, size: 2 }), |
| 17 | +); |
| 18 | + |
| 19 | +// const dimensions = Array.from( |
| 20 | +// xSequentialFillFromStep({ from: 700, step: 100, size: 13 }), |
| 21 | +// ); |
| 22 | + |
| 23 | +const dimensions = [512]; |
| 24 | +lineplot(() => { |
| 25 | + bench('hibrid($cardinality,$dimension)', function* (ctx) { |
| 26 | + const cardinality = ctx.get('cardinality'); |
| 27 | + const size = ctx.get('dimension'); |
| 28 | + // Prepare matrices once |
| 29 | + let A = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 30 | + let B = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 31 | + |
| 32 | + // Benchmark the multiplication |
| 33 | + yield () => do_not_optimize(A.mmul(B)); |
| 34 | + do_not_optimize(A); |
| 35 | + do_not_optimize(B); |
| 36 | + }) |
| 37 | + .gc('inner') |
| 38 | + .args('cardinality', cardinalities) //.range('size', 32, 1024, 2); //.args('size', sizes); |
| 39 | + .args('dimension', dimensions); |
| 40 | + |
| 41 | + bench('small($cardinality,$dimension)', function* (ctx) { |
| 42 | + const cardinality = ctx.get('cardinality'); |
| 43 | + const size = ctx.get('dimension'); |
| 44 | + // Prepare matrices once |
| 45 | + let A = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 46 | + let B = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 47 | + |
| 48 | + // Benchmark the multiplication |
| 49 | + yield () => do_not_optimize(A._mmulSmall(B)); |
| 50 | + // Explicit cleanup |
| 51 | + do_not_optimize(A); |
| 52 | + do_not_optimize(B); |
| 53 | + }) |
| 54 | + .gc('inner') |
| 55 | + .args('cardinality', cardinalities) //.range('size', 32, 1024, 2); //.args('size', sizes); |
| 56 | + .args('dimension', dimensions); |
| 57 | + |
| 58 | + bench('low($cardinality,$dimension)', function* (ctx) { |
| 59 | + const cardinality = ctx.get('cardinality'); |
| 60 | + const size = ctx.get('dimension'); |
| 61 | + // Prepare matrices once |
| 62 | + let A = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 63 | + let B = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 64 | + |
| 65 | + // Benchmark the multiplication |
| 66 | + yield () => do_not_optimize(A._mmulLowDensity(B)); |
| 67 | + // Explicit cleanup |
| 68 | + do_not_optimize(A); |
| 69 | + do_not_optimize(B); |
| 70 | + }) |
| 71 | + .gc('inner') |
| 72 | + .args('cardinality', cardinalities) //.range('size', 32, 1024, 2); //.args('size', sizes); |
| 73 | + .args('dimension', dimensions); |
| 74 | + |
| 75 | + bench('medium($cardinality,$dimension)', function* (ctx) { |
| 76 | + const cardinality = ctx.get('cardinality'); |
| 77 | + const size = ctx.get('dimension'); |
| 78 | + // Prepare matrices once |
| 79 | + let A = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 80 | + let B = new SparseMatrix(randomMatrix(size, size, cardinality)); |
| 81 | + |
| 82 | + // Benchmark the multiplication |
| 83 | + yield () => { |
| 84 | + do_not_optimize(A._mmulMediumDensity(B)); |
| 85 | + }; |
| 86 | + |
| 87 | + // Explicit cleanup |
| 88 | + do_not_optimize(A); |
| 89 | + do_not_optimize(B); |
| 90 | + }) |
| 91 | + .gc('inner') |
| 92 | + .args('cardinality', cardinalities) //.range('size', 32, 1024, 2); //.args('size', sizes); |
| 93 | + .args('dimension', dimensions); |
| 94 | +}); |
| 95 | + |
| 96 | +// Run benchmarks and capture results |
| 97 | +const results = await run({ |
| 98 | + // Force GC between every benchmark |
| 99 | + gc: true, |
| 100 | + // // More samples for statistical significance |
| 101 | + // min_samples: 20, |
| 102 | + // max_samples: 200, |
| 103 | + // // Longer warmup to stabilize CPU state |
| 104 | + // warmup_samples: 10, |
| 105 | + // warmup_threshold: 100, // ms |
| 106 | + // Longer minimum time for stable measurements |
| 107 | + // min_cpu_time: 2000, // 2 seconds minimum |
| 108 | + // // Batch settings to reduce variance |
| 109 | + // batch_samples: 5, |
| 110 | + // batch_threshold: 10, // ms |
| 111 | + // // Enable colors |
| 112 | + // colors: true, |
| 113 | +}); |
| 114 | + |
| 115 | +// Process and store results |
| 116 | +const processedResults = []; |
| 117 | + |
| 118 | +for (const benchmark of results.benchmarks) { |
| 119 | + for (const run of benchmark.runs) { |
| 120 | + if (run.stats) { |
| 121 | + processedResults.push({ |
| 122 | + name: benchmark.alias, |
| 123 | + cardinality: run.args.cardinality, |
| 124 | + dimension: run.args.dimension, |
| 125 | + avg: run.stats.avg, |
| 126 | + min: run.stats.min, |
| 127 | + max: run.stats.max, |
| 128 | + p50: run.stats.p50, |
| 129 | + p75: run.stats.p75, |
| 130 | + p99: run.stats.p99, |
| 131 | + samples: run.stats.samples.length, |
| 132 | + ticks: run.stats.ticks, |
| 133 | + }); |
| 134 | + } |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +// Save results to JSON file |
| 139 | +await writeFile( |
| 140 | + path.join(import.meta.dirname, `benchmark-results.json`), |
| 141 | + JSON.stringify(processedResults, null, 2), |
| 142 | +); |
0 commit comments