Skip to content

Commit 465e8cd

Browse files
authored
chore(NODE-6382): add benchmarking for helpers (#717)
1 parent 61537f5 commit 465e8cd

File tree

7 files changed

+130
-0
lines changed

7 files changed

+130
-0
lines changed

.evergreen/config.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ functions:
113113
add_expansions_to_env: true
114114
args:
115115
- .evergreen/run-granular-benchmarks.sh
116+
run custom benchmarks:
117+
- command: subprocess.exec
118+
type: test
119+
params:
120+
working_dir: src
121+
binary: bash
122+
add_expansions_to_env: true
123+
args:
124+
- .evergreen/run-custom-benchmarks.sh
116125
run spec benchmarks:
117126
- command: subprocess.exec
118127
type: test
@@ -246,6 +255,19 @@ tasks:
246255
- command: perf.send
247256
params:
248257
file: src/test/bench/etc/resultsCollectedMeans.json
258+
- name: run-custom-benchmarks
259+
commands:
260+
- func: fetch source
261+
vars:
262+
# This needs to stay pinned at Node v18.16.0 for consistency across perf runs.
263+
NODE_LTS_VERSION: v18.16.0
264+
- func: install dependencies
265+
vars:
266+
NPM_VERSION: 9
267+
- func: run custom benchmarks
268+
- command: perf.send
269+
params:
270+
file: src/customBenchmarkResults.json
249271
- name: run-spec-benchmarks
250272
commands:
251273
- func: fetch source
@@ -300,3 +322,4 @@ buildvariants:
300322
tasks:
301323
- run-granular-benchmarks
302324
- run-spec-benchmarks
325+
- run-custom-benchmarks

.evergreen/run-custom-benchmarks.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
source "${PROJECT_DIRECTORY}/.evergreen/init-node-and-npm-env.sh"
4+
set -o xtrace
5+
6+
npm run check:custom-bench

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ docs/public
3333
.nvmrc
3434

3535
benchmarks.json
36+
customBenchmarkResults.json

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"check:web-no-bigint": "WEB=true NO_BIGINT=true mocha test/node",
106106
"check:granular-bench": "npm run build:bench && node ./test/bench/etc/run_granular_benchmarks.js",
107107
"check:spec-bench": "npm run build:bench && node ./test/bench/lib/spec/bsonBench.js",
108+
"check:custom-bench": "npm run build && node ./test/bench/custom/main.mjs",
108109
"build:bench": "cd test/bench && npx tsc",
109110
"build:ts": "node ./node_modules/typescript/bin/tsc",
110111
"build:dts": "npm run build:ts && api-extractor run --typescript-compiler-folder node_modules/typescript --local && node etc/clean_definition_files.cjs",

test/bench/custom/benchmarks.mjs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* eslint-disable strict */
2+
import { BSON } from '../../../lib/bson.mjs';
3+
4+
const ObjectId_isValid = [
5+
function objectid_isvalid_wrong_string_length() {
6+
BSON.ObjectId.isValid('a');
7+
},
8+
/** wrong character at the start, could be the most short circuited code path */
9+
function objectid_isvalid_invalid_hex_at_start() {
10+
BSON.ObjectId.isValid('g6e84ebdc96f4c0772f0cbbf');
11+
},
12+
/** wrong character at the end, could be the least short circuited code path */
13+
function objectid_isvalid_invalid_hex_at_end() {
14+
BSON.ObjectId.isValid('66e84ebdc96f4c0772f0cbbg');
15+
},
16+
function objectid_isvalid_valid_hex_string() {
17+
BSON.ObjectId.isValid('66e84ebdc96f4c0772f0cbbf');
18+
}
19+
];
20+
21+
// Add benchmarks here:
22+
export const benchmarks = [...ObjectId_isValid];

test/bench/custom/main.mjs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* eslint-disable strict */
2+
3+
import util from 'node:util';
4+
import fs from 'node:fs';
5+
import os from 'node:os';
6+
import benchmark from 'benchmark';
7+
import { benchmarks } from './benchmarks.mjs';
8+
9+
const hw = os.cpus();
10+
const ram = os.totalmem() / 1024 ** 3;
11+
const platform = { name: hw[0].model, cores: hw.length, ram: `${ram}GB` };
12+
13+
const systemInfo = () =>
14+
[
15+
`\n- cpu: ${platform.name}`,
16+
`- cores: ${platform.cores}`,
17+
`- arch: ${os.arch()}`,
18+
`- os: ${process.platform} (${os.release()})`,
19+
`- ram: ${platform.ram}\n`
20+
].join('\n');
21+
console.log(systemInfo());
22+
23+
const suite = new benchmark.Suite();
24+
25+
for (const bench of benchmarks) suite.add(bench.name, bench);
26+
27+
suite
28+
.on('cycle', function logBenchmark(event) {
29+
console.log(String(event.target));
30+
})
31+
.on('complete', function outputPerfSend() {
32+
const data = Array.from(this).map(bench => ({
33+
info: { test_name: bench.name },
34+
metrics: [{ name: 'ops_per_sec', value: bench.hz }]
35+
}));
36+
console.log(util.inspect(data, { depth: Infinity, colors: true }));
37+
fs.writeFileSync('customBenchmarkResults.json', JSON.stringify(data), 'utf8');
38+
})
39+
.run();

test/bench/custom/readme.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Custom Benchmark Tests
2+
3+
In this directory are tests for code paths not covered by our spec or granular (de)serialization benchmarks.
4+
5+
## How to write your own
6+
7+
In `benchmarks.mjs` add a new test to an existing array or make a new array for a new subject area.
8+
Try to fit the name of the function into the format of: "subject area", "method or function" "test case that is being covered" (Ex. `objectid_isvalid_bestcase_false`).
9+
Make sure your test is added to the `benchmarks` export.
10+
11+
### Example
12+
13+
```js
14+
const ObjectId_isValid = [
15+
function objectid_isvalid_strlen() {
16+
BSON.ObjectId.isValid('a');
17+
},
18+
// ...
19+
];
20+
21+
export const benchmarks = [...ObjectId_isValid];
22+
```
23+
24+
## Output
25+
26+
The JSON emitted at the end of the benchmarks must follow our performance tracking format.
27+
28+
The JSON must be an array of "`Test`"s:
29+
30+
```ts
31+
type Metric = { name: string, value: number }
32+
type Test = {
33+
info: { test_name: string },
34+
metrics: Metric[]
35+
}
36+
```
37+
38+
The metric collected is always "ops_per_sec" so higher is better.

0 commit comments

Comments
 (0)