├── .github
└── workflows
│ ├── commit-message-validation.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .npmignore
├── .release-please-manifest.json
├── CHANGELOG.md
├── README.md
├── assets
├── README.md
├── logo-text-dark.svg
├── logo-text-light.svg
└── logo.svg
├── biome.json
├── doc
└── Plugins.md
├── examples
├── .gitignore
├── chart-report
│ └── node.js
├── create-uint32array
│ ├── node.js
│ └── node.js.log
├── crypto-verify
│ ├── node.js
│ ├── node.js.log
│ ├── private-key.pem
│ └── public-key.pem
├── csv-report
│ └── node.js
├── default-report
│ └── node.js
├── deleting-properties
│ ├── node.js
│ └── node.js.log
├── empty
│ ├── node.js
│ └── node.js.log
├── fs-read-async
│ ├── node.js
│ ├── node.js.log
│ ├── node.managed.js
│ ├── node.managed.js.log
│ └── sample-file.txt
├── fs-read-sync
│ ├── node.js
│ ├── node.js.log
│ └── sample-file.txt
├── html-report
│ ├── node.js
│ └── result.html
├── json-report
│ └── node.js
├── plugins
│ ├── all.js
│ ├── all.js.log
│ ├── v8-get-opt-status.js
│ ├── v8-get-opt-status.js.log
│ ├── v8-never-optimize.js
│ ├── v8-never-optimize.js.log
│ ├── v8-optimize-next-call.js
│ └── v8-optimize-next-call.js.log
├── run.sh
├── string-replace
│ ├── node.js
│ └── node.js.log
├── string-searching
│ ├── node.js
│ └── node.js.log
├── time-mode.js
└── worker-threads
│ └── node.js
├── lib
├── clock.js
├── histogram.js
├── index.js
├── lifecycle.js
├── plugins.js
├── plugins
│ ├── memory.js
│ ├── v8-never-opt.js
│ ├── v8-opt.js
│ └── v8-print-status.js
├── report.js
├── reporter
│ ├── chart.js
│ ├── csv.js
│ ├── html.js
│ ├── json.js
│ ├── template.html
│ └── text.js
├── utils
│ └── styleText.js
├── validators.js
└── worker-runner.js
├── package.json
├── release-please-config.json
└── test
├── async.js
├── basic.js
├── env.js
├── fixtures
├── bench.js
├── copy.js
└── opt-managed.js
├── managed.js
├── plugin-api-doc.js
├── plugins.js
├── reporter.js
├── time-mode.js
└── worker.js
/.github/workflows/commit-message-validation.yml:
--------------------------------------------------------------------------------
1 | name: Commit Message Validation
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | validate:
8 | name: Validate
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | with:
13 | fetch-depth: 0
14 |
15 | - uses: webiny/action-conventional-commits@v1.3.0
16 | with:
17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | permissions:
9 | contents: write
10 | pull-requests: write
11 | id-token: write
12 |
13 | jobs:
14 | release-please:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout Code
18 | uses: actions/checkout@v4
19 |
20 | - name: Release Please
21 | uses: google-github-actions/release-please-action@v4
22 | id: release
23 |
24 | - name: Use Node 22.x
25 | uses: actions/setup-node@v4
26 | if: ${{ steps.release.outputs.release_created }}
27 | with:
28 | node-version: 22
29 | registry-url: 'https://registry.npmjs.org'
30 |
31 | - name: Install Deps
32 | run: npm install
33 | if: ${{ steps.release.outputs.release_created }}
34 |
35 | - name: NPM Publish
36 | run: npm publish --provenance --access public
37 | if: ${{ steps.release.outputs.release_created }}
38 | env:
39 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
40 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | node-version: [18, 20, 22, 23]
16 |
17 | env:
18 | CI: true
19 |
20 | steps:
21 | - name: Checkout code
22 | uses: actions/checkout@v4
23 |
24 | - name: Set up Node.js
25 | uses: actions/setup-node@v4
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 |
29 | - name: Install dependencies
30 | run: npm install
31 |
32 | - name: Run linter
33 | run: npm run lint:ci
34 |
35 | - name: Run tests
36 | run: npm test
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | tags
2 | .idea
3 | node_modules/
4 | package-lock.json
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # We doesn't wan to publish the logo and other
2 | assets
3 |
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | ".": "0.7.0"
3 | }
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [0.7.0](https://github.com/RafaelGSS/bench-node/compare/v0.6.0...v0.7.0) (2025-05-15)
4 |
5 |
6 | ### Features
7 |
8 | * add opsSecPerRun result ([#74](https://github.com/RafaelGSS/bench-node/issues/74)) ([34ad9e9](https://github.com/RafaelGSS/bench-node/commit/34ad9e95b23d92d80442955569f04efdafc97655))
9 |
10 | ## [0.6.0](https://github.com/RafaelGSS/bench-node/compare/v0.5.4...v0.6.0) (2025-04-30)
11 |
12 |
13 | ### Features
14 |
15 | * add time mode benchmark ([#71](https://github.com/RafaelGSS/bench-node/issues/71)) ([9d72044](https://github.com/RafaelGSS/bench-node/commit/9d72044a13a9fd93ae5f1b97fc26ff4a7db7d5f1))
16 |
17 |
18 | ### Documentation
19 |
20 | * add JSDoc to functions ([#69](https://github.com/RafaelGSS/bench-node/issues/69)) ([e5c6695](https://github.com/RafaelGSS/bench-node/commit/e5c66957ec737f1a8f5ecc4f204e8032daf50aca))
21 |
22 |
23 | ### Miscellaneous Chores
24 |
25 | * start issuing semver-minor ([3a261ae](https://github.com/RafaelGSS/bench-node/commit/3a261ae94ea029e8aed68153968570eb7c38a2c1))
26 |
27 | ## [0.5.4](https://github.com/RafaelGSS/bench-node/compare/v0.5.3...v0.5.4) (2025-03-13)
28 |
29 |
30 | ### Features
31 |
32 | * expose histogram sample data to reporters via sampleData property ([#67](https://github.com/RafaelGSS/bench-node/issues/67)) ([833bec1](https://github.com/RafaelGSS/bench-node/commit/833bec16ae8167785e148ec939fc6211a0662822))
33 |
34 | ## [0.5.3](https://github.com/RafaelGSS/bench-node/compare/v0.5.2...v0.5.3) (2025-02-24)
35 |
36 |
37 | ### Features
38 |
39 | * add min samples as param ([#65](https://github.com/RafaelGSS/bench-node/issues/65)) ([9c6812e](https://github.com/RafaelGSS/bench-node/commit/9c6812e1124f44d95f8d086cba01b5302ec5187e))
40 |
41 |
42 | ### Miscellaneous Chores
43 |
44 | * **readme:** clean + update ([#61](https://github.com/RafaelGSS/bench-node/issues/61)) ([b5e1e8b](https://github.com/RafaelGSS/bench-node/commit/b5e1e8bb507b9f8b017a91e8c814fb1046908042))
45 |
46 | ## [0.5.2](https://github.com/RafaelGSS/bench-node/compare/v0.5.1...v0.5.2) (2025-01-27)
47 |
48 |
49 | ### Features
50 |
51 | * **chartReport:** include node-v ([#53](https://github.com/RafaelGSS/bench-node/issues/53)) ([695842e](https://github.com/RafaelGSS/bench-node/commit/695842eb58c8b6106598a4cef26fe44a552065c6))
52 | * improve htmlReport UX ([#60](https://github.com/RafaelGSS/bench-node/issues/60)) ([79ce533](https://github.com/RafaelGSS/bench-node/commit/79ce5335e95d531b90c4e26fde983ec1c6de9502))
53 |
54 |
55 | ### Documentation
56 |
57 | * **readme:** add badges and links ([f85f809](https://github.com/RafaelGSS/bench-node/commit/f85f809aa0f85987d3b01ef9737ed0a855e46741))
58 |
59 |
60 | ### Miscellaneous Chores
61 |
62 | * **"branding":** add logo ([#58](https://github.com/RafaelGSS/bench-node/issues/58)) ([3eedd06](https://github.com/RafaelGSS/bench-node/commit/3eedd064791f5804810545db247a509aea618234))
63 |
64 | ## [0.5.1](https://github.com/RafaelGSS/bench-node/compare/v0.5.0...v0.5.1) (2025-01-19)
65 |
66 |
67 | ### Features
68 |
69 | * create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))
70 | * **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))
71 | * use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))
72 |
73 |
74 | ### Bug Fixes
75 |
76 | * ignore package.json due to release-please ([7c95b0a](https://github.com/RafaelGSS/bench-node/commit/7c95b0a4fd41aa81576503ac9444a775ed498eda))
77 | * lint package.json ([549f6ca](https://github.com/RafaelGSS/bench-node/commit/549f6ca574f4a30915e86dff9cd073b3d90def1e))
78 |
79 |
80 | ### Miscellaneous Chores
81 |
82 | * **main:** release 0.5.1 ([#44](https://github.com/RafaelGSS/bench-node/issues/44)) ([4e51324](https://github.com/RafaelGSS/bench-node/commit/4e51324ea129c3607229aaec3b8d22ef221d0e7d))
83 | * **main:** release 0.5.2 ([#45](https://github.com/RafaelGSS/bench-node/issues/45)) ([baf2014](https://github.com/RafaelGSS/bench-node/commit/baf20147c1f09f3e50491845e536c590db0d8aa5))
84 | * **main:** release 0.5.3 ([4757182](https://github.com/RafaelGSS/bench-node/commit/4757182c015cfbd769ebf3969c8269120271e5b3))
85 |
86 |
87 | ### Tests
88 |
89 | * add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))
90 |
91 |
92 | ### Continuous Integration
93 |
94 | * **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))
95 |
96 | ## [0.5.3](https://github.com/RafaelGSS/bench-node/compare/v0.5.2...v0.5.3) (2025-01-16)
97 |
98 |
99 | ### Features
100 |
101 | * add benchmark repetition ([#27](https://github.com/RafaelGSS/bench-node/issues/27)) ([d65e8aa](https://github.com/RafaelGSS/bench-node/commit/d65e8aab609b882a32331a48bb60fb81ee2db24a))
102 | * add enough context to plugin methods to figure out bench task name ([c16cf34](https://github.com/RafaelGSS/bench-node/commit/c16cf340699cf198ca10146f30c158697afff908))
103 | * add html reporter ([a69fdfb](https://github.com/RafaelGSS/bench-node/commit/a69fdfb7415eeb4645e7116be125ccf876d00ebc))
104 | * add JSONReporter ([7b51c16](https://github.com/RafaelGSS/bench-node/commit/7b51c16db1446b4a2c921c2548e14462197f4779))
105 | * code and examples ([#1](https://github.com/RafaelGSS/bench-node/issues/1)) ([503b573](https://github.com/RafaelGSS/bench-node/commit/503b573a67cf9245383da949274b30412c366084))
106 | * create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))
107 | * **enrichers:** added V8NeverOptimizeEnricher and V8OptimizeOnNextCallEnricher ([16e842e](https://github.com/RafaelGSS/bench-node/commit/16e842eb5dad9703fd009979f68b4f71c98436b2))
108 | * initial commit ([ee2d46f](https://github.com/RafaelGSS/bench-node/commit/ee2d46fc446a481c7bca731639759e4b7529c405))
109 | * **memory-enricher:** added support to report memory heap statistics ([441b3ad](https://github.com/RafaelGSS/bench-node/commit/441b3adfee5d92cdd32cb0d4bfd5e7b49d14c2af))
110 | * **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))
111 | * use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))
112 |
113 |
114 | ### Bug Fixes
115 |
116 | * handle htmlReport when bench suite is uppercase ([1685144](https://github.com/RafaelGSS/bench-node/commit/16851442e3fe3a97f8e6fc5c98993c77162dc4bc))
117 | * **lifecycle:** missing imports ([08c6064](https://github.com/RafaelGSS/bench-node/commit/08c60646736ee1236cb371143594e337b1d5f502))
118 | * typo ([d2a36ae](https://github.com/RafaelGSS/bench-node/commit/d2a36aec5dae24b9a4e95e4f055109a73d3b6bbc))
119 |
120 |
121 | ### Miscellaneous Chores
122 |
123 | * add exec permission to run.sh ([5d0f4ef](https://github.com/RafaelGSS/bench-node/commit/5d0f4ef72849189472b6700ddf7e56376eea61a2))
124 | * add node_modules to ignore ([478f24c](https://github.com/RafaelGSS/bench-node/commit/478f24c3fb8cd896e28e1c87e3212269fe9e31eb))
125 | * **examples:** added example benchmarks ([b4b50b2](https://github.com/RafaelGSS/bench-node/commit/b4b50b23def45698c854bf3bbe434d3f3a92567d))
126 | * **gitignore:** ignore .idea folder ([e9a13ae](https://github.com/RafaelGSS/bench-node/commit/e9a13ae640fd2ec2d7e714c0c6c9240f4ab1c628))
127 | * **main:** release 0.5.1 ([#44](https://github.com/RafaelGSS/bench-node/issues/44)) ([4e51324](https://github.com/RafaelGSS/bench-node/commit/4e51324ea129c3607229aaec3b8d22ef221d0e7d))
128 | * **main:** release 0.5.2 ([#45](https://github.com/RafaelGSS/bench-node/issues/45)) ([baf2014](https://github.com/RafaelGSS/bench-node/commit/baf20147c1f09f3e50491845e536c590db0d8aa5))
129 | * rename to bench-node ([2f15705](https://github.com/RafaelGSS/bench-node/commit/2f157051e3b1988ac3a8094e0fc7e4daee267a48))
130 | * rename to nodebenchmark ([9806a31](https://github.com/RafaelGSS/bench-node/commit/9806a31c819073d705bd59c29adc35e808e61d6c))
131 | * **run:** added script to run all examples ([6733730](https://github.com/RafaelGSS/bench-node/commit/6733730de9fa83a0b6ee7f243b1c3c0576f6f4ad))
132 | * update rafaelgss email ([a5ec544](https://github.com/RafaelGSS/bench-node/commit/a5ec5445a777c9db12181cae70cd47def0ac56c2))
133 |
134 |
135 | ### Code Refactoring
136 |
137 | * **lib:** from esm to commonjs ([f25d0e4](https://github.com/RafaelGSS/bench-node/commit/f25d0e40c293a07fe865f09f9bd6693b3152e5b0))
138 | * **lib:** make the code usable outside/inside node core ([c60c80e](https://github.com/RafaelGSS/bench-node/commit/c60c80e8fd6cad52f5275419252e313e03767893))
139 | * **validators:** added missing validators on clock ([478fc7e](https://github.com/RafaelGSS/bench-node/commit/478fc7e3456c84797cd718b2c7eeb7e876bad2bc))
140 |
141 |
142 | ### Tests
143 |
144 | * add a test documenting the plugin signature and lifecycle ([fd379d6](https://github.com/RafaelGSS/bench-node/commit/fd379d6ed51317504255eb78a24e33db21e0b3a7))
145 | * add basic test suite ([8349ee0](https://github.com/RafaelGSS/bench-node/commit/8349ee0f96236646776fd12843c01d1d9c806b42))
146 | * add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))
147 | * add scenario for optimized managed benchmark ([74c4db1](https://github.com/RafaelGSS/bench-node/commit/74c4db1046857f9af57c0c54cc5bf801d0195339))
148 | * add test case for copy function ([ddf6dc7](https://github.com/RafaelGSS/bench-node/commit/ddf6dc7b4e7a695f6bff5766788b4b0d5beec527))
149 | * fix the plugin api test ([be8ec69](https://github.com/RafaelGSS/bench-node/commit/be8ec69ff9481ce55b8e49f5732e01a468f6b5de))
150 | * include TODO test for managed and async ([15ff469](https://github.com/RafaelGSS/bench-node/commit/15ff46924bb969d724d1f92f5611a3f4385f0d47))
151 | * increase percentage diff on CI ([fa57188](https://github.com/RafaelGSS/bench-node/commit/fa571883f30fd7033a12e05f291fe12bf4816152))
152 |
153 |
154 | ### Build System
155 |
156 | * move run.sh to examples folder ([08ac769](https://github.com/RafaelGSS/bench-node/commit/08ac7699032a32f3a04a252cc48ee1514fd734bd))
157 |
158 |
159 | ### Continuous Integration
160 |
161 | * **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))
162 |
163 | ## [0.5.2](https://github.com/RafaelGSS/bench-node/compare/v0.5.1...v0.5.2) (2025-01-16)
164 |
165 |
166 | ### Features
167 |
168 | * add benchmark repetition ([#27](https://github.com/RafaelGSS/bench-node/issues/27)) ([d65e8aa](https://github.com/RafaelGSS/bench-node/commit/d65e8aab609b882a32331a48bb60fb81ee2db24a))
169 | * add enough context to plugin methods to figure out bench task name ([c16cf34](https://github.com/RafaelGSS/bench-node/commit/c16cf340699cf198ca10146f30c158697afff908))
170 | * add html reporter ([a69fdfb](https://github.com/RafaelGSS/bench-node/commit/a69fdfb7415eeb4645e7116be125ccf876d00ebc))
171 | * add JSONReporter ([7b51c16](https://github.com/RafaelGSS/bench-node/commit/7b51c16db1446b4a2c921c2548e14462197f4779))
172 | * code and examples ([#1](https://github.com/RafaelGSS/bench-node/issues/1)) ([503b573](https://github.com/RafaelGSS/bench-node/commit/503b573a67cf9245383da949274b30412c366084))
173 | * create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))
174 | * **enrichers:** added V8NeverOptimizeEnricher and V8OptimizeOnNextCallEnricher ([16e842e](https://github.com/RafaelGSS/bench-node/commit/16e842eb5dad9703fd009979f68b4f71c98436b2))
175 | * initial commit ([ee2d46f](https://github.com/RafaelGSS/bench-node/commit/ee2d46fc446a481c7bca731639759e4b7529c405))
176 | * **memory-enricher:** added support to report memory heap statistics ([441b3ad](https://github.com/RafaelGSS/bench-node/commit/441b3adfee5d92cdd32cb0d4bfd5e7b49d14c2af))
177 | * **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))
178 | * use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))
179 |
180 |
181 | ### Bug Fixes
182 |
183 | * handle htmlReport when bench suite is uppercase ([1685144](https://github.com/RafaelGSS/bench-node/commit/16851442e3fe3a97f8e6fc5c98993c77162dc4bc))
184 | * **lifecycle:** missing imports ([08c6064](https://github.com/RafaelGSS/bench-node/commit/08c60646736ee1236cb371143594e337b1d5f502))
185 | * typo ([d2a36ae](https://github.com/RafaelGSS/bench-node/commit/d2a36aec5dae24b9a4e95e4f055109a73d3b6bbc))
186 |
187 |
188 | ### Miscellaneous Chores
189 |
190 | * add exec permission to run.sh ([5d0f4ef](https://github.com/RafaelGSS/bench-node/commit/5d0f4ef72849189472b6700ddf7e56376eea61a2))
191 | * add node_modules to ignore ([478f24c](https://github.com/RafaelGSS/bench-node/commit/478f24c3fb8cd896e28e1c87e3212269fe9e31eb))
192 | * **examples:** added example benchmarks ([b4b50b2](https://github.com/RafaelGSS/bench-node/commit/b4b50b23def45698c854bf3bbe434d3f3a92567d))
193 | * **gitignore:** ignore .idea folder ([e9a13ae](https://github.com/RafaelGSS/bench-node/commit/e9a13ae640fd2ec2d7e714c0c6c9240f4ab1c628))
194 | * **main:** release 0.5.1 ([#44](https://github.com/RafaelGSS/bench-node/issues/44)) ([4e51324](https://github.com/RafaelGSS/bench-node/commit/4e51324ea129c3607229aaec3b8d22ef221d0e7d))
195 | * rename to bench-node ([2f15705](https://github.com/RafaelGSS/bench-node/commit/2f157051e3b1988ac3a8094e0fc7e4daee267a48))
196 | * rename to nodebenchmark ([9806a31](https://github.com/RafaelGSS/bench-node/commit/9806a31c819073d705bd59c29adc35e808e61d6c))
197 | * **run:** added script to run all examples ([6733730](https://github.com/RafaelGSS/bench-node/commit/6733730de9fa83a0b6ee7f243b1c3c0576f6f4ad))
198 | * update rafaelgss email ([a5ec544](https://github.com/RafaelGSS/bench-node/commit/a5ec5445a777c9db12181cae70cd47def0ac56c2))
199 |
200 |
201 | ### Code Refactoring
202 |
203 | * **lib:** from esm to commonjs ([f25d0e4](https://github.com/RafaelGSS/bench-node/commit/f25d0e40c293a07fe865f09f9bd6693b3152e5b0))
204 | * **lib:** make the code usable outside/inside node core ([c60c80e](https://github.com/RafaelGSS/bench-node/commit/c60c80e8fd6cad52f5275419252e313e03767893))
205 | * **validators:** added missing validators on clock ([478fc7e](https://github.com/RafaelGSS/bench-node/commit/478fc7e3456c84797cd718b2c7eeb7e876bad2bc))
206 |
207 |
208 | ### Tests
209 |
210 | * add a test documenting the plugin signature and lifecycle ([fd379d6](https://github.com/RafaelGSS/bench-node/commit/fd379d6ed51317504255eb78a24e33db21e0b3a7))
211 | * add basic test suite ([8349ee0](https://github.com/RafaelGSS/bench-node/commit/8349ee0f96236646776fd12843c01d1d9c806b42))
212 | * add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))
213 | * add scenario for optimized managed benchmark ([74c4db1](https://github.com/RafaelGSS/bench-node/commit/74c4db1046857f9af57c0c54cc5bf801d0195339))
214 | * add test case for copy function ([ddf6dc7](https://github.com/RafaelGSS/bench-node/commit/ddf6dc7b4e7a695f6bff5766788b4b0d5beec527))
215 | * fix the plugin api test ([be8ec69](https://github.com/RafaelGSS/bench-node/commit/be8ec69ff9481ce55b8e49f5732e01a468f6b5de))
216 | * include TODO test for managed and async ([15ff469](https://github.com/RafaelGSS/bench-node/commit/15ff46924bb969d724d1f92f5611a3f4385f0d47))
217 | * increase percentage diff on CI ([fa57188](https://github.com/RafaelGSS/bench-node/commit/fa571883f30fd7033a12e05f291fe12bf4816152))
218 |
219 |
220 | ### Build System
221 |
222 | * move run.sh to examples folder ([08ac769](https://github.com/RafaelGSS/bench-node/commit/08ac7699032a32f3a04a252cc48ee1514fd734bd))
223 |
224 |
225 | ### Continuous Integration
226 |
227 | * **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))
228 |
229 | ## [0.5.1](https://github.com/RafaelGSS/bench-node/compare/v0.5.0...v0.5.1) (2025-01-14)
230 |
231 |
232 | ### Features
233 |
234 | * create csv reporter ([#38](https://github.com/RafaelGSS/bench-node/issues/38)) ([11be8e5](https://github.com/RafaelGSS/bench-node/commit/11be8e52e8cbb5e17f378a8462dd1c4bf7b35351))
235 | * **reporter:** polish chart output ([#40](https://github.com/RafaelGSS/bench-node/issues/40)) ([91082b6](https://github.com/RafaelGSS/bench-node/commit/91082b6441cfa6ba7b195d7386d493d689e29454))
236 | * use biome linter ([#34](https://github.com/RafaelGSS/bench-node/issues/34)) ([11c1108](https://github.com/RafaelGSS/bench-node/commit/11c11088e12d2b77547389eb0a5055ad3ff11427))
237 |
238 |
239 | ### Tests
240 |
241 | * add managed basic tests ([#36](https://github.com/RafaelGSS/bench-node/issues/36)) ([c491a32](https://github.com/RafaelGSS/bench-node/commit/c491a328329bc79b2ef8124856b162c8df0e8cfb))
242 |
243 |
244 | ### Continuous Integration
245 |
246 | * **release:** add support to release via release-please ([#42](https://github.com/RafaelGSS/bench-node/issues/42)) ([5263cc6](https://github.com/RafaelGSS/bench-node/commit/5263cc68a5c854a260b68e1f5b930496153ac7fb))
247 |
--------------------------------------------------------------------------------
/assets/README.md:
--------------------------------------------------------------------------------
1 | # "Branding"
2 |
3 | ## Logo
4 |
5 | | Logo | Description |
6 | | --- | --- |
7 | |  | simple logo for readme |
8 | |  | logo with text for light backgrounds |
9 | |  | logo with text for dark backgrounds |
10 |
11 | ## Other
12 |
13 | - **Font**: [Geist](https://fonts.google.com/specimen/Geist)
14 | - **Light Green**: `#84BA64`
15 | - **Dark Green**: `#2C682C`
16 |
17 | > The design of the assets was created by [@AugustinMauroy](https://github.com/AugustinMauroy).
18 |
--------------------------------------------------------------------------------
/assets/logo-text-dark.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/assets/logo-text-light.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
3 | "organizeImports": {
4 | "enabled": true
5 | },
6 | "linter": {
7 | "enabled": true,
8 | "rules": {
9 | "recommended": true,
10 | "style": {
11 | "noParameterAssign": "off",
12 | "noUselessElse": "off"
13 | }
14 | }
15 | },
16 | "formatter": {
17 | "enabled": true
18 | },
19 | "files": {
20 | "ignore": ["examples/*", "test/plugin-api-doc.js", "package.json"]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/doc/Plugins.md:
--------------------------------------------------------------------------------
1 | # Plugins
2 |
3 | The benchmark module supports a flexible plugin system that
4 | allows you to extend its functionality by adding custom plugins.
5 | This documentation explains how to create, validate, and use
6 | plugins within the benchmarking framework.
7 |
8 | [V8NeverOptimizePlugin](#class-v8neveroptimizeplugin) is enabled by default.
9 |
10 | To observe how a plugin is used, see the `plugin-api-doc.js` file in tests and explore its results.
11 |
12 | ## Structure
13 |
14 | Each plugin is expected to follow a specific structure with required methods
15 | for integration into the benchmark module. The plugins are required to define
16 | the following methods:
17 |
18 | * `isSupported()`: This method checks if the plugin can run in the
19 | current environment. If the plugin uses features specific to certain
20 | environments (e.g., V8 engine features), it should return `true` if those
21 | features are available and `false` otherwise.
22 |
23 | * `toString()`: This method should return a string representation of the plugin.
24 | It’s used for logging and error messages.
25 |
26 | In addition to these required methods, plugins can optionally define other
27 | methods based on their functionality, such as `beforeClockTemplate()`,
28 | `afterClockTemplate()`, `onCompleteBenchmark()`, and more.
29 |
30 | ## Plugin Methods
31 |
32 | ### `isSupported()` (required)
33 |
34 | This method checks if the plugin's functionality is available in the
35 | current environment. For instance, if a plugin uses specific V8 engine commands,
36 | this method ensures the environment supports them.
37 |
38 | ### `beforeClockTemplate(varNames)`
39 |
40 | * `varNames` {Object}
41 | * `bench` {string} - Name for the benchmark variable.
42 | * `context` {string} - Name for the context variable.
43 | * `timer` {string} - Name for the timer variable.
44 | * `awaitOrEmpty` {string} - A string with `await` or empty string (`''`).
45 |
46 | Some plugins need to modify or prepare the code before the benchmark starts.
47 | The `beforeClockTemplate()` method allows you to inject code before the timing
48 | process begins.
49 |
50 | This method must return an array where:
51 |
52 | * The first element is a string representing the JavaScript code to be executed
53 | before the benchmark function.
54 |
55 | * The second element (optional) is a string representing a function that will
56 | wrap the benchmark function. This wrapper is used to customize how the
57 | benchmark function is called during execution.
58 |
59 | The wrapped function provides a powerful way to manipulate how the benchmark
60 | is run without directly modifying the benchmark logic.
61 |
62 | ```js
63 | beforeClockTemplate() {
64 | let code = '';
65 |
66 | code += `
67 | function DoNotOptimize(x) {}
68 | // Prevent DoNotOptimize from optimizing or being inlined.
69 | %NeverOptimizeFunction(DoNotOptimize);
70 | `
71 | return [code, 'DoNotOptimize'];
72 | }
73 | ```
74 |
75 | In this example, the plugin injects the `DoNotOptimize` function and also
76 | provides it as a wrapper for the benchmark function.
77 |
78 | ### `afterClockTemplate(varNames)`
79 |
80 | * `varNames` {Object}
81 | * `bench` {string} - Name for the benchmark variable.
82 | * `context` {string} - Name for the context variable.
83 | * `timer` {string} - Name for the timer variable.
84 | * `awaitOrEmpty` {string} - A string with `await` or empty string (`''`).
85 |
86 | After the benchmark runs, this method can inject code to gather performance data
87 | or reset configurations. It must return an array where:
88 |
89 | * The first element is a string containing the JavaScript code to be executed
90 | after the benchmark finishes.
91 |
92 | Unlike `beforeClockTemplate`, `afterClockTemplate` does not support a second
93 | element in the returned array, as it only runs cleanup or data collection code
94 | after the benchmark is executed.
95 |
96 | ### `onCompleteBenchmark(result)`
97 |
98 | * `result` {Object}
99 | * `duration` {number} - Benchmark duration
100 | * `count` {number} - Number of iterations
101 | * `context` {Object} - A object used to store results after the benchmark clock
102 |
103 | This method is called when the benchmark completes. Plugins can collect and
104 | process data from the benchmark results in this step.
105 |
106 | ### `toString()` (required)
107 |
108 | This method returns a string identifier for the plugin, typically the plugin’s
109 | name. It is used in error messages and logging.
110 |
111 | ## Example Plugins
112 |
113 | Here are examples of plugins that follow the required structure and functionality.
114 |
115 | ```js
116 | class V8OptimizeOnNextCallPlugin {
117 | isSupported() {
118 | try {
119 | new Function(`%OptimizeFunctionOnNextCall(() => {})`)();
120 | return true;
121 | } catch (e) {
122 | return false;
123 | }
124 | }
125 |
126 | beforeClockTemplate({ awaitOrEmpty, bench }) {
127 | let code = '';
128 |
129 | code += `%OptimizeFunctionOnNextCall(${ bench }.fn);\n`;
130 | code += `${ awaitOrEmpty }${ bench }.fn();\n`;
131 | code += `${ awaitOrEmpty }${ bench }.fn();\n`;
132 |
133 | return [code];
134 | }
135 |
136 | toString() {
137 | return 'V8OptimizeOnNextCallPlugin';
138 | }
139 | }
140 | ```
141 |
142 | ## Official Plugins
143 |
144 | This is a list of official plugins that can be fetched when requiring
145 | `bench-node` module.
146 |
147 | ```js
148 | const { V8OptimizeOnNextCallPlugin, Suite } = require('bench-node');
149 | const suite = new Suite({
150 | plugins: [new V8OptimizeOnNextCallPlugin()],
151 | })
152 | ```
153 |
154 | ### Class: `V8OptimizeOnNextCallPlugin`
155 |
156 | The `V8OptimizeOnNextCallPlugin` triggers the V8 engine to optimize the
157 | function before it is called. This can improve performance in repeated
158 | benchmarks.
159 |
160 | ### Class: `V8NeverOptimizePlugin`
161 |
162 | The `V8NeverOptimizePlugin` prevents the V8 engine from optimizing or inlining
163 | a function, useful when you want to benchmark functions without any
164 | optimization.
165 |
166 | ### Class: `V8GetOptimizationStatus`
167 |
168 | The `V8GetOptimizationStatus` plugin collects the V8 engine's optimization
169 | status for a given function after it has been benchmarked.
170 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RafaelGSS/bench-node/29aa66c882723f74490a37b2b3f082b27a9c965a/examples/.gitignore
--------------------------------------------------------------------------------
/examples/chart-report/node.js:
--------------------------------------------------------------------------------
1 | const { Suite, chartReport } = require('../../lib');
2 | const assert = require('node:assert');
3 |
4 | const suite = new Suite({
5 | reporter: chartReport,
6 | });
7 |
8 | suite
9 | .add('single with matcher', function () {
10 | const pattern = /[123]/g
11 | const replacements = { 1: 'a', 2: 'b', 3: 'c' }
12 | const subject = '123123123123123123123123123123123123123123123123'
13 | const r = subject.replace(pattern, m => replacements[m])
14 | assert.ok(r);
15 | })
16 | .add('multiple replaces', function () {
17 | const subject = '123123123123123123123123123123123123123123123123'
18 | const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')
19 | assert.ok(r);
20 | })
21 | .run();
22 |
--------------------------------------------------------------------------------
/examples/create-uint32array/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 |
3 | const suite = new Suite();
4 |
5 | suite
6 | .add(`new Uint32Array(1024)`, function () {
7 | return new Uint32Array(1024);
8 | })
9 | .add(`new Uint32Array(1024) with 10 repetitions`, {repeatSuite: 10}, function () {
10 | return new Uint32Array(1024);
11 | })
12 | .add(`new Uint32Array(1024) with min samples of 50`, {minSamples: 50}, function() {
13 | return new Uint32Array(1024);
14 | })
15 | .add(`[Managed] new Uint32Array(1024)`, function (timer) {
16 | const assert = require('node:assert');
17 |
18 | let r;
19 |
20 | timer.start();
21 | for (let i = 0; i < timer.count; i++) {
22 | r = new Uint32Array(1024);
23 | }
24 | timer.end(timer.count);
25 |
26 | assert.ok(r);
27 | })
28 | .run();
29 |
--------------------------------------------------------------------------------
/examples/create-uint32array/node.js.log:
--------------------------------------------------------------------------------
1 | new Uint32Array(1024) x 2,930,763 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(332.08ns ... 355.52ns) p75=344.36ns p99=355.52ns
2 | [Managed] new Uint32Array(1024) x 2,934,651 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(335.94ns ... 357.76ns) p75=340.16ns p99=357.76ns
3 | ----------------------------------------------------------------------------
4 | new Uint32Array(1024) x 2,967,727 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(330.75ns ... 337.55ns) p75=334.98ns p99=337.55ns
5 | [Managed] new Uint32Array(1024) x 3,029,150 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(326.51ns ... 341.28ns) p75=331.01ns p99=341.28ns
6 | ----------------------------------------------------------------------------
7 | new Uint32Array(1024) x 2,920,851 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(332.88ns ... 363.08ns) p75=348.19ns p99=363.08ns
8 | [Managed] new Uint32Array(1024) x 3,032,256 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(327.13ns ... 343.75ns) p75=329.19ns p99=343.75ns
9 | ----------------------------------------------------------------------------
10 |
--------------------------------------------------------------------------------
/examples/crypto-verify/node.js:
--------------------------------------------------------------------------------
1 | const crypto = require('node:crypto');
2 | const { readFileSync } = require('fs');
3 | const { Suite } = require('../../lib');
4 |
5 | const rsaPrivateKey = readFileSync(`${__dirname}/private-key.pem`, 'utf-8');
6 | const rsaPublicKey = readFileSync(`${__dirname}/public-key.pem`, 'utf-8');
7 |
8 | const thing = 'hello world';
9 | const algorithm = 'RSA-SHA256';
10 | const signature = crypto.createSign(algorithm).update(thing).sign(rsaPrivateKey, 'base64');
11 |
12 | const suite = new Suite();
13 |
14 | suite
15 | .add(`crypto.createVerify('${algorithm}')`, function () {
16 | var verifier = crypto.createVerify(algorithm);
17 | verifier.update(thing);
18 | verifier.verify(rsaPublicKey, signature, 'base64');
19 | })
20 | .add(`crypto.verify('${algorithm}')`, function () {
21 | crypto.verify(algorithm, thing, rsaPublicKey, Buffer.from(signature, 'base64'));
22 | })
23 | .add(`[Managed] crypto.createVerify('RSA-SHA256')`, function (timer) {
24 | const crypto = require('node:crypto');
25 | const { readFileSync } =require('node:fs');
26 | const assert = require('node:assert');
27 |
28 | const rsaPrivateKey = readFileSync(`${__dirname}/private-key.pem`, 'utf-8');
29 | const rsaPublicKey = readFileSync(`${__dirname}/public-key.pem`, 'utf-8');
30 |
31 | const thing = 'hello world'
32 | const signature = crypto.createSign('RSA-SHA256').update(thing).sign(rsaPrivateKey, 'base64')
33 |
34 | let verifier;
35 |
36 | timer.start();
37 | for (let i = 0; i < timer.count; i++) {
38 | verifier = crypto.createVerify('RSA-SHA256')
39 | verifier.update(thing)
40 | verifier.verify(rsaPublicKey, signature, 'base64')
41 | }
42 | timer.end(timer.count);
43 |
44 | assert.ok(verifier);
45 | })
46 | .add(`[Managed] crypto.verify('RSA-SHA256')`, function (timer) {
47 | const crypto = require('node:crypto');
48 | const { readFileSync } = require('node:fs');
49 | const assert = require('node:assert');
50 |
51 | const rsaPrivateKey = readFileSync(`${__dirname}/private-key.pem`, 'utf-8');
52 | const rsaPublicKey = readFileSync(`${__dirname}/public-key.pem`, 'utf-8');
53 |
54 | const thing = 'hello world'
55 | const signature = crypto.createSign('RSA-SHA256').update(thing).sign(rsaPrivateKey, 'base64')
56 |
57 | let r;
58 |
59 | timer.start();
60 | for (let i = 0; i < timer.count; i++) {
61 | r = crypto.verify('RSA-SHA256', thing, rsaPublicKey, Buffer.from(signature, 'base64'));
62 | }
63 | timer.end(timer.count);
64 |
65 | assert.ok(r);
66 | })
67 | .run();
68 |
--------------------------------------------------------------------------------
/examples/crypto-verify/node.js.log:
--------------------------------------------------------------------------------
1 | crypto.createVerify('RSA-SHA256') x 10,925 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(88.50us ... 93.51us) p75=91.54us p99=93.51us
2 | crypto.verify('RSA-SHA256') x 10,988 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(90.93us ... 91.84us) p75=91.58us p99=91.84us
3 | [Managed] crypto.createVerify('RSA-SHA256') x 10,520 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(88.44us ... 92.23us) p75=91.47us p99=92.23us
4 | [Managed] crypto.verify('RSA-SHA256') x 10,948 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(89.98us ... 92.36us) p75=91.49us p99=92.36us
5 | ----------------------------------------------------------------------------
6 | crypto.createVerify('RSA-SHA256') x 10,807 ops/sec (8 runs sampled) v8-never-optimize=true min..max=(91.66us ... 92.58us) p75=92.23us p99=92.58us
7 | crypto.verify('RSA-SHA256') x 10,964 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(88.95us ... 92.46us) p75=91.83us p99=92.46us
8 | [Managed] crypto.createVerify('RSA-SHA256') x 10,954 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(90.08us ... 92.88us) p75=91.72us p99=92.88us
9 | [Managed] crypto.verify('RSA-SHA256') x 10,990 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(90.31us ... 92.43us) p75=91.43us p99=92.43us
10 | ----------------------------------------------------------------------------
11 | crypto.createVerify('RSA-SHA256') x 10,890 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(87.88us ... 94.08us) p75=92.96us p99=94.08us
12 | crypto.verify('RSA-SHA256') x 10,906 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(91.03us ... 92.45us) p75=91.66us p99=92.45us
13 | [Managed] crypto.createVerify('RSA-SHA256') x 11,050 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(87.94us ... 92.09us) p75=91.88us p99=92.09us
14 | [Managed] crypto.verify('RSA-SHA256') x 10,990 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(90.29us ... 92.13us) p75=91.92us p99=92.13us
15 | ----------------------------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/examples/crypto-verify/private-key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEAz7QweYwr7puNtc8/6jf4pADXfY2/HVq7LgI7JXkAwW5HTEjU
3 | Rvu02SYC7n/ylr86Da4hcAEAFBC2baVh7J0EnLo6D4FA3yV4s5zj7tqL+p2CdniY
4 | MDEny+uxS/9eRuLf1RIYHjSVB6wOoU4lg9FtDOQA8W09ZDLvc3/4ZSwEf9O2J5Vm
5 | qcSGaPt35eIEBS0iIMiOUCGqbUcVMZkp4JO7I65HOK/g0Scvb1LLY6f4qDsThqyi
6 | ID/NXM5LtjnztRP+9dCLT9DdwC+LeU2te6vcEwC2bPYs3nUwkZL4qqIWO6GHaavn
7 | gFKSmbmc9muVAPmYW/ffMtznKZaWCp9Li0JGuQIDAQABAoIBAQC7OI7hYSpQgEKy
8 | eUgBlcY3/tI/SD/W8+v5QuWRl4rI0ODPsG44NbcEbbECzq4al/B6WFWnoh8x9waZ
9 | uxOTts1rgKnJRBb3jc1JCcijirfWhZgNthJojkZzF9bOzDds6iAc7Zxzza3wJnVh
10 | jRFfyqzji7oV5QQLh6YzlEyQ1aaQmOKTkCDV1UDetI8iItUvq3i8EwsFmtfvXjTl
11 | aymL/xSd3R6w3WDEd8PS5ZPDoFmkt2h/4IhOo6bVXLwWBWqcopRAvzGQrc806c2l
12 | jjePwk9fddIG+vIHc8DHqaC/WzMR6iBt3K7Q9Eyu4k97qeOxB5TIEVlhxg2DJON9
13 | DlGtInXpAoGBAPje3L0xL441i7kh7GqiWtXiDgxVwdPmN5p7n755MddSxvu8894h
14 | T9/LxwAVcpolXANgyRtrl5bu/gXgznKefmVr976BzG2DY33PESf5FnHZAmixWSfz
15 | VOdfVlz+Bk/BI+nVwmeT0+8NSHJ6TJ/9NOSzI9ctUceszIHG5/Gfc2wHAoGBANWn
16 | bHNT7aphFqyUMfWyc6+h8Radq/RVMrOGeD2wRRqUTeb2Ydbx2OwB+gcwnlbsQjfS
17 | BYAa3kn1z5K5kOigB9qu6x7zuM9SMReKIlMg3NpYVyKKj+JaNF3YArFa5TWy0B26
18 | wNJ0FiiZyPq4eP5hPwM+Bed4ytIRWa54P5GeRYc/AoGAWT8ijb4rvaW6G4Ps0jiy
19 | tmzAeO/v+FtgqUeX+6helUccEH6sPYZYrHrZPFB0ro6jNprow6qLzBacheMeZcAs
20 | t5ZGW80UUFmDvkQZdOpAgEdAM+cVf9wlIGvx/psiDEvI4zxC4P4ETH/I8TSmceFN
21 | rI4JVkrsPtza4ddAqkdyDtUCgYAaoMs7dHJikccpqy6u2JbihORvVSdhRF0VUuUZ
22 | iyaRsXokFwEKsQnAIF7xFnYljzyRiHN3C+I4hZJhTw9obsmLz9EuAmI+NJg5vtWY
23 | Vrgv3mK9w1c7dtKf/5QWVqXKk4asreHqWN2KIeCSnvs1eRlJZimGN9/PXqo2vHXv
24 | yDISMQKBgE22EhVB99rEsLXDCXOUHIFGpW46ZavQc4DLXg+uIQm3y0WWyc0oyNF8
25 | 9kr1luu5QI5PV9mWpSKa3SKmc74biyxhlUp/DYuMyuSXpdDegMAKheAHVXP9ToU/
26 | 5qhq4HBcISN3CwSAYcFcbqC3cB07T+IwX6NTEz4zel4gty8t/tsM
27 | -----END RSA PRIVATE KEY-----
--------------------------------------------------------------------------------
/examples/crypto-verify/public-key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz7QweYwr7puNtc8/6jf4
3 | pADXfY2/HVq7LgI7JXkAwW5HTEjURvu02SYC7n/ylr86Da4hcAEAFBC2baVh7J0E
4 | nLo6D4FA3yV4s5zj7tqL+p2CdniYMDEny+uxS/9eRuLf1RIYHjSVB6wOoU4lg9Ft
5 | DOQA8W09ZDLvc3/4ZSwEf9O2J5VmqcSGaPt35eIEBS0iIMiOUCGqbUcVMZkp4JO7
6 | I65HOK/g0Scvb1LLY6f4qDsThqyiID/NXM5LtjnztRP+9dCLT9DdwC+LeU2te6vc
7 | EwC2bPYs3nUwkZL4qqIWO6GHaavngFKSmbmc9muVAPmYW/ffMtznKZaWCp9Li0JG
8 | uQIDAQAB
9 | -----END PUBLIC KEY-----
--------------------------------------------------------------------------------
/examples/csv-report/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 | const assert = require('node:assert');
3 |
4 | const suite = new Suite();
5 |
6 | suite
7 | .add('single with matcher', function () {
8 | const pattern = /[123]/g
9 | const replacements = { 1: 'a', 2: 'b', 3: 'c' }
10 | const subject = '123123123123123123123123123123123123123123123123'
11 | const r = subject.replace(pattern, m => replacements[m])
12 | assert.ok(r);
13 | })
14 | .add('multiple replaces', function () {
15 | const subject = '123123123123123123123123123123123123123123123123'
16 | const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')
17 | assert.ok(r);
18 | })
19 | .run();
20 |
--------------------------------------------------------------------------------
/examples/default-report/node.js:
--------------------------------------------------------------------------------
1 | const { Suite, csvReport } = require('../../lib');
2 | const assert = require('node:assert');
3 |
4 | const suite = new Suite({
5 | reporter: csvReport,
6 | });
7 |
8 | suite
9 | .add('single with matcher', function () {
10 | const pattern = /[123]/g
11 | const replacements = { 1: 'a', 2: 'b', 3: 'c' }
12 | const subject = '123123123123123123123123123123123123123123123123'
13 | const r = subject.replace(pattern, m => replacements[m])
14 | assert.ok(r);
15 | })
16 | .add('multiple replaces', function () {
17 | const subject = '123123123123123123123123123123123123123123123123'
18 | const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')
19 | assert.ok(r);
20 | })
21 | .run();
22 |
--------------------------------------------------------------------------------
/examples/deleting-properties/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 |
3 | const suite = new Suite();
4 |
5 | const NullObject = function NullObject() { }
6 | NullObject.prototype = Object.create(null);
7 | %NeverOptimizeFunction(NullObject);
8 |
9 | suite
10 | .add('Using delete property', function () {
11 | const data = { x: 1, y: 2, z: 3 }
12 | delete data.y
13 |
14 | data.x
15 | data.y
16 | data.z
17 | })
18 | .add('Using delete property (proto: null)', function () {
19 | const data = { __proto__: null, x: 1, y: 2, z: 3 }
20 | delete data.y
21 |
22 | data.x
23 | data.y
24 | data.z
25 | })
26 | .add('Using delete property (cached proto: null)', function () {
27 | const data = new NullObject()
28 |
29 | data.x = 1
30 | data.y = 2
31 | data.z = 3
32 |
33 | delete data.y
34 |
35 | data.x
36 | data.y
37 | data.z
38 | })
39 | .add('Using undefined assignment', function () {
40 | const data = { x: 1, y: 2, z: 3 }
41 | data.y = undefined
42 |
43 | data.x
44 | data.y
45 | data.z
46 | })
47 | .add('Using undefined assignment (proto: null)', function () {
48 | const data = { __proto__: null, x: 1, y: 2, z: 3 }
49 | data.y = undefined
50 |
51 | data.x
52 | data.y
53 | data.z
54 | })
55 | .add('Using undefined property (cached proto: null)', function () {
56 | const data = new NullObject()
57 |
58 | data.x = 1
59 | data.y = 2
60 | data.z = 3
61 |
62 | data.y = undefined
63 |
64 | data.x
65 | data.y
66 | data.z
67 | })
68 | .add('[Managed] Using undefined property (cached proto: null)', function (t) {
69 | const NullObject = function () { }
70 | NullObject.prototype = Object.create(null)
71 |
72 | t.start();
73 | for (let i = 0; i < t.count; i++) {
74 | const data = new NullObject()
75 |
76 | data.x = 1
77 | data.y = 2
78 | data.z = 3
79 |
80 | data.y = undefined
81 |
82 | data.x
83 | data.y
84 | data.z
85 | }
86 | t.end(t.count);
87 | })
88 | .run();
89 |
--------------------------------------------------------------------------------
/examples/deleting-properties/node.js.log:
--------------------------------------------------------------------------------
1 | Using delete property x 7,763,866 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(128.11ns ... 129.99ns) p75=129.03ns p99=129.99ns
2 | Using delete property (proto: null) x 22,946,068 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(43.13ns ... 44.11ns) p75=43.74ns p99=44.11ns
3 | Using delete property (cached proto: null) x 7,331,951 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(135.64ns ... 137.39ns) p75=136.52ns p99=137.39ns
4 | Using undefined assignment x 133,449,849 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(7.45ns ... 7.56ns) p75=7.53ns p99=7.56ns
5 | Using undefined assignment (proto: null) x 25,432,632 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(38.72ns ... 40.06ns) p75=39.60ns p99=40.06ns
6 | Using undefined property (cached proto: null) x 58,871,059 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(16.92ns ... 17.12ns) p75=17.01ns p99=17.12ns
7 | [Managed] Using undefined property (cached proto: null) x 35,463,712 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(28.05ns ... 28.43ns) p75=28.25ns p99=28.43ns
8 | ----------------------------------------------------------------------------
9 | Using delete property x 6,669,856 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(149.14ns ... 151.02ns) p75=150.12ns p99=151.02ns
10 | Using delete property (proto: null) x 15,242,131 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(64.77ns ... 66.38ns) p75=66.25ns p99=66.38ns
11 | Using delete property (cached proto: null) x 5,920,843 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(167.87ns ... 170.33ns) p75=169.07ns p99=170.33ns
12 | Using undefined assignment x 78,647,245 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(12.62ns ... 12.83ns) p75=12.70ns p99=12.83ns
13 | Using undefined assignment (proto: null) x 16,278,910 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(60.79ns ... 62.25ns) p75=61.57ns p99=62.25ns
14 | Using undefined property (cached proto: null) x 27,262,884 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(36.54ns ... 36.65ns) p75=36.62ns p99=36.65ns
15 | [Managed] Using undefined property (cached proto: null) x 28,068,785 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(35.07ns ... 35.79ns) p75=35.67ns p99=35.79ns
16 | ----------------------------------------------------------------------------
17 | Using delete property x 7,632,095 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(129.63ns ... 132.85ns) p75=131.19ns p99=132.85ns
18 | Using delete property (proto: null) x 23,040,357 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(43.06ns ... 44.20ns) p75=43.56ns p99=44.20ns
19 | Using delete property (cached proto: null) x 7,222,103 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(137.05ns ... 139.88ns) p75=139.18ns p99=139.88ns
20 | Using undefined assignment x 133,133,677 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.49ns ... 7.53ns) p75=7.52ns p99=7.53ns
21 | Using undefined assignment (proto: null) x 25,217,884 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(39.24ns ... 40.48ns) p75=39.89ns p99=40.48ns
22 | Using undefined property (cached proto: null) x 58,899,972 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(16.94ns ... 17.03ns) p75=16.99ns p99=17.03ns
23 | [Managed] Using undefined property (cached proto: null) x 34,904,120 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(28.34ns ... 29.89ns) p75=28.84ns p99=29.89ns
24 | ----------------------------------------------------------------------------
25 |
--------------------------------------------------------------------------------
/examples/empty/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 |
3 | const suite = new Suite();
4 |
5 | suite
6 | .add(`empty`, function () {})
7 | .add(`empty async`, async function () {})
8 | .run();
9 |
--------------------------------------------------------------------------------
/examples/empty/node.js.log:
--------------------------------------------------------------------------------
1 | empty x 218,984,985 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(4.53ns ... 4.66ns) p75=4.58ns p99=4.66ns
2 | empty async x 20,898,403 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(47.46ns ... 48.57ns) p75=48.04ns p99=48.57ns
3 | ----------------------------------------------------------------------------
4 | empty x 220,571,877 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(4.49ns ... 4.61ns) p75=4.58ns p99=4.61ns
5 | empty async x 20,925,022 ops/sec (14 runs sampled) v8-never-optimize=true min..max=(37.43ns ... 48.60ns) p75=48.03ns p99=48.60ns
6 | ----------------------------------------------------------------------------
7 | empty x 218,923,690 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(4.52ns ... 4.64ns) p75=4.59ns p99=4.64ns
8 | empty async x 20,792,931 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(42.94ns ... 49.45ns) p75=48.54ns p99=49.45ns
9 | ----------------------------------------------------------------------------
10 |
--------------------------------------------------------------------------------
/examples/fs-read-async/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 | const { readFile } = require('node:fs/promises');
3 | const { resolve } = require('node:path');
4 |
5 | const suite = new Suite();
6 |
7 | const sampleFile = resolve(__dirname, 'sample-file.txt');
8 |
9 | suite
10 | .add('readFile', async function () {
11 | const r = await readFile(sampleFile);
12 | })
13 | .add('readFile utf-8', async function () {
14 | const r = await readFile(sampleFile, 'utf-8');
15 | })
16 | .add('[managed] readFile', async function (timer) {
17 | const { readFile } = require('node:fs/promises');
18 | const { resolve } = require('node:path');
19 | const assert = require('node:assert');
20 |
21 | const sampleFile = resolve(__dirname, 'sample-file.txt');
22 | let r;
23 |
24 | timer.start();
25 | for (let i = 0; i < timer.count; i++) {
26 | r = await readFile(sampleFile);
27 | }
28 | timer.end(timer.count);
29 |
30 | assert.ok(r);
31 | })
32 | .add('[managed] readFile utf-8', async function (timer) {
33 | const { readFile } = require('node:fs/promises');
34 | const { resolve } = require('node:path');
35 | const assert = require('node:assert');
36 |
37 | const sampleFile = resolve(__dirname, 'sample-file.txt');
38 | let r;
39 |
40 | timer.start();
41 | for (let i = 0; i < timer.count; i++) {
42 | r = await readFile(sampleFile, 'utf-8');
43 | }
44 | timer.end(timer.count);
45 |
46 | assert.ok(r);
47 | })
48 | .run();
49 |
--------------------------------------------------------------------------------
/examples/fs-read-async/node.js.log:
--------------------------------------------------------------------------------
1 | readFile x 23,167 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.59us ... 43.59us) p75=43.42us p99=43.59us
2 | readFile utf-8 x 23,021 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(43.14us ... 43.92us) p75=43.64us p99=43.92us
3 | [managed] readFile x 23,386 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.63us ... 43.04us) p75=42.89us p99=43.04us
4 | [managed] readFile utf-8 x 23,302 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.76us ... 43.30us) p75=43.01us p99=43.30us
5 | ----------------------------------------------------------------------------
6 | readFile x 23,177 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.70us ... 43.54us) p75=43.31us p99=43.54us
7 | readFile utf-8 x 23,155 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.34us ... 43.53us) p75=43.15us p99=43.53us
8 | [managed] readFile x 23,538 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.22us ... 42.82us) p75=42.59us p99=42.82us
9 | [managed] readFile utf-8 x 23,458 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.11us ... 43.20us) p75=42.86us p99=43.20us
10 | ----------------------------------------------------------------------------
11 | readFile x 23,235 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.73us ... 43.41us) p75=43.17us p99=43.41us
12 | readFile utf-8 x 23,130 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.90us ... 44.40us) p75=43.62us p99=44.40us
13 | [managed] readFile x 23,490 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.40us ... 42.89us) p75=42.66us p99=42.89us
14 | [managed] readFile utf-8 x 23,553 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(41.99us ... 42.81us) p75=42.58us p99=42.81us
15 | ----------------------------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/examples/fs-read-async/node.managed.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 |
3 | const suite = new Suite();
4 |
5 | suite
6 | .add('readFile', async function (timer) {
7 | const { readFile } = require('node:fs/promises');
8 | const { resolve } = require('node:path');
9 | const assert = require('node:assert');
10 |
11 | const sampleFile = resolve(__dirname, 'sample-file.txt');
12 | let r;
13 |
14 | timer.start();
15 | for (let i = 0; i < timer.count; i++) {
16 | r = await readFile(sampleFile);
17 | }
18 | timer.end(timer.count);
19 |
20 | assert.ok(r);
21 | })
22 | .add('readFile utf-8', async function (timer) {
23 | const { readFile } = require('node:fs/promises');
24 | const { resolve } = require('node:path');
25 | const assert = require('node:assert');
26 |
27 | const sampleFile = resolve(__dirname, 'sample-file.txt');
28 | let r;
29 |
30 | timer.start();
31 | for (let i = 0; i < timer.count; i++) {
32 | r = await readFile(sampleFile, 'utf-8');
33 | }
34 | timer.end(timer.count);
35 |
36 | assert.ok(r);
37 | })
38 | .run();
39 |
--------------------------------------------------------------------------------
/examples/fs-read-async/node.managed.js.log:
--------------------------------------------------------------------------------
1 | readFile x 23,066 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.59us ... 43.63us) p75=43.20us p99=43.63us
2 | readFile utf-8 x 23,334 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(42.21us ... 43.70us) p75=42.96us p99=43.70us
3 | ----------------------------------------------------------------------------
4 | readFile x 23,145 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.64us ... 43.18us) p75=43.04us p99=43.18us
5 | readFile utf-8 x 23,324 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.54us ... 43.80us) p75=42.93us p99=43.80us
6 | ----------------------------------------------------------------------------
7 | readFile x 23,124 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(42.36us ... 43.30us) p75=42.99us p99=43.30us
8 | readFile utf-8 x 23,277 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(41.50us ... 43.88us) p75=42.98us p99=43.88us
9 | ----------------------------------------------------------------------------
10 |
--------------------------------------------------------------------------------
/examples/fs-read-async/sample-file.txt:
--------------------------------------------------------------------------------
1 | hello
--------------------------------------------------------------------------------
/examples/fs-read-sync/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 | const { readFileSync } = require('node:fs');
3 | const { resolve } = require('node:path');
4 |
5 | const suite = new Suite();
6 |
7 | const sampleFile = resolve(__dirname, 'sample-file.txt');
8 |
9 | suite
10 | .add('readFileSync', function () {
11 | const r = readFileSync(sampleFile);
12 | })
13 | .add('readFileSync utf-8', function () {
14 | const r = readFileSync(sampleFile, 'utf-8');
15 | })
16 | .add('[Managed] readFileSync', function (timer) {
17 | const { readFileSync } = require('node:fs');
18 | const { resolve } = require('node:path');
19 | const assert = require('node:assert');
20 |
21 | const sampleFile = resolve(__dirname, 'sample-file.txt');
22 | let r;
23 |
24 | timer.start();
25 | for (let i = 0; i < timer.count; i++) {
26 | r = readFileSync(sampleFile);
27 | }
28 | timer.end(timer.count);
29 |
30 | assert.ok(r);
31 | })
32 | .add('[Managed] readFileSync utf-8', function (timer) {
33 | const { readFileSync } = require('node:fs');
34 | const { resolve } = require('node:path');
35 | const assert = require('node:assert');
36 |
37 | const sampleFile = resolve(__dirname, 'sample-file.txt');
38 | let r;
39 |
40 | timer.start();
41 | for (let i = 0; i < timer.count; i++) {
42 | r = readFileSync(sampleFile, 'utf-8');
43 | }
44 | timer.end(timer.count);
45 |
46 | assert.ok(r);
47 | })
48 |
49 | .run();
50 |
--------------------------------------------------------------------------------
/examples/fs-read-sync/node.js.log:
--------------------------------------------------------------------------------
1 | readFileSync x 198,828 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(5.01us ... 5.06us) p75=5.04us p99=5.06us
2 | readFileSync utf-8 x 203,828 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(4.88us ... 4.94us) p75=4.92us p99=4.94us
3 | [Managed] readFileSync x 196,632 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(5.05us ... 5.08us) p75=5.07us p99=5.08us
4 | [Managed] readFileSync utf-8 x 203,265 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(4.89us ... 4.92us) p75=4.91us p99=4.92us
5 | ----------------------------------------------------------------------------
6 | readFileSync x 198,461 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(5.00us ... 5.05us) p75=5.02us p99=5.05us
7 | readFileSync utf-8 x 205,171 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(4.82us ... 4.86us) p75=4.86us p99=4.86us
8 | [Managed] readFileSync x 193,061 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(5.05us ... 5.48us) p75=5.30us p99=5.48us
9 | [Managed] readFileSync utf-8 x 204,046 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(4.86us ... 4.92us) p75=4.91us p99=4.92us
10 | ----------------------------------------------------------------------------
11 | readFileSync x 196,533 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(5.02us ... 5.20us) p75=5.10us p99=5.20us
12 | readFileSync utf-8 x 205,213 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(4.83us ... 4.89us) p75=4.86us p99=4.89us
13 | [Managed] readFileSync x 183,333 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(5.06us ... 6.46us) p75=5.28us p99=6.46us
14 | [Managed] readFileSync utf-8 x 201,593 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(4.90us ... 5.07us) p75=5.06us p99=5.07us
15 | ----------------------------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/examples/fs-read-sync/sample-file.txt:
--------------------------------------------------------------------------------
1 | hello
--------------------------------------------------------------------------------
/examples/html-report/node.js:
--------------------------------------------------------------------------------
1 | const { Suite, htmlReport } = require('../../lib');
2 | const assert = require('node:assert');
3 |
4 | const suite = new Suite({
5 | reporter: htmlReport,
6 | });
7 |
8 | suite
9 | .add('single with matcher', function () {
10 | const pattern = /[123]/g
11 | const replacements = { 1: 'a', 2: 'b', 3: 'c' }
12 | const subject = '123123123123123123123123123123123123123123123123'
13 | const r = subject.replace(pattern, m => replacements[m])
14 | assert.ok(r);
15 | })
16 | .add('Multiple replaces', function () {
17 | const subject = '123123123123123123123123123123123123123123123123'
18 | const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')
19 | assert.ok(r);
20 | })
21 | .run();
22 |
--------------------------------------------------------------------------------
/examples/html-report/result.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Benchmark Visualizer
7 |
100 |
101 |
102 |
103 |
Node.js version: v20.18.1
104 |
Platform: darwin arm64
105 |
CPU Cores: 8 vCPUs | 16.0GB Mem
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | single-with-matcher(660,788.4 ops/sec)
118 |
119 |
120 |
121 | Multiple-replaces(578,527 ops/sec)
122 |
123 |
124 |
125 |
126 |
129 |
130 |
131 |
132 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/examples/json-report/node.js:
--------------------------------------------------------------------------------
1 | const { Suite, jsonReport } = require('../../lib');
2 | const assert = require('node:assert');
3 |
4 | const suite = new Suite({
5 | reporter: jsonReport,
6 | });
7 |
8 | suite
9 | .add('single with matcher', function () {
10 | const pattern = /[123]/g
11 | const replacements = { 1: 'a', 2: 'b', 3: 'c' }
12 | const subject = '123123123123123123123123123123123123123123123123'
13 | const r = subject.replace(pattern, m => replacements[m])
14 | assert.ok(r);
15 | })
16 | .add('Multiple replaces', function () {
17 | const subject = '123123123123123123123123123123123123123123123123'
18 | const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')
19 | assert.ok(r);
20 | })
21 | .run();
22 |
--------------------------------------------------------------------------------
/examples/plugins/all.js:
--------------------------------------------------------------------------------
1 | const {
2 | Suite,
3 | V8GetOptimizationStatus,
4 | V8NeverOptimizePlugin,
5 | V8OptimizeOnNextCallPlugin,
6 | } = require('../../lib');
7 |
8 | const suite = new Suite({
9 | plugins: [
10 | new V8GetOptimizationStatus(),
11 | new V8NeverOptimizePlugin(),
12 | // new V8OptimizeOnNextCallPlugin(),
13 | ],
14 | });
15 |
16 | suite
17 | .add(`new Uint32Array(1024)`, function () {
18 | return new Uint32Array(1024);
19 | })
20 | .add(`[Managed] new Uint32Array(1024)`, function (timer) {
21 | const assert = require('node:assert');
22 |
23 | let r;
24 |
25 | timer.start();
26 | for (let i = 0; i < timer.count; i++) {
27 | r = new Uint32Array(1024);
28 | }
29 | timer.end(timer.count);
30 |
31 | assert.ok(r);
32 | })
33 | .run();
34 |
--------------------------------------------------------------------------------
/examples/plugins/all.js.log:
--------------------------------------------------------------------------------
1 | new Uint32Array(1024) x 2,395,395 ops/sec (14 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" v8-never-optimize=true min..max=(302.80ns ... 435.20ns) p75=420.50ns p99=435.20ns
2 | [Managed] new Uint32Array(1024) x 1,878,469 ops/sec (12 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" v8-never-optimize=true min..max=(217.82ns ... 577.91ns) p75=430.46ns p99=577.91ns
3 | ----------------------------------------------------------------------------
4 | new Uint32Array(1024) x 2,282,799 ops/sec (13 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" v8-never-optimize=true min..max=(339.06ns ... 479.78ns) p75=449.29ns p99=479.78ns
5 | [Managed] new Uint32Array(1024) x 2,219,660 ops/sec (10 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" v8-never-optimize=true min..max=(383.40ns ... 548.65ns) p75=461.81ns p99=548.65ns
6 | ----------------------------------------------------------------------------
7 | new Uint32Array(1024) x 2,362,479 ops/sec (11 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" v8-never-optimize=true min..max=(384.64ns ... 456.35ns) p75=440.57ns p99=456.35ns
8 | [Managed] new Uint32Array(1024) x 2,272,352 ops/sec (10 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" v8-never-optimize=true min..max=(421.95ns ... 451.89ns) p75=443.17ns p99=451.89ns
9 | ----------------------------------------------------------------------------
10 |
--------------------------------------------------------------------------------
/examples/plugins/v8-get-opt-status.js:
--------------------------------------------------------------------------------
1 | const { Suite, V8GetOptimizationStatus } = require('../../lib');
2 |
3 | const suite = new Suite({
4 | plugins: [new V8GetOptimizationStatus()],
5 | });
6 |
7 | suite
8 | .add(`new Uint32Array(1024)`, function () {
9 | return new Uint32Array(1024);
10 | })
11 | .add(`[Managed] new Uint32Array(1024)`, function (timer) {
12 | const assert = require('node:assert');
13 |
14 | let r;
15 |
16 | timer.start();
17 | for (let i = 0; i < timer.count; i++) {
18 | r = new Uint32Array(1024);
19 | }
20 | timer.end(timer.count);
21 |
22 | assert.ok(r);
23 | })
24 | .run();
25 |
--------------------------------------------------------------------------------
/examples/plugins/v8-get-opt-status.js.log:
--------------------------------------------------------------------------------
1 | new Uint32Array(1024) x 2,353,498 ops/sec (10 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" min..max=(387.20ns ... 435.11ns) p75=426.25ns p99=435.11ns
2 | [Managed] new Uint32Array(1024) x 2,295,396 ops/sec (12 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" min..max=(346.78ns ... 471.27ns) p75=450.57ns p99=471.27ns
3 | ----------------------------------------------------------------------------
4 | new Uint32Array(1024) x 2,206,492 ops/sec (11 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" min..max=(413.34ns ... 498.95ns) p75=457.27ns p99=498.95ns
5 | [Managed] new Uint32Array(1024) x 2,308,235 ops/sec (10 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" min..max=(410.95ns ... 472.38ns) p75=444.99ns p99=472.38ns
6 | ----------------------------------------------------------------------------
7 | new Uint32Array(1024) x 2,765,470 ops/sec (11 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" min..max=(343.99ns ... 396.13ns) p75=386.55ns p99=396.13ns
8 | [Managed] new Uint32Array(1024) x 2,773,144 ops/sec (11 runs sampled) v8-opt-status="Optimized, Interpreted, Marked for Optimization, Concurrently Optimizing" min..max=(351.75ns ... 377.92ns) p75=369.54ns p99=377.92ns
9 | ----------------------------------------------------------------------------
10 |
--------------------------------------------------------------------------------
/examples/plugins/v8-never-optimize.js:
--------------------------------------------------------------------------------
1 | const { Suite, V8NeverOptimizePlugin } = require('../../lib');
2 |
3 | const suite = new Suite({
4 | plugins: [new V8NeverOptimizePlugin()],
5 | });
6 |
7 | suite
8 | .add(`new Uint32Array(1024)`, function () {
9 | return new Uint32Array(1024);
10 | })
11 | .add(`[Managed] new Uint32Array(1024)`, function (timer) {
12 | const assert = require('node:assert');
13 |
14 | let r;
15 |
16 | timer.start();
17 | for (let i = 0; i < timer.count; i++) {
18 | r = new Uint32Array(1024);
19 | }
20 | timer.end(timer.count);
21 |
22 | assert.ok(r);
23 | })
24 | .run();
25 |
--------------------------------------------------------------------------------
/examples/plugins/v8-never-optimize.js.log:
--------------------------------------------------------------------------------
1 | new Uint32Array(1024) x 2,535,349 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(357.30ns ... 442.55ns) p75=416.14ns p99=442.55ns
2 | [Managed] new Uint32Array(1024) x 2,646,454 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(356.81ns ... 405.88ns) p75=382.20ns p99=405.88ns
3 | ----------------------------------------------------------------------------
4 | new Uint32Array(1024) x 2,785,937 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(350.61ns ... 368.86ns) p75=360.60ns p99=368.86ns
5 | [Managed] new Uint32Array(1024) x 2,535,974 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(353.44ns ... 462.38ns) p75=447.16ns p99=462.38ns
6 | ----------------------------------------------------------------------------
7 | new Uint32Array(1024) x 2,336,195 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(380.49ns ... 453.11ns) p75=431.40ns p99=453.11ns
8 | [Managed] new Uint32Array(1024) x 2,624,328 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(341.92ns ... 432.82ns) p75=411.22ns p99=432.82ns
9 | ----------------------------------------------------------------------------
10 |
--------------------------------------------------------------------------------
/examples/plugins/v8-optimize-next-call.js:
--------------------------------------------------------------------------------
1 | const { Suite, V8OptimizeOnNextCallPlugin } = require('../../lib');
2 |
3 | const suite = new Suite({
4 | plugins: [new V8OptimizeOnNextCallPlugin()],
5 | });
6 |
7 | suite
8 | .add(`new Uint32Array(1024)`, function () {
9 | return new Uint32Array(1024);
10 | })
11 | .add(`[Managed] new Uint32Array(1024)`, function (timer) {
12 | const assert = require('node:assert');
13 |
14 | let r;
15 |
16 | timer.start();
17 | for (let i = 0; i < timer.count; i++) {
18 | r = new Uint32Array(1024);
19 | }
20 | timer.end(timer.count);
21 |
22 | assert.ok(r);
23 | })
24 | .run();
25 |
--------------------------------------------------------------------------------
/examples/plugins/v8-optimize-next-call.js.log:
--------------------------------------------------------------------------------
1 | new Uint32Array(1024) x 2,812,339 ops/sec (13 runs sampled) v8-optimize-next-call=enabled min..max=(314.34ns ... 373.62ns) p75=359.22ns p99=373.62ns
2 | [Managed] new Uint32Array(1024) x 2,763,965 ops/sec (11 runs sampled) v8-optimize-next-call=enabled min..max=(348.80ns ... 381.77ns) p75=373.99ns p99=381.77ns
3 | ----------------------------------------------------------------------------
4 | new Uint32Array(1024) x 2,827,020 ops/sec (11 runs sampled) v8-optimize-next-call=enabled min..max=(298.24ns ... 365.66ns) p75=354.33ns p99=365.66ns
5 | [Managed] new Uint32Array(1024) x 2,775,090 ops/sec (12 runs sampled) v8-optimize-next-call=enabled min..max=(349.78ns ... 378.17ns) p75=360.17ns p99=378.17ns
6 | ----------------------------------------------------------------------------
7 | new Uint32Array(1024) x 2,793,090 ops/sec (9 runs sampled) v8-optimize-next-call=enabled min..max=(342.17ns ... 352.58ns) p75=350.66ns p99=352.58ns
8 | [Managed] new Uint32Array(1024) x 2,751,982 ops/sec (10 runs sampled) v8-optimize-next-call=enabled min..max=(352.72ns ... 385.30ns) p75=367.30ns p99=385.30ns
9 | ----------------------------------------------------------------------------
10 |
--------------------------------------------------------------------------------
/examples/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # clean previous logs
4 | rm -f ./**/*.log
5 |
6 | for filename in ./**/*.*js; do
7 | echo "[1] Running $filename"
8 | node --allow-natives-syntax "./$filename" | sed "s,\x1B\[[0-9;]*m,,g" >>"$filename.log"
9 | echo -e "----------------------------------------------------------------------------" >>"$filename.log"
10 |
11 | echo "[2] Running $filename"
12 | node --allow-natives-syntax "./$filename" | sed "s,\x1B\[[0-9;]*m,,g" >>"$filename.log"
13 | echo -e "----------------------------------------------------------------------------" >>"$filename.log"
14 |
15 | echo "[3] Running $filename"
16 | node --allow-natives-syntax "./$filename" | sed "s,\x1B\[[0-9;]*m,,g" >>"$filename.log"
17 | echo -e "----------------------------------------------------------------------------" >>"$filename.log"
18 | done
19 |
--------------------------------------------------------------------------------
/examples/string-replace/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 |
3 | const suite = new Suite();
4 |
5 | const pattern = /[123]/g
6 | const replacements = { 1: 'a', 2: 'b', 3: 'c' }
7 |
8 | const subject = '123123123123123123123123123123123123123123123123'
9 |
10 | suite
11 | .add('single with matcher', function () {
12 | const r = subject.replace(pattern, m => replacements[m])
13 | })
14 | .add('multiple replaces', function () {
15 | const r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c')
16 | })
17 | .add('[Managed] single with matcher', function (timer) {
18 | const assert = require('node:assert');
19 |
20 | const pattern = /[123]/g
21 | const replacements = { 1: 'a', 2: 'b', 3: 'c' }
22 |
23 | const subject = '123123123123123123123123123123123123123123123123'
24 |
25 | let r;
26 |
27 | timer.start();
28 | for (let i = 0; i < timer.count; i++) {
29 | r = subject.replace(pattern, m => replacements[m]);
30 | }
31 | timer.end(timer.count);
32 |
33 | assert.ok(r);
34 | })
35 | .add('[Managed] multiple replaces', function (timer) {
36 | const assert = require('node:assert');
37 |
38 | const subject = '123123123123123123123123123123123123123123123123'
39 |
40 | let r;
41 |
42 | timer.start();
43 | for (let i = 0; i < timer.count; i++) {
44 | r = subject.replace(/1/g, 'a').replace(/2/g, 'b').replace(/3/g, 'c');
45 | }
46 | timer.end(timer.count);
47 | assert.ok(r);
48 | })
49 | .run()
50 |
--------------------------------------------------------------------------------
/examples/string-replace/node.js.log:
--------------------------------------------------------------------------------
1 | single with matcher x 752,020 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(1.32us ... 1.34us) p75=1.33us p99=1.34us
2 | multiple replaces x 642,602 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.55us) p75=1.55us p99=1.55us
3 | [Managed] single with matcher x 774,049 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(1.28us ... 1.30us) p75=1.29us p99=1.30us
4 | [Managed] multiple replaces x 645,958 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.58us) p75=1.55us p99=1.58us
5 | ----------------------------------------------------------------------------
6 | single with matcher x 748,221 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.33us ... 1.35us) p75=1.35us p99=1.35us
7 | multiple replaces x 639,390 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(1.55us ... 1.57us) p75=1.56us p99=1.57us
8 | [Managed] single with matcher x 765,396 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(1.30us ... 1.32us) p75=1.31us p99=1.32us
9 | [Managed] multiple replaces x 644,492 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(1.55us ... 1.55us) p75=1.55us p99=1.55us
10 | ----------------------------------------------------------------------------
11 | single with matcher x 742,699 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(1.32us ... 1.34us) p75=1.33us p99=1.34us
12 | multiple replaces x 640,209 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.57us) p75=1.56us p99=1.57us
13 | [Managed] single with matcher x 774,824 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.29us ... 1.30us) p75=1.29us p99=1.30us
14 | [Managed] multiple replaces x 642,062 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(1.54us ... 1.60us) p75=1.57us p99=1.60us
15 | ----------------------------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/examples/string-searching/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 |
3 | const suite = new Suite();
4 |
5 | const text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8'
6 | const regex = /application\/json/
7 |
8 | suite
9 | .add('Using includes', function () {
10 | const r = text.includes('application/json')
11 | })
12 | .add('Using indexof', function () {
13 | const r = text.indexOf('application/json') !== -1
14 | })
15 | .add('Using cached RegExp.test', function () {
16 | const r = regex.test(text)
17 | })
18 | .add('[Managed] Using includes', function (timer) {
19 | const assert = require('node:assert');
20 |
21 | const text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8';
22 |
23 | let r;
24 |
25 | timer.start();
26 | for (let i = 0; i < timer.count; i++) {
27 | r = text.includes('application/json');
28 | }
29 | timer.end(timer.count);
30 |
31 | assert.ok(r);
32 | })
33 | .add('[Managed] Using indexof', function (timer) {
34 | const assert = require('node:assert');
35 |
36 | const text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8';
37 |
38 | let r;
39 |
40 | timer.start();
41 | for (let i = 0; i < timer.count; i++) {
42 | r = text.indexOf('application/json') !== -1;
43 | }
44 | timer.end(timer.count);
45 |
46 | assert.ok(r);
47 | })
48 | .add('[Managed] Using cached RegExp.test', function (timer) {
49 | const assert = require('node:assert');
50 |
51 | const regex = /application\/json/;
52 | const text = 'text/html,application/xhtml+xml,application/xml;application/json;q=0.9,image/avif,image/webp,*/*;q=0.8';
53 |
54 | let r;
55 |
56 | timer.start();
57 | for (let i = 0; i < timer.count; i++) {
58 | r = regex.test(text);
59 | }
60 | timer.end(timer.count);
61 |
62 | assert.ok(r);
63 | })
64 | .run();
65 |
--------------------------------------------------------------------------------
/examples/string-searching/node.js.log:
--------------------------------------------------------------------------------
1 | Using includes x 131,465,887 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(7.53ns ... 7.61ns) p75=7.59ns p99=7.61ns
2 | Using indexof x 130,628,503 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.62ns ... 7.67ns) p75=7.65ns p99=7.67ns
3 | Using cached RegExp.test x 19,855,444 ops/sec (9 runs sampled) v8-never-optimize=true min..max=(50.32ns ... 50.38ns) p75=50.37ns p99=50.38ns
4 | [Managed] Using includes x 2,259,413,999 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns
5 | [Managed] Using indexof x 2,262,452,858 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns
6 | [Managed] Using cached RegExp.test x 24,771,962 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(40.11ns ... 40.43ns) p75=40.37ns p99=40.43ns
7 | ----------------------------------------------------------------------------
8 | Using includes x 131,872,369 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(7.53ns ... 7.70ns) p75=7.63ns p99=7.70ns
9 | Using indexof x 131,276,601 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.56ns ... 7.67ns) p75=7.59ns p99=7.67ns
10 | Using cached RegExp.test x 19,684,556 ops/sec (12 runs sampled) v8-never-optimize=true min..max=(50.51ns ... 51.39ns) p75=50.75ns p99=51.39ns
11 | [Managed] Using includes x 2,279,136,862 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns
12 | [Managed] Using indexof x 2,259,877,221 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns
13 | [Managed] Using cached RegExp.test x 24,313,713 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(40.92ns ... 41.19ns) p75=41.19ns p99=41.19ns
14 | ----------------------------------------------------------------------------
15 | Using includes x 132,464,212 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(7.52ns ... 7.56ns) p75=7.56ns p99=7.56ns
16 | Using indexof x 132,177,669 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(7.52ns ... 7.67ns) p75=7.60ns p99=7.67ns
17 | Using cached RegExp.test x 19,854,899 ops/sec (8 runs sampled) v8-never-optimize=true min..max=(50.28ns ... 50.36ns) p75=50.32ns p99=50.36ns
18 | [Managed] Using includes x 2,259,179,846 ops/sec (17 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns
19 | [Managed] Using indexof x 2,181,069,609 ops/sec (11 runs sampled) v8-never-optimize=true min..max=(0.50ns ... 0.50ns) p75=0.50ns p99=0.50ns
20 | [Managed] Using cached RegExp.test x 23,308,043 ops/sec (10 runs sampled) v8-never-optimize=true min..max=(40.23ns ... 41.24ns) p75=40.69ns p99=41.24ns
21 | ----------------------------------------------------------------------------
22 |
--------------------------------------------------------------------------------
/examples/time-mode.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../lib');
2 |
3 | const timeSuite = new Suite({
4 | benchmarkMode: 'time' // Set mode for the entire suite
5 | });
6 |
7 | const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
8 |
9 | timeSuite.add('Async Delay 100ms (time)', async () => {
10 | await delay(100);
11 | });
12 |
13 | timeSuite.add('Sync Busy Wait 50ms (time)', () => {
14 | const start = Date.now();
15 | while (Date.now() - start < 50);
16 | });
17 |
18 | timeSuite.add('Quick Sync Op with 5 repeats (time)', { repeatSuite: 5 }, () => {
19 | // This will run exactly once per repeat (5 times total)
20 | // and report the average time
21 | let x = 1 + 1;
22 | });
23 |
24 |
25 | (async () => {
26 | console.log('\nRunning benchmark suite in TIME mode...');
27 | await timeSuite.run();
28 | })();
29 |
--------------------------------------------------------------------------------
/examples/worker-threads/node.js:
--------------------------------------------------------------------------------
1 | const { Suite } = require('../../lib');
2 |
3 | const suite = new Suite({
4 | useWorkers: true,
5 | });
6 |
7 | suite
8 | .add('Using import without node: prefix', function () {
9 | return import('fs');
10 | })
11 | .add('Using import with node: prefix', function () {
12 | return import('node:fs');
13 | })
14 | .run();
15 |
--------------------------------------------------------------------------------
/lib/clock.js:
--------------------------------------------------------------------------------
1 | const { debug, types } = require("node:util");
2 | const { validateNumber } = require("./validators");
3 |
4 | let debugBench = debug("benchmark", (fn) => {
5 | debugBench = fn;
6 | });
7 |
8 | const kUnmanagedTimerResult = Symbol("kUnmanagedTimerResult");
9 |
10 | // If the smallest time measurement is 1ns
11 | // the minimum resolution of this timer is 0.5
12 | const MIN_RESOLUTION = 0.5;
13 |
14 | class Timer {
15 | constructor() {
16 | this.now = process.hrtime.bigint;
17 | }
18 |
19 | get scale() {
20 | return 1e9;
21 | }
22 |
23 | get resolution() {
24 | return 1 / 1e9;
25 | }
26 |
27 | /**
28 | * @param {number} timeInNs
29 | * @returns {string}
30 | */
31 | format(timeInNs) {
32 | validateNumber(timeInNs, "timeInNs", 0);
33 |
34 | if (timeInNs > 1e9) {
35 | return `${(timeInNs / 1e9).toFixed(2)}s`;
36 | }
37 |
38 | if (timeInNs > 1e6) {
39 | return `${(timeInNs / 1e6).toFixed(2)}ms`;
40 | }
41 |
42 | if (timeInNs > 1e3) {
43 | return `${(timeInNs / 1e3).toFixed(2)}us`;
44 | }
45 |
46 | return `${(timeInNs).toFixed(2)}ns`;
47 | }
48 | }
49 |
50 | const timer = new Timer();
51 |
52 | class ManagedTimer {
53 | startTime;
54 | endTime;
55 | iterations;
56 | recommendedCount;
57 |
58 | /**
59 | * @param {number} recommendedCount
60 | */
61 | constructor(recommendedCount) {
62 | this.recommendedCount = recommendedCount;
63 | }
64 |
65 | /**
66 | * Returns the recommended value to be used to benchmark your code
67 | * @returns {number}
68 | */
69 | get count() {
70 | return this.recommendedCount;
71 | }
72 |
73 | /**
74 | * Starts the timer
75 | */
76 | start() {
77 | this.startTime = timer.now();
78 | }
79 |
80 | /**
81 | * Stops the timer
82 | * @param {number} [iterations=1] The amount of iterations that run
83 | */
84 | end(iterations = 1) {
85 | this.endTime = timer.now();
86 | validateNumber(iterations, "iterations", 1);
87 | this.iterations = iterations;
88 | }
89 |
90 | [kUnmanagedTimerResult](context) {
91 | if (this.startTime === undefined)
92 | throw new Error("You forgot to call .start()");
93 |
94 | if (this.endTime === undefined)
95 | throw new Error("You forgot to call .end(count)");
96 |
97 | return [Number(this.endTime - this.startTime), this.iterations, context];
98 | }
99 | }
100 |
101 | function createRunUnmanagedBenchmark(bench, awaitOrEmpty) {
102 | const varNames = {
103 | awaitOrEmpty,
104 | timer: "timer",
105 | context: "context",
106 | bench: "bench",
107 | };
108 |
109 | let code = `
110 | let i = 0;
111 | let ${varNames.context} = {};
112 | `;
113 |
114 | let benchFnCall = `${awaitOrEmpty}${varNames.bench}.fn()`;
115 | const wrapFunctions = [];
116 | for (const p of bench.plugins) {
117 | if (typeof p.beforeClockTemplate === "function") {
118 | const [newCode, functionToCall] = p.beforeClockTemplate(varNames);
119 | code += newCode;
120 | if (functionToCall) {
121 | wrapFunctions.push(functionToCall);
122 | }
123 | }
124 | }
125 | benchFnCall = wrapFunctions.reduce((prev, n) => {
126 | return `${n}(${prev})`;
127 | }, benchFnCall);
128 |
129 | code += `
130 | const startedAt = ${varNames.timer}.now();
131 |
132 | for (; i < count; i++)
133 | ${benchFnCall};
134 |
135 | const duration = Number(${varNames.timer}.now() - startedAt);
136 | `;
137 |
138 | for (const p of bench.plugins) {
139 | if (typeof p.afterClockTemplate === "function") {
140 | [newCode] = p.afterClockTemplate(varNames);
141 | code += newCode;
142 | }
143 | }
144 |
145 | code += `return [duration, count, ${varNames.context}];`;
146 | return code;
147 | }
148 |
149 | function createRunManagedBenchmark(bench, awaitOrEmpty) {
150 | const varNames = {
151 | awaitOrEmpty,
152 | timer: "timer",
153 | context: "context",
154 | bench: "bench",
155 | };
156 |
157 | let code = `
158 | let i = 0;
159 | let ${varNames.context} = {};
160 | `;
161 |
162 | let benchFnCall = `${awaitOrEmpty}${varNames.bench}.fn(${varNames.timer})`;
163 | const wrapFunctions = [];
164 | for (const p of bench.plugins) {
165 | if (typeof p.beforeClockTemplate === "function") {
166 | [newCode, functionToCall] = p.beforeClockTemplate(varNames);
167 | code += newCode;
168 | if (functionToCall) {
169 | wrapFunctions.push(functionToCall);
170 | }
171 | }
172 | }
173 | benchFnCall = wrapFunctions.reduce((prev, n) => {
174 | return `${n}(${prev})`;
175 | }, benchFnCall);
176 |
177 | code += `
178 | ${benchFnCall};
179 | const result = ${varNames.timer}[kUnmanagedTimerResult](${varNames.context});
180 | `;
181 | for (const p of bench.plugins) {
182 | if (typeof p.afterClockTemplate === "function") {
183 | [newCode] = p.afterClockTemplate(varNames);
184 | code += newCode;
185 | }
186 | }
187 |
188 | code += "return result;";
189 | return code;
190 | }
191 |
192 | const AsyncFunction = (async () => {}).constructor;
193 | const SyncFunction = (() => {}).constructor;
194 |
195 | function createFnString(bench) {
196 | const { isAsync, hasArg } = bench;
197 |
198 | const compiledFnStringFactory = hasArg
199 | ? createRunManagedBenchmark
200 | : createRunUnmanagedBenchmark;
201 | const compiledFnString = compiledFnStringFactory(
202 | bench,
203 | isAsync ? "await " : "",
204 | );
205 | return compiledFnString;
206 | }
207 |
208 | function createRunner(bench, recommendedCount) {
209 | const { isAsync, hasArg } = bench;
210 | const compiledFnString = bench.fnStr;
211 |
212 | const createFnPrototype = isAsync ? AsyncFunction : SyncFunction;
213 | const compiledFn = createFnPrototype(
214 | "bench",
215 | "timer",
216 | "count",
217 | "kUnmanagedTimerResult",
218 | compiledFnString,
219 | );
220 | const selectedTimer = hasArg ? new ManagedTimer(recommendedCount) : timer;
221 | const runner = compiledFn.bind(
222 | globalThis,
223 | bench,
224 | selectedTimer,
225 | recommendedCount,
226 | kUnmanagedTimerResult,
227 | );
228 | debugBench(`Compiled Code: ${compiledFnString}`);
229 | debugBench(
230 | `Created compiled benchmark, hasArg=${hasArg}, isAsync=${isAsync}, recommendedCount=${recommendedCount}`,
231 | );
232 |
233 | return runner;
234 | }
235 |
236 | /**
237 | * Executes a benchmark and returns the time taken and number of iterations
238 | * @param {import('./index').Benchmark} bench - The benchmark to execute
239 | * @param {number} recommendedCount - The recommended number of iterations
240 | * @param {Object} [options] - Additional options
241 | * @param {boolean} [options.timeMode=false] - If true, runs the benchmark exactly once
242 | * @returns {Promise<[number, number]>} - Returns [duration, iterations]
243 | */
244 | async function clockBenchmark(bench, recommendedCount, options = {}) {
245 | const runner = createRunner(bench, recommendedCount);
246 | const result = await runner();
247 |
248 | // Just to avoid issues with empty fn
249 | result[0] = Math.max(MIN_RESOLUTION, result[0]);
250 |
251 | for (const p of bench.plugins) {
252 | if (typeof p.onCompleteBenchmark === "function") {
253 | // TODO: this won't work when useWorkers=true
254 | p.onCompleteBenchmark(result, bench);
255 | }
256 | }
257 |
258 | debugBench(
259 | `Took ${timer.format(result[0])} to execute ${result[1]} iterations${options.timeMode ? " (time mode)" : ""}`,
260 | );
261 | return result;
262 | }
263 |
264 | module.exports = {
265 | clockBenchmark,
266 | createFnString,
267 | timer,
268 | MIN_RESOLUTION,
269 | debugBench,
270 | };
271 |
--------------------------------------------------------------------------------
/lib/histogram.js:
--------------------------------------------------------------------------------
1 | const { validateNumber } = require("./validators");
2 |
3 | /**
4 | * A class that calculates and maintains statistical measurements for a set of numeric samples.
5 | * Handles outlier removal, and calculates various statistical measures like mean, standard deviation,
6 | * coefficient of variation, and percentiles.
7 | */
8 | class StatisticalHistogram {
9 | all = [];
10 | min;
11 | max;
12 | mean;
13 | cv;
14 | stddev;
15 |
16 | /**
17 | * @returns {number[]}
18 | */
19 | get samples() {
20 | return this.all.slice();
21 | }
22 |
23 | /**
24 | * @param {number} percentile
25 | * @returns {number}
26 | */
27 | percentile(percentile) {
28 | validateNumber(percentile, "percentile");
29 |
30 | if (Number.isNaN(percentile) || percentile < 0 || percentile > 100)
31 | throw new Error(
32 | "Invalid percentile value. Must be a number between 0 and 100.",
33 | );
34 |
35 | if (this.all.length === 0) return 0;
36 |
37 | if (percentile === 0) return this.all[0];
38 |
39 | return this.all[Math.ceil(this.all.length * (percentile / 100)) - 1];
40 | }
41 |
42 | /**
43 | * @param {number} value
44 | */
45 | record(value) {
46 | validateNumber(value, "value", 0);
47 |
48 | this.all.push(value);
49 | }
50 |
51 | finish() {
52 | this.removeOutliers();
53 |
54 | this.calculateMinMax();
55 | this.calculateMean();
56 | this.calculateStd();
57 | this.calculateCv();
58 | }
59 |
60 | /**
61 | * References:
62 | * - https://gist.github.com/rmeissn/f5b42fb3e1386a46f60304a57b6d215a
63 | * - https://en.wikipedia.org/wiki/Interquartile_range
64 | */
65 | removeOutliers() {
66 | this.all.sort((a, b) => a - b);
67 |
68 | const size = this.all.length;
69 |
70 | if (size < 4) return;
71 |
72 | let q1;
73 | let q3;
74 |
75 | if (((size - 1) / 4) % 1 === 0 || (size / 4) % 1 === 0) {
76 | q1 =
77 | (1 / 2) *
78 | (this.all[Math.floor(size / 4) - 1] + this.all[Math.floor(size / 4)]);
79 | q3 =
80 | (1 / 2) *
81 | (this.all[Math.ceil((size * 3) / 4) - 1] +
82 | this.all[Math.ceil((size * 3) / 4)]);
83 | } else {
84 | q1 = this.all[Math.floor(size / 4)];
85 | q3 = this.all[Math.floor((size * 3) / 4)];
86 | }
87 |
88 | const iqr = q3 - q1;
89 | const minValue = q1 - iqr * 1.5;
90 | const maxValue = q3 + iqr * 1.5;
91 |
92 | this.all = this.all.filter(
93 | (value) => value <= maxValue && value >= minValue,
94 | );
95 | }
96 |
97 | calculateMinMax() {
98 | this.min = Number.POSITIVE_INFINITY;
99 | this.max = Number.NEGATIVE_INFINITY;
100 |
101 | for (let i = 0; i < this.all.length; i++) {
102 | this.min = Math.min(this.all[i], this.min);
103 | this.max = Math.max(this.all[i], this.max);
104 | }
105 | }
106 |
107 | calculateMean() {
108 | if (this.all.length === 0) {
109 | this.mean = 0;
110 | return;
111 | }
112 |
113 | if (this.all.length === 1) {
114 | this.mean = this.all[0];
115 | return;
116 | }
117 |
118 | this.mean =
119 | this.all.reduce(
120 | (acc, value) => Math.min(Number.MAX_SAFE_INTEGER, acc + value),
121 | 0,
122 | ) / this.all.length;
123 | }
124 |
125 | calculateStd() {
126 | if (this.all.length < 2) {
127 | this.stddev = 0;
128 | return;
129 | }
130 | const variance =
131 | this.all.reduce((acc, value) => {
132 | return acc + (value - this.mean) ** 2;
133 | }, 0) /
134 | (this.all.length - 1);
135 | this.stddev = Math.sqrt(variance);
136 | }
137 |
138 | /**
139 | * References:
140 | * - https://en.wikipedia.org/wiki/Coefficient_of_variation
141 | * - https://github.com/google/benchmark/blob/159eb2d0ffb85b86e00ec1f983d72e72009ec387/src/statistics.ccL81-L88
142 | */
143 | calculateCv() {
144 | if (this.all.length < 2 || this.mean === 0) {
145 | this.cv = 0;
146 | return;
147 | }
148 |
149 | this.cv = (this.stddev / this.mean) * 100;
150 | }
151 | }
152 |
153 | module.exports = {
154 | StatisticalHistogram,
155 | };
156 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | const { Worker } = require("node:worker_threads");
2 | const { types } = require("node:util");
3 | const path = require("node:path");
4 |
5 | const {
6 | textReport,
7 | chartReport,
8 | htmlReport,
9 | jsonReport,
10 | csvReport,
11 | } = require("./report");
12 | const {
13 | getInitialIterations,
14 | runBenchmark,
15 | runWarmup,
16 | } = require("./lifecycle");
17 | const { debugBench, timer, createFnString } = require("./clock");
18 | const {
19 | validatePlugins,
20 | V8NeverOptimizePlugin,
21 | V8GetOptimizationStatus,
22 | V8OptimizeOnNextCallPlugin,
23 | } = require("./plugins");
24 | const {
25 | validateFunction,
26 | validateNumber,
27 | validateObject,
28 | validateString,
29 | validateArray,
30 | validateBenchmarkMode,
31 | } = require("./validators");
32 |
33 | const getFunctionBody = (string) =>
34 | string.substring(string.indexOf("{") + 1, string.lastIndexOf("}"));
35 |
36 | class Benchmark {
37 | name = "Benchmark";
38 | fn;
39 | minTime;
40 | maxTime;
41 | plugins;
42 | repeatSuite;
43 | minSamples;
44 |
45 | constructor(name, fn, minTime, maxTime, plugins, repeatSuite, minSamples) {
46 | this.name = name;
47 | this.fn = fn;
48 | this.minTime = minTime;
49 | this.maxTime = maxTime;
50 | this.plugins = plugins;
51 | this.repeatSuite = repeatSuite;
52 | this.minSamples = minSamples;
53 |
54 | this.hasArg = this.fn.length >= 1;
55 | if (this.fn.length > 1) {
56 | process.emitWarning(
57 | `The benchmark "${this.name}" function should not have more than 1 argument.`,
58 | );
59 | }
60 |
61 | this.isAsync = types.isAsyncFunction(this.fn);
62 |
63 | this.fnStr = createFnString(this);
64 | }
65 |
66 | serializeBenchmark() {
67 | // Regular functions can't be passed to worker.postMessage
68 | // So we pass the string and deserialize fnStr into a new Function
69 | // on worker
70 | this.fn = getFunctionBody(this.fn.toString());
71 | }
72 | }
73 |
74 | const defaultBenchOptions = {
75 | // 0.05s - Arbitrary number used in some benchmark tools
76 | minTime: 0.05,
77 | // 0.5s - Arbitrary number used in some benchmark tools
78 | maxTime: 0.5,
79 | // Number of times the benchmark will be repeated
80 | repeatSuite: 1,
81 | // Number minimum of samples the each round
82 | minSamples: 10,
83 | };
84 |
85 | function throwIfNoNativesSyntax() {
86 | if (process.execArgv.includes("--allow-natives-syntax") === false) {
87 | throw new Error(
88 | "bench-node module must be run with --allow-natives-syntax argument",
89 | );
90 | }
91 | }
92 |
93 | class Suite {
94 | #benchmarks;
95 | #reporter;
96 | #plugins;
97 | #useWorkers;
98 | #benchmarkMode;
99 |
100 | constructor(options = {}) {
101 | this.#benchmarks = [];
102 | validateObject(options, "options");
103 |
104 | if (options?.reporter !== undefined) {
105 | if (options?.reporter !== false && options?.reporter !== null) {
106 | validateFunction(options.reporter, "reporter");
107 | }
108 | this.#reporter = options.reporter;
109 | } else {
110 | this.#reporter = textReport;
111 | }
112 |
113 | this.#useWorkers = options.useWorkers || false;
114 |
115 | if (options?.plugins) {
116 | validateArray(options.plugins, "plugin");
117 | validatePlugins(options.plugins);
118 | }
119 | this.#plugins = options?.plugins || [new V8NeverOptimizePlugin()];
120 |
121 | // Benchmark Mode setup
122 | this.#benchmarkMode = options.benchmarkMode || "ops"; // Default to 'ops'
123 | validateBenchmarkMode(this.#benchmarkMode, "options.benchmarkMode");
124 | }
125 |
126 | add(name, options, fn) {
127 | validateString(name, "name");
128 | if (typeof options === "function") {
129 | fn = options;
130 | options = defaultBenchOptions;
131 | } else {
132 | validateObject(options, "options");
133 | options = {
134 | ...defaultBenchOptions,
135 | ...options,
136 | };
137 | validateNumber(
138 | options.minTime,
139 | "options.minTime",
140 | timer.resolution * 1e3,
141 | );
142 | validateNumber(options.maxTime, "options.maxTime", options.minTime);
143 | validateNumber(
144 | options.repeatSuite,
145 | "options.repeatSuite",
146 | options.repeatSuite,
147 | );
148 | validateNumber(
149 | options.minSamples,
150 | "options.minSamples",
151 | options.minSamples,
152 | );
153 | }
154 | validateFunction(fn, "fn");
155 |
156 | const benchmark = new Benchmark(
157 | name,
158 | fn,
159 | options.minTime,
160 | options.maxTime,
161 | this.#plugins,
162 | options.repeatSuite,
163 | options.minSamples,
164 | );
165 | this.#benchmarks.push(benchmark);
166 | return this;
167 | }
168 |
169 | async run() {
170 | throwIfNoNativesSyntax();
171 | const results = new Array(this.#benchmarks.length);
172 |
173 | // It doesn't make sense to warmup a fresh new instance of Worker.
174 | // TODO: support warmup directly in the Worker.
175 | if (!this.#useWorkers) {
176 | // This is required to avoid variance on first benchmark run
177 | for (let i = 0; i < this.#benchmarks.length; ++i) {
178 | const benchmark = this.#benchmarks[i];
179 | debugBench(
180 | `Warmup ${benchmark.name} with minTime=${benchmark.minTime}, maxTime=${benchmark.maxTime}`,
181 | );
182 | const initialIteration = await getInitialIterations(benchmark);
183 | await runWarmup(benchmark, initialIteration, {
184 | minTime: 0.005,
185 | maxTime: 0.05,
186 | });
187 | }
188 | }
189 |
190 | for (let i = 0; i < this.#benchmarks.length; ++i) {
191 | const benchmark = this.#benchmarks[i];
192 | // Warmup is calculated to reduce noise/bias on the results
193 | const initialIterations = await getInitialIterations(benchmark);
194 | debugBench(
195 | `Starting ${benchmark.name} with mode=${this.#benchmarkMode}, minTime=${benchmark.minTime}, maxTime=${benchmark.maxTime}, repeatSuite=${benchmark.repeatSuite}, minSamples=${benchmark.minSamples}`,
196 | );
197 |
198 | let result;
199 | if (this.#useWorkers) {
200 | if (this.#benchmarkMode === "time") {
201 | console.warn(
202 | "Warning: Worker mode currently doesn't fully support 'time' benchmarkMode.",
203 | );
204 | }
205 | result = await this.runWorkerBenchmark(benchmark, initialIterations);
206 | } else {
207 | result = await runBenchmark(
208 | benchmark,
209 | initialIterations,
210 | this.#benchmarkMode,
211 | benchmark.repeatSuite,
212 | benchmark.minSamples,
213 | );
214 | }
215 | results[i] = result;
216 | }
217 |
218 | if (this.#reporter) {
219 | this.#reporter(results);
220 | }
221 |
222 | return results;
223 | }
224 |
225 | async runWorkerBenchmark(benchmark, initialIterations) {
226 | return new Promise((resolve, reject) => {
227 | const workerPath = path.resolve(__dirname, "./worker-runner.js");
228 | const worker = new Worker(workerPath);
229 |
230 | benchmark.serializeBenchmark();
231 | worker.postMessage({
232 | benchmark,
233 | initialIterations,
234 | benchmarkMode: this.#benchmarkMode, // Pass suite mode
235 | repeatSuite: benchmark.repeatSuite,
236 | minSamples: benchmark.minSamples,
237 | });
238 |
239 | worker.on("message", (result) => {
240 | resolve(result);
241 | worker.terminate();
242 | });
243 | worker.on("error", (error) => {
244 | reject(error);
245 | worker.terminate();
246 | });
247 | worker.on("exit", (code) => {
248 | if (code !== 0)
249 | reject(new Error(`Worker stopped with exit code ${code}`));
250 | });
251 | });
252 | }
253 | }
254 |
255 | module.exports = {
256 | Suite,
257 | V8NeverOptimizePlugin,
258 | V8GetOptimizationStatus,
259 | V8OptimizeOnNextCallPlugin,
260 | chartReport,
261 | textReport,
262 | htmlReport,
263 | jsonReport,
264 | csvReport,
265 | };
266 |
--------------------------------------------------------------------------------
/lib/lifecycle.js:
--------------------------------------------------------------------------------
1 | const {
2 | clockBenchmark,
3 | debugBench,
4 | MIN_RESOLUTION,
5 | timer,
6 | } = require("./clock");
7 | const { StatisticalHistogram } = require("./histogram");
8 |
9 | /**
10 | * @param {number} durationPerOp The amount of time each operation takes
11 | * @param {number} targetTime The amount of time we want the benchmark to execute
12 | */
13 | function getItersForOpDuration(durationPerOp, targetTime) {
14 | const totalOpsForMinTime = targetTime / (durationPerOp / timer.scale);
15 |
16 | return Math.min(
17 | Number.MAX_SAFE_INTEGER,
18 | Math.max(1, Math.round(totalOpsForMinTime)),
19 | );
20 | }
21 |
22 | function parsePluginsResult(plugins, name) {
23 | const result = [];
24 | for (const p of plugins) {
25 | result.push({
26 | name: p.toString(),
27 | result: p.getResult?.(name) ?? "enabled",
28 | report: p.getReport?.(name) ?? "",
29 | });
30 | }
31 | return result;
32 | }
33 |
34 | /**
35 | * Calculates and returns the initial number of iterations for a benchmark
36 | * @param {import('./index').Benchmark} bench - The benchmark object to be executed
37 | * @returns {Promise} The calculated number of initial iterations
38 | */
39 | async function getInitialIterations(bench) {
40 | const { 0: duration, 1: realIterations } = await clockBenchmark(bench, 30);
41 |
42 | // Just to avoid issues with empty fn
43 | const durationPerOp = Math.max(MIN_RESOLUTION, duration / realIterations);
44 | debugBench(
45 | `Duration per operation on initial count: ${timer.format(durationPerOp)}`,
46 | );
47 |
48 | // TODO: is this a correct assumpion?
49 | if (durationPerOp / timer.scale >= bench.maxTime)
50 | process.emitWarning(
51 | `The benchmark "${bench.name}" has a duration per operation greater than the maxTime.`,
52 | );
53 |
54 | return getItersForOpDuration(durationPerOp, bench.minTime);
55 | }
56 |
57 | /**
58 | * Executes the warmup phase of a benchmark
59 | * @param {import('./index').Benchmark} bench - The benchmark object to be executed
60 | * @param {number} initialIterations - The initial number of iterations to run
61 | * @param {Object} options - Warmup options
62 | * @param {number} [options.minTime] - Minimum time for warmup, defaults to bench.minTime
63 | * @param {number} [options.maxTime] - Maximum time for warmup, defaults to bench.minTime
64 | * @returns {Promise}
65 | */
66 | async function runWarmup(bench, initialIterations, { minTime, maxTime }) {
67 | minTime = minTime ?? bench.minTime;
68 | maxTime = maxTime ?? bench.minTime;
69 |
70 | const maxDuration = maxTime * timer.scale;
71 | const minSamples = 10;
72 |
73 | let iterations = 0;
74 | let timeSpent = 0;
75 | let samples = 0;
76 |
77 | while (timeSpent < maxDuration || samples <= minSamples) {
78 | const { 0: duration, 1: realIterations } = await clockBenchmark(
79 | bench,
80 | initialIterations,
81 | );
82 | timeSpent += duration;
83 |
84 | iterations += realIterations;
85 | iterations = Math.min(Number.MAX_SAFE_INTEGER, iterations);
86 |
87 | // Just to avoid issues with empty fn
88 | const durationPerOp = Math.max(MIN_RESOLUTION, duration / realIterations);
89 |
90 | const minWindowTime = Math.max(
91 | 0,
92 | Math.min((maxDuration - timeSpent) / timer.scale, minTime),
93 | );
94 | initialIterations = getItersForOpDuration(durationPerOp, minWindowTime);
95 | samples++;
96 | }
97 | }
98 |
99 | async function runBenchmarkOnce(
100 | bench,
101 | histogram,
102 | { initialIterations, maxDuration, minSamples },
103 | benchmarkMode = "ops",
104 | ) {
105 | let iterations = 0;
106 | let timeSpent = 0;
107 |
108 | // For time mode, we want to run the benchmark exactly once
109 | if (benchmarkMode === "time") {
110 | const { 0: duration, 1: realIterations } = await clockBenchmark(bench, 1);
111 | timeSpent = duration;
112 | iterations = realIterations;
113 |
114 | // Record the duration in the histogram
115 | histogram.record(duration);
116 |
117 | return { iterations, timeSpent };
118 | }
119 |
120 | // Ops mode - run the sampling loop
121 | while (timeSpent < maxDuration || histogram.samples.length <= minSamples) {
122 | const { 0: duration, 1: realIterations } = await clockBenchmark(
123 | bench,
124 | initialIterations,
125 | );
126 | timeSpent += duration;
127 |
128 | iterations += realIterations;
129 | iterations = Math.min(Number.MAX_SAFE_INTEGER, iterations);
130 |
131 | // Just to avoid issues with empty fn
132 | const durationPerOp = Math.max(MIN_RESOLUTION, duration / realIterations);
133 |
134 | histogram.record(durationPerOp);
135 |
136 | const minWindowTime = Math.max(
137 | 0,
138 | Math.min((maxDuration - timeSpent) / timer.scale, bench.minTime),
139 | );
140 | initialIterations = getItersForOpDuration(durationPerOp, minWindowTime);
141 | }
142 |
143 | return { iterations, timeSpent };
144 | }
145 |
146 | /**
147 | * Executes a benchmark with the specified parameters
148 | * @param {import('./index').Benchmark} bench - The benchmark object to be executed
149 | * @param {number} initialIterations - The initial number of iterations to run
150 | * @param {string} benchmarkMode - The benchmark mode ('ops' or 'time')
151 | * @param {number} repeatSuite - Number of times to repeat the benchmark suite
152 | * @param {number} minSamples - Minimum number of samples to collect
153 | * @returns {Promise