Skip to content

Commit a3568a8

Browse files
committed
Merge branch 'improve-performance-mmul' of github.com:jobo322/sparse-matrix into improve-performance-mmul
2 parents ac26a10 + 87966e4 commit a3568a8

18 files changed

+557
-488
lines changed

benchmark/benchmark.js

Lines changed: 0 additions & 127 deletions
This file was deleted.

benchmark/benchmarkLinePlot.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { run, bench, lineplot, do_not_optimize } from 'mitata';
2+
import { xSequentialFillFromStep } from 'ml-spectra-processing';
3+
4+
import { SparseMatrix } from '../src/index.js';
5+
6+
import { SparseMatrix as SparseMatrixOld } from './class/SparseMatrixOld.js';
7+
import { randomMatrix } from './utils/randomMatrix.js';
8+
9+
const density = 0.02; // Fixed density for this comparison;
10+
11+
/* eslint
12+
func-names: 0
13+
camelcase: 0
14+
*/
15+
// Prepare matrices once
16+
const sizes = Array.from(
17+
xSequentialFillFromStep({ from: 4, step: 4, size: 13 }),
18+
);
19+
lineplot(() => {
20+
bench('Sparse.mmul($size)', function* (ctx) {
21+
const size = ctx.get('size');
22+
23+
// Prepare matrices once
24+
const A = new SparseMatrix(randomMatrix(size, size, density));
25+
const B = new SparseMatrix(randomMatrix(size, size, density));
26+
// Benchmark the multiplication
27+
yield () => do_not_optimize(A.mmul(B));
28+
})
29+
.gc('inner')
30+
.args('size', sizes); // 16, 32, 64, 128, 256
31+
32+
bench('SparseOld.mmul($size)', function* (ctx) {
33+
const size = ctx.get('size');
34+
const A = randomMatrix(size, size, density);
35+
const B = randomMatrix(size, size, density);
36+
const AOld = new SparseMatrixOld(A);
37+
const BOld = new SparseMatrixOld(B);
38+
39+
// Benchmark the multiplication
40+
yield () => do_not_optimize(AOld.mmul(BOld));
41+
})
42+
.gc('inner')
43+
.args('size', sizes);
44+
45+
// bench('Dense.mmul($size)', function* (ctx) {
46+
// const size = ctx.get('size');
47+
48+
// // Prepare matrices once
49+
// const A = randomMatrix(size, size, density);
50+
// const B = randomMatrix(size, size, density);
51+
// const ADense = new Matrix(A);
52+
// const BDense = new Matrix(B);
53+
54+
// // Benchmark the multiplication
55+
// yield () => do_not_optimize(ADense.mmul(BDense));
56+
// }).gc('inner').range('size', min, max, multiplier);
57+
});
58+
59+
await run();

benchmark/class/SparseMatrixOld.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ export class SparseMatrix {
159159
*/
160160
mmul(other) {
161161
if (this.columns !== other.rows) {
162-
// eslint-disable-next-line no-console
163162
console.warn(
164163
'Number of columns of left matrix are not equal to number of rows of right matrix.',
165164
);
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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+
);

benchmark/denseMatrix.js

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { run, bench, group, do_not_optimize } from 'mitata';
2+
// import { Matrix } from 'ml-matrix';
3+
4+
import { SparseMatrix as SparseMatrixOld } from './class/SparseMatrixOld.js';
5+
import { randomMatrix } from './utils/randomMatrix.js';
6+
import { SparseMatrix } from '../src/index.js';
7+
8+
/* eslint
9+
func-names: 0
10+
camelcase: 0
11+
*/
12+
13+
const sizes = [8, 16, 32, 256, 512, 1024];
14+
const densities = [0.125, 0.0625, 0.03125, 0.0039, 0.00197, 0.001];
15+
16+
for (let i = 0; i < sizes.length; i++) {
17+
const size = sizes[i];
18+
const density = densities[i];
19+
const denseA = randomMatrix(size, size, density);
20+
const denseB = randomMatrix(size, size, density);
21+
const AOld = new SparseMatrixOld(denseA);
22+
const BOld = new SparseMatrixOld(denseB);
23+
24+
const A = new SparseMatrix(denseA);
25+
const B = new SparseMatrix(denseB);
26+
27+
group(`size:${size}-density:${density}`, () => {
28+
bench('mmulNew', () => {
29+
do_not_optimize(A.mmul(B));
30+
}).gc('inner');
31+
bench('mmul', () => {
32+
do_not_optimize(AOld.mmul(BOld));
33+
}).gc('inner');
34+
});
35+
}
36+
37+
await run({ silent: false });

0 commit comments

Comments
 (0)