Skip to content

Commit 9613021

Browse files
authored
chore: add bson-ext to benchmark tool (#503)
1 parent 651b60e commit 9613021

File tree

2 files changed

+187
-88
lines changed

2 files changed

+187
-88
lines changed

etc/benchmarks/lib_runner.mjs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2+
/* eslint-disable @typescript-eslint/no-var-requires */
3+
import { performance } from 'perf_hooks';
4+
import { readFile } from 'fs/promises';
5+
import { cpus, totalmem } from 'os';
6+
import { exec as execCb } from 'child_process';
7+
import { promisify } from 'util';
8+
const exec = promisify(execCb);
9+
10+
const hw = cpus();
11+
const ram = totalmem() / 1024 ** 3;
12+
const platform = { name: hw[0].model, cores: hw.length, ram: `${ram}GB` };
13+
14+
export const systemInfo = iterations =>
15+
[
16+
`\n- cpu: ${platform.name}`,
17+
`- cores: ${platform.cores}`,
18+
`- os: ${process.platform}`,
19+
`- ram: ${platform.ram}`,
20+
`- iterations: ${iterations.toLocaleString()}`
21+
].join('\n');
22+
23+
export const readJSONFile = async path =>
24+
JSON.parse(await readFile(new URL(path, import.meta.url), { encoding: 'utf8' }));
25+
26+
function average(array) {
27+
let sum = 0;
28+
for (const value of array) sum += value;
29+
return sum / array.length;
30+
}
31+
32+
function testPerformance(lib, [fn, arg], iterations) {
33+
let measurements = [];
34+
let thrownError = null;
35+
for (let i = 0; i < iterations; i++) {
36+
const start = performance.now();
37+
try {
38+
fn(i, lib, arg);
39+
} catch (error) {
40+
thrownError = error;
41+
break;
42+
}
43+
const end = performance.now();
44+
measurements.push(end - start);
45+
}
46+
return { result: average(measurements).toFixed(8), thrownError };
47+
}
48+
49+
export function getCurrentLocalBSON(libs) {
50+
return libs.filter(({ name }) => name === 'local')[0];
51+
}
52+
53+
export async function getLibs() {
54+
return await Promise.all([
55+
(async () => {
56+
const { stdout } = await exec('git rev-parse --short HEAD');
57+
const hash = stdout.trim();
58+
return {
59+
name: 'local',
60+
lib: await import('../../lib/bson.js'),
61+
version: hash
62+
};
63+
})(),
64+
(async () => ({
65+
name: 'released',
66+
lib: await import('../../node_modules/bson_latest/lib/bson.js'),
67+
version: (await readJSONFile('../../node_modules/bson_latest/package.json')).version
68+
}))(),
69+
(async () => {
70+
const legacyBSON = (await import('../../node_modules/bson_legacy/index.js')).default;
71+
return {
72+
name: 'previous major',
73+
lib: { ...legacyBSON, ...legacyBSON.prototype },
74+
version: (await readJSONFile('../../node_modules/bson_legacy/package.json')).version
75+
};
76+
})(),
77+
(async () => ({
78+
name: 'bson-ext',
79+
lib: await import('../../node_modules/bson_ext/lib/index.js'),
80+
version: (await readJSONFile('../../node_modules/bson_ext/package.json')).version
81+
}))()
82+
]).catch(error => {
83+
console.error(error);
84+
console.error(
85+
`Please run:\n${[
86+
'npm run build',
87+
'npm install --no-save bson_ext@npm:bson-ext@4 bson_legacy@npm:bson@1 bson_latest@npm:bson@latest'
88+
].join('\n')}`
89+
);
90+
process.exit(1);
91+
});
92+
}
93+
94+
/**
95+
* ```ts
96+
* interface {
97+
* iterations?: number;
98+
* setup: (lib: any[]) => any;
99+
* name: string;
100+
* run:(index: number, bson: typeof import('../../src/bson'), setupRes: any) => any )
101+
* }
102+
* ```
103+
*/
104+
export async function runner({ iterations, setup, name, run, skip }) {
105+
if (skip) {
106+
console.log(`skipped ${name}\n`);
107+
return;
108+
}
109+
const BSONLibs = await getLibs();
110+
const setupResult = setup?.(BSONLibs) ?? null;
111+
112+
console.log(`\ntesting: ${name}`);
113+
114+
for (const bson of BSONLibs) {
115+
const { result: perf, thrownError } = testPerformance(bson, [run, setupResult], iterations);
116+
if (thrownError != null) {
117+
console.log(
118+
`${bson.name.padEnd(14, ' ')} - v ${bson.version.padEnd(8, ' ')} - error ${thrownError}`
119+
);
120+
} else {
121+
console.log(
122+
`${bson.name.padEnd(14, ' ')} - v ${bson.version.padEnd(8, ' ')} - avg ${perf}ms`
123+
);
124+
}
125+
}
126+
127+
console.log();
128+
}

etc/benchmarks/main.mjs

Lines changed: 59 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,66 @@
11
/* eslint-disable @typescript-eslint/no-var-requires */
22
import { performance } from 'perf_hooks';
3-
import { readFile } from 'fs/promises';
4-
import { cpus, totalmem } from 'os';
3+
import { runner, systemInfo, getCurrentLocalBSON } from './lib_runner.mjs';
54

6-
const hw = cpus();
7-
const ram = totalmem() / 1024 ** 3;
8-
const platform = { name: hw[0].model, cores: hw.length, ram: `${ram}GB` };
9-
const ITERATIONS = 1_000_000;
5+
const iterations = 1_000_000;
6+
const startedEntireRun = performance.now();
7+
console.log(systemInfo(iterations));
8+
console.log();
109

11-
const systemInfo = [
12-
`\n- cpu: ${platform.name}`,
13-
`- cores: ${platform.cores}`,
14-
`- os: ${process.platform}`,
15-
`- ram: ${platform.ram}`,
16-
`- iterations: ${ITERATIONS.toLocaleString()}`
17-
].join('\n');
18-
19-
const readJSONFile = async path =>
20-
JSON.parse(await readFile(new URL(path, import.meta.url), { encoding: 'utf8' }));
21-
22-
function average(array) {
23-
let sum = 0;
24-
for (const value of array) sum += value;
25-
return sum / array.length;
26-
}
27-
28-
function testPerformance(fn, iterations = ITERATIONS) {
29-
let measurements = [];
30-
for (let i = 0; i < iterations; i++) {
31-
const start = performance.now();
32-
fn(i);
33-
const end = performance.now();
34-
measurements.push(end - start);
35-
}
36-
return average(measurements).toFixed(8);
37-
}
38-
39-
async function main() {
40-
const [currentBSON, currentReleaseBSON, legacyBSONLib] = await Promise.all([
41-
(async () => ({
42-
lib: await import('../../lib/bson.js'),
43-
version: 'current local'
44-
}))(),
45-
(async () => ({
46-
lib: await import('../../node_modules/bson_latest/lib/bson.js'),
47-
version: (await readJSONFile('../../node_modules/bson_latest/package.json')).version
48-
}))(),
49-
(async () => {
50-
const legacyBSON = (await import('../../node_modules/bson_legacy/index.js')).default;
51-
return {
52-
lib: { ...legacyBSON, ...legacyBSON.prototype },
53-
version: (await readJSONFile('../../node_modules/bson_legacy/package.json')).version
54-
};
55-
})()
56-
]).catch(error => {
57-
console.error(error);
58-
console.error(
59-
`Please run:\n${[
60-
'npm run build',
61-
'npm install --no-save bson_legacy@npm:bson@1 bson_latest@npm:bson@latest'
62-
].join('\n')}`
63-
);
64-
process.exit(1);
65-
});
66-
67-
const documents = Array.from({ length: ITERATIONS }, () =>
68-
currentReleaseBSON.lib.serialize({
69-
_id: new currentReleaseBSON.lib.ObjectId(),
70-
field1: 'value1'
71-
})
72-
);
73-
74-
console.log(systemInfo);
75-
76-
for (const bson of [currentBSON, currentReleaseBSON, legacyBSONLib]) {
77-
console.log(`\nBSON@${bson.version}`);
78-
console.log(
79-
`deserialize({ oid, string }, { validation: { utf8: false } }) takes ${testPerformance(i =>
80-
bson.lib.deserialize(documents[i], { validation: { utf8: false } })
81-
)}ms on average`
82-
);
83-
84-
const oidBuffer = Buffer.from('00'.repeat(12), 'hex');
85-
console.log(
86-
`new Oid(buf) take ${testPerformance(() => new bson.lib.ObjectId(oidBuffer))}ms on average`
10+
////////////////////////////////////////////////////////////////////////////////////////////////////
11+
await runner({
12+
skip: false,
13+
name: 'deserialize({ oid, string }, { validation: { utf8: false } })',
14+
iterations,
15+
setup(libs) {
16+
const bson = getCurrentLocalBSON(libs);
17+
return Array.from({ length: iterations }, () =>
18+
bson.lib.serialize({
19+
_id: new bson.lib.ObjectId(),
20+
field1: 'value1'
21+
})
8722
);
23+
},
24+
run(i, bson, documents) {
25+
bson.lib.deserialize(documents[i], { validation: { utf8: false } });
8826
}
27+
});
28+
////////////////////////////////////////////////////////////////////////////////////////////////////
29+
await runner({
30+
skip: true,
31+
name: 'new Oid(buf)',
32+
iterations,
33+
setup() {
34+
return Buffer.from('00'.repeat(12), 'hex');
35+
},
36+
run(i, bson, oidBuffer) {
37+
new bson.lib.ObjectId(oidBuffer);
38+
}
39+
});
40+
////////////////////////////////////////////////////////////////////////////////////////////////////
41+
await runner({
42+
skip: true,
43+
name: 'BSON.deserialize(largeDocument)',
44+
iterations,
45+
setup(libs) {
46+
const bson = getCurrentLocalBSON(libs);
47+
const entries = Array.from({ length: 10 }, (_, i) => [
48+
`${i}_${'a'.repeat(10)}`,
49+
'b'.repeat(10)
50+
]);
51+
const document = Object.fromEntries(entries);
52+
const bytes = bson.lib.serialize(document);
53+
console.log(`largeDocument { byteLength: ${bytes.byteLength} }`);
54+
return bytes;
55+
},
56+
run(i, bson, largeDocument) {
57+
new bson.lib.deserialize(largeDocument);
58+
}
59+
});
8960

90-
console.log();
91-
}
92-
93-
main()
94-
.then(() => null)
95-
.catch(error => console.error(error));
61+
// End
62+
console.log(
63+
'Total time taken to benchmark:',
64+
(performance.now() - startedEntireRun).toLocaleString(),
65+
'ms'
66+
);

0 commit comments

Comments
 (0)