{"id":13495385,"url":"https://github.com/caderek/benny","last_synced_at":"2026-01-17T13:11:51.336Z","repository":{"id":35134957,"uuid":"211187190","full_name":"caderek/benny","owner":"caderek","description":"A dead simple benchmarking framework for JS/TS libs","archived":false,"fork":false,"pushed_at":"2023-03-04T04:49:13.000Z","size":4268,"stargazers_count":768,"open_issues_count":25,"forks_count":15,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-12-19T13:58:18.367Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://caderek.github.io/benny/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/caderek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-09-26T21:50:32.000Z","updated_at":"2025-12-10T23:37:30.000Z","dependencies_parsed_at":"2023-10-20T19:01:09.282Z","dependency_job_id":null,"html_url":"https://github.com/caderek/benny","commit_stats":{"total_commits":142,"total_committers":2,"mean_commits":71.0,"dds":0.007042253521126751,"last_synced_commit":"e8c5fc490a952fa7168fe8cdd159fa0c5a8b0ab7"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/caderek/benny","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caderek%2Fbenny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caderek%2Fbenny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caderek%2Fbenny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caderek%2Fbenny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/caderek","download_url":"https://codeload.github.com/caderek/benny/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caderek%2Fbenny/sbom","scorecard":{"id":261556,"data":{"date":"2025-08-11","repo":{"name":"github.com/caderek/benny","commit":"e8c5fc490a952fa7168fe8cdd159fa0c5a8b0ab7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: ISC License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"17 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T10:56:43.129Z","repository_id":35134957,"created_at":"2025-08-17T10:56:43.129Z","updated_at":"2025-08-17T10:56:43.129Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27977649,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-23T02:00:07.087Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T19:01:34.193Z","updated_at":"2026-01-17T13:11:51.317Z","avatar_url":"https://github.com/caderek.png","language":"TypeScript","readme":"# Benny - a dead simple benchmarking framework\n\n![npm](https://img.shields.io/npm/v/benny)\n![CircleCI](https://img.shields.io/circleci/build/github/caderek/benny)\n![Codecov](https://img.shields.io/codecov/c/github/caderek/benny)\n![Snyk Vulnerabilities for npm package](https://img.shields.io/snyk/vulnerabilities/npm/benny)\n![GitHub](https://img.shields.io/github/license/caderek/benny)\n\n![Example](https://raw.githubusercontent.com/caderek/benny/master/benny.gif)\n\n## Table of contents\n\n1. [Overview](#overview)\n2. [Installation](#installation)\n3. [Quick example](#quick-example)\n4. [API](#api)\n5. [Working with many suites](#many-suites)\n6. [Working with async code](#async-code)\n7. [Tweaking benchmarks](#tweaking)\n8. [Code reuse](#code-reuse)\n9. [Snippets](#snippets)\n10. [Additional examples](#additional-examples)\n11. [License](#license)\n\n\u003ca id='overview'\u003e\u003c/a\u003e\n\n## Overview\n\nBenny builds on top of the excellent (but complex) [benchmark](https://www.npmjs.com/package/benchmark) package.\n\nBenny provides an improved API that allows you to:\n\n- easily prepare benchmarks for synchronous, as well as async code,\n- prepare local setup (sync or async) for each case,\n- skip or run only selected cases,\n- save results to a JSON / CSV / HTML (table or chart) file,\n- pretty-print results without additional setup,\n- use suite results as Promises.\n\nAdditionally, it provides sound defaults suitable for most use cases (that you can tweak if you need) and excellent IDE support with built-in type definitions.\n\n\u003ca id='installation'\u003e\u003c/a\u003e\n\n## Installation\n\nUsing NPM:\n\n```sh\nnpm i benny -D\n```\n\nUsing Yarn:\n\n```\nyarn add benny -D\n```\n\n\u003ca id='quick-example'\u003e\u003c/a\u003e\n\n## Quick example\n\n```js\n/* benchmark.js */\nconst b = require('benny')\n\nb.suite(\n  'Example',\n\n  b.add('Reduce two elements', () =\u003e {\n    ;[1, 2].reduce((a, b) =\u003e a + b)\n  }),\n\n  b.add('Reduce five elements', () =\u003e {\n    ;[1, 2, 3, 4, 5].reduce((a, b) =\u003e a + b)\n  }),\n\n  b.cycle(),\n  b.complete(),\n  b.save({ file: 'reduce', version: '1.0.0' }),\n  b.save({ file: 'reduce', format: 'chart.html' }),\n)\n```\n\n---\n\nExecute:\n\n```sh\nnode benchmark.js\n```\n\n---\n\nOutput:\n\n```\nRunning \"Example\" suite...\nProgress: 100%\n\n  Reduce two elements:\n    213 985 744 ops/s, ±0.61%   | fastest\n\n  Reduce five elements:\n    109 395 371 ops/s, ±0.66%   | slowest, 48.88% slower\n\nFinished 2 cases!\n  Fastest: Reduce two elements\n  Slowest: Reduce five elements\n\nSaved to: benchmark/results/reduce.json\n\nSaved to: benchmark/results/reduce.chart.html\n```\n\n---\n\nJSON file content:\n\n```json\n{\n  \"name\": \"Example\",\n  \"date\": \"2021-10-02T03:00:10.907Z\",\n  \"version\": \"1.0.0\",\n  \"results\": [\n    {\n      \"name\": \"Reduce two elements\",\n      \"ops\": 213985744,\n      \"margin\": 0.61,\n      \"percentSlower\": 0\n    },\n    {\n      \"name\": \"Reduce five elements\",\n      \"ops\": 109395371,\n      \"margin\": 0.66,\n      \"percentSlower\": 48.88\n    }\n  ],\n  \"fastest\": {\n    \"name\": \"Reduce two elements\",\n    \"index\": 0\n  },\n  \"slowest\": {\n    \"name\": \"Reduce five elements\",\n    \"index\": 1\n  }\n}\n```\n\nNote: If you use the `{ details: true }` option in your save function, you will get the result similar to this one:\n\n\u003cdetails\u003e\n\u003csummary\u003eclick to expand\u003c/summary\u003e\n\u003cpre lang=\"json\"\u003e\n{\n  \"name\": \"Example\",\n  \"date\": \"2021-10-02T03:29:32.520Z\",\n  \"version\": \"1.0.0\",\n  \"results\": [\n    {\n      \"name\": \"Reduce two elements\",\n      \"ops\": 213928866,\n      \"margin\": 0.68,\n      \"options\": {\n        \"delay\": 0.005,\n        \"initCount\": 1,\n        \"minTime\": 0.05,\n        \"maxTime\": 5,\n        \"minSamples\": 5\n      },\n      \"samples\": 92,\n      \"promise\": false,\n      \"details\": {\n        \"min\": 4.430438606424907e-9,\n        \"max\": 5.0581801724029255e-9,\n        \"mean\": 4.6744509956552405e-9,\n        \"median\": 4.656646139027003e-9,\n        \"standardDeviation\": 1.5617421028409467e-10,\n        \"marginOfError\": 3.191328246925009e-11,\n        \"relativeMarginOfError\": 0.6827172324389006,\n        \"standardErrorOfMean\": 1.628228697410719e-11,\n        \"sampleVariance\": 2.439038395786062e-20,\n        \"sampleResults\": [\n          4.430438606424907e-9,\n          ...other \n        ]\n      },\n      \"completed\": true,\n      \"percentSlower\": 0\n    },\n    {\n      \"name\": \"Reduce five elements\",\n      \"ops\": 109203399,\n      \"margin\": 0.92,\n      \"options\": {\n        \"delay\": 0.005,\n        \"initCount\": 1,\n        \"minTime\": 0.05,\n        \"maxTime\": 5,\n        \"minSamples\": 5\n      },\n      \"samples\": 90,\n      \"promise\": false,\n      \"details\": {\n        \"min\": 8.963947485831316e-9,\n        \"max\": 1.2164955890034665e-8,\n        \"mean\": 9.15722416437813e-9,\n        \"median\": 9.072483556407842e-9,\n        \"standardDeviation\": 4.0880731896036814e-10,\n        \"marginOfError\": 8.446046713469782e-11,\n        \"relativeMarginOfError\": 0.9223370053913447,\n        \"standardErrorOfMean\": 4.309207506872338e-11,\n        \"sampleVariance\": 1.6712342403556417e-19,\n        \"sampleResults\": [\n          8.963947485831316e-9,\n          ...other \n        ]\n      },\n      \"completed\": true,\n      \"percentSlower\": 48.95\n    }\n  ],\n  \"fastest\": {\n    \"name\": \"Reduce two elements\",\n    \"index\": 0\n  },\n  \"slowest\": {\n    \"name\": \"Reduce five elements\",\n    \"index\": 1\n  }\n}\n\u003c/pre\u003e\n\u003c/details\u003e\n\n---\n\nHTML chart (it uses Chart.js on canvas, so you can save it as PNG by right-clicking on it):\n\n![chart](https://raw.githubusercontent.com/caderek/benny/master/chart.png)\n\n\u003ca id='api'\u003e\u003c/a\u003e\n\n## API\n\n```js\n// You can also use ES Modules syntax and default imports\nconst { add, complete, cycle, save, suite } = require('benny')\n\nsuite(\n  /**\n   * Name of the suite - required\n   */\n  \"My suite\",\n\n  /**\n   * If the code that you want to benchmark has no setup,\n   * you can run it directly:\n   */\n  add('My first case', () =\u003e {\n    myFunction()\n  }),\n\n  /**\n   * If the code that you want to benchmark requires setup,\n   * you should return it wrapped in a function:\n   */\n  add('My second case', () =\u003e {\n    // Some setup:\n    const testArr = Array.from({ length: 1000 }, (_, index) =\u003e index)\n\n    // Benchmarked code wrapped in a function:\n    return () =\u003e myOtherFunction(testArr)\n  }),\n\n  /**\n   * This benchmark will be skipped:\n   */\n  add.skip('My third case', () =\u003e {\n    1 + 1\n  }),\n\n  /**\n   * This benchmark will be the only one that runs\n   * (unless there are other cases marked by .only)\n   */\n  add.only('My fourth case', () =\u003e {\n    Math.max(1, 2, 3, 4, 5)\n  }),\n\n  /**\n   * This will run after each benchmark in the suite.\n   *\n   * You can pass a function that takes:\n   *   - as a first argument: an object with the current result\n   *   - as a second argument: an object with all cases (even unfinished ones)\n   * If you return a value, it will be logged,\n   * replacing in-place the previous cycle output.\n   *\n   * You can use this function multiple times with different handlers.\n   *\n   * By default, it pretty-prints case results\n   */\n  cycle(),\n\n  /**\n   * This will run after all benchmarks in the suite.\n   *\n   * You can pass a function that takes an object with all results.\n   *\n   * You can use this function multiple times with different handlers.\n   *\n   * By default, it pretty-prints a simple summary.\n   */\n  complete(),\n\n  /**\n   * This will set the config for the whole suite.\n   *\n   * All entries are optional.\n   */\n  configure({\n    /**\n     * Benchmark options for every case.\n     *\n     * Can be overridden by each individual case (as a third parameter to the `add` method).\n     *\n     * See: \"Tweaking benchmarks\" section.\n     */\n    cases: {\n      ...optionsForEveryTestCase,\n    },\n    /**\n     * Other general setting for the whole suite.\n     */\n\n    /**\n     * The minimum precision (decimal places) of the results displayed\n     * by the default `cycle`, `complete` and `save` functions.\n     *\n     * This precision will be automatically increased if needed.\n     *\n     * Default: 0 (no decimal places, unless required to differentiate cases)\n     */\n    minDisplayPrecision: 2,\n  }),\n\n  /**\n   * This will save the results to a file.\n   * You can pass an options object.\n   *\n   * You can use this function multiple times\n   * if you need multiple output files with different options.\n   *\n   * By default saves to benchmark/results/\u003cISO-DATE-TIME\u003e.json\n   */\n  save({\n    /**\n     * String or function that produces a string,\n     * if function, then results object will be passed as argument:\n     */\n    file: 'myFileNameWithoutExtension'\n    /**\n     * Destination folder (can be nested), will be created if not exists:\n     */\n    folder: 'myFolder',\n    /**\n     * Version string - if provided will be included in the file content\n     */\n    version: require('package.json').version,\n    /**\n     * A flag that indicates whether detailed or simplified result will be saved\n     * Default: false (simplified results)\n     */\n    details: true,\n    /**\n     * Output format, currently supported:\n     *   'json' | 'csv' | 'table.html' | 'chart.html'\n     * Default: 'json'\n     */\n    format: 'csv',\n  }),\n)\n```\n\nAll methods are optional - use the ones you need.\n\nAdditionally, each suite returns a `Promise` that resolves with results object (the same as passed to the `complete` method).\n\n\u003ca id=\"many-suites\"\u003e\u003c/a\u003e\n\n## Working with many suites\n\nYou can create as many suites as you want. It is a good practice to define each suite in a separate file, so you can run them independently if you need. To run multiple suites, create the main file, where you import all your suites.\n\nExample:\n\n```js\n/* suites/suite-one.js */\n\nconst b = require('benny')\n\nmodule.exports = b.suite(\n  'Suite one',\n\n  b.add('Reduce two elements', () =\u003e {\n    ;[1, 2].reduce((a, b) =\u003e a + b)\n  }),\n\n  b.add('Reduce five elements', () =\u003e {\n    ;[1, 2, 3, 4, 5].reduce((a, b) =\u003e a + b)\n  }),\n\n  b.cycle(),\n  b.complete(),\n  b.save({ file: 'reduce' }),\n)\n```\n\n```js\n/* suites/suite-two.js */\n\nconst b = require('benny')\n\nmodule.exports = b.suite(\n  'Suite two',\n\n  b.add('Multiple two numbers', () =\u003e {\n    2 * 2\n  }),\n\n  b.add('Multiply three numbers', () =\u003e {\n    2 * 2 * 2\n  }),\n\n  b.cycle(),\n  b.complete(),\n  b.save({ file: 'add' }),\n)\n```\n\n```js\n/* benchmark.js */\n\nrequire('./suites/suite-one')\nrequire('./suites/suite-two')\n```\n\nRun:\n\n```\nnode benchmark.js\n```\n\n### Multiple async suites\n\nIf your suites contain async benchmarks, you should wrap them in a function (so they wont execute immediately), and use await when calling each of them. That way the results of many suites won't interfere with each other.\n\n```js\n/* suites/async-suite-one.js */\n\nconst b = require('benny')\n\nmodule.exports = () =\u003e b.suite(/* ...your async benchmarks */)\n```\n\n```js\n/* suites/async-suite-two.js */\n\nconst b = require('benny')\n\nmodule.exports = () =\u003e b.suite(/* ...your async benchmarks */)\n```\n\n```js\n/* async-benchmark.js */\n\nconst asyncSuite1 = require('./suites/async-suite-one')\nconst asyncSuite2 = require('./suites/async-suite-two')\n\nconst main = async () =\u003e {\n  await asyncSuite1()\n  await asyncSuite2()\n}\n\nmain()\n```\n\nRun:\n\n```\nnode async-benchmark.js\n```\n\n\u003ca id=\"async-code\"\u003e\u003c/a\u003e\n\n## Working with async code\n\nBenny handles Promises out of the box. You can have async benchmarks, async setup, or both.\n\nTo demonstrate how this work, I will use the `delay` function that simulates a long-pending promise:\n\n```js\nconst delay = (seconds) =\u003e\n  new Promise((resolve) =\u003e setTimeout(resolve, seconds * 1000))\n```\n\n### Async benchmark without setup\n\n```js\nadd('Async benchmark without setup', async () =\u003e {\n  // You can use await or return - works the same,\n  // (async function always returns a Promise)\n  await delay(0.5) // Resulting in 2 ops/s\n})\n```\n\nIf a benchmark has many async operations you should await every statement that you want to be completed before the next iteration:\n\n```js\nadd('Async benchmark without setup - many async operations', async () =\u003e {\n  await delay(0.5)\n  await delay(0.5)\n  // Resulting in 1 ops/s\n})\n```\n\n### Async benchmark with setup - return a promise wrapped in a function\n\n```js\nadd('Async benchmark with some setup', async () =\u003e {\n  await delay(2) // Setup can be async, it will not affect the results\n\n  return async () =\u003e {\n    await delay(0.5) // Still 2 ops/s\n  }\n})\n```\n\n### Synchronous benchmark with async setup\n\n```js\nadd('Sync benchmark with some async setup', async () =\u003e {\n  await delay(2) // Setup can be async, it will not affect the results\n\n  return () =\u003e {\n    1 + 1 // High ops, not affected by slow, async setup\n  }\n})\n```\n\nIf we add these cases to a suite and execute it, we will get results that would look similar to this:\n\n```\nRunning \"Async madness\" suite...\n\n  Async benchmark without setup:\n    2 ops/s, ±0.02%             | 100% slower\n\n  Async benchmark without setup - many async operations:\n    1 ops/s, ±0.05%             | slowest, 100% slower\n\n  Async benchmark with some setup:\n    2 ops/s, ±0.11%             | 100% slower\n\n  Sync benchmark with some async setup:\n    674 553 637 ops/s, ±2.13%   | fastest\n\nFinished 4 cases!\n  Fastest: Sync benchmark with some async setup\n  Slowest: Async benchmark without setup - many async operations\n\nSaved to: benchmark/results/async-madness.json\n```\n\n_Note: If you look closely, because of the `async` keyword, the last two examples return not a function, but a Promise, that resolves to a function, that returns either another Promise or other value (undefined in the last case). Benny is smart enough to get your intent and build proper async or sync benchmark for you._\n\n\u003ca id=\"tweaking\"\u003e\u003c/a\u003e\n\n## Tweaking benchmarks\n\nIf the default results are not optimal (high error margin, etc.), you can change parameters for each case by providing an options object as a third parameter to the `add` function.\n\nYou can also provide non-default, general settings for every case under `cases` key in the `configure` function.\n\nPriority of the options:\n\n1. options passed to the `add` function,\n2. options passed to the `configure` function,\n3. library defaults\n\nAvailable options:\n\n```typescript\n/**\n * The delay between test cycles (secs).\n *\n * @default 0.005\n */\ndelay: number\n\n/**\n * The default number of times to execute a test on a benchmark's first cycle.\n *\n * @default 1\n */\ninitCount: number\n\n/**\n * The maximum time a benchmark is allowed to run before finishing (secs).\n *\n * Note: Cycle delays aren't counted toward the maximum time.\n *\n * @default 5\n */\nmaxTime: number\n\n/**\n * The minimum sample size required to perform statistical analysis.\n *\n * @default 5\n */\nminSamples: number\n\n/**\n * The time needed to reduce the percent uncertainty of measurement to 1% (secs).\n *\n * @default 0\n */\nminTime: number\n```\n\nExample usage:\n\n```js\nconst b = require('benny')\n\nconst options = {\n  minSamples: 10,\n  maxTime: 2,\n}\n\nb.suite(\n  'My suite',\n\n  b.add(\n    'Reduce two elements',\n    () =\u003e {\n      ;[1, 2].reduce((a, b) =\u003e a + b)\n    },\n    options,\n  ),\n  // ...other methods\n)\n```\n\n\u003ca id=\"code-reuse\"\u003e\u003c/a\u003e\n\n## Code reuse\n\nYou may wonder why I chose functions over chainable methods - it allows you to better reuse your code while keeping the API minimal and instead leveraging the language itself.\n\n### Reusing benchmark options\n\nIf you have many cases, where default benchmarking options are not optimal, you can decorate the `add` function and use the new function instead, for example:\n\n```js\n/**\n * Let's import and add `function` and rename it to `rawAdd`.\n * You can also import it unchanged, and give the decorating function\n * a different name - that would be a better approach\n * if you want to use a decorated version only in some cases.\n */\nconst { add } = require('benny')\n\nconst customAdd = (caseName, fn) =\u003e\n  add(caseName, fn, {\n    /* custom options */\n  })\n```\n\nYou can now use this new function instead of the original version in your benchmark suite.\n\n_TIP: If you want to provide custom options for every case in the suite, you can use `configure` function instead of this._\n\n### Reusing handlers\n\nIf you use custom handlers for `cycle` and `complete` functions (or you have custom options for `save` function) you can set up them once, and reuse everywhere you need.\n\nFor example:\n\n```js\n/* helpers/handlers.js */\nconst { complete, cycle, save } = require('benny')\n\nconst handlers = (fileName) =\u003e {\n  return [\n    cycle((currentResult, summary) =\u003e {\n      /* your custom cycle handling goes here */\n    }),\n    complete((summary) =\u003e {\n      /* your custom complete handling goes here */\n    }),\n    save({ file: fileName /* other custom save options */ }),\n  ]\n}\n\nmodule.exports = handlers\n```\n\nYou can now reuse it, using the spread operator:\n\n```js\nconst { add, suite } = require('benny')\nconst handlers = require('./helpers/handlers')\n\nmodule.exports = suite(\n  'My suite',\n  add(/* benchmark setup */),\n  add(/* benchmark setup */),\n  add(/* benchmark setup */),\n\n  ...handlers('my-suite'),\n)\n```\n\n### Parameterized cases\n\nThere will often be the case, that you want to run very similar benchmarks, that differ very slightly, and can be parameterized, for example, let's say that we want to check the performance of our code for different size of the array:\n\nInstead of:\n\n```js\nconst { add, cycle, suite } = require('benny')\n\nsuite(\n  'Reduce',\n\n  add('Raw JS 10 elements', () =\u003e {\n    const input = Array.from({ length: 10 }, (_, i) =\u003e i)\n\n    return () =\u003e input.reduce((a, b) =\u003e a + b)\n  }),\n\n  add('Raw JS 1000 elements', () =\u003e {\n    const input = Array.from({ length: 1000 }, (_, i) =\u003e i)\n\n    return () =\u003e input.reduce((a, b) =\u003e a + b)\n  }),\n\n  add('Raw JS 1000000 elements', () =\u003e {\n    const input = Array.from({ length: 1000000 }, (_, i) =\u003e i)\n\n    return () =\u003e input.reduce((a, b) =\u003e a + b)\n  }),\n\n  cycle(),\n)\n```\n\nYou can auto-generate cases like this:\n\n```js\nconst { add, cycle, suite } = require('benny')\n\nconst testManySizes = (...sizes) =\u003e\n  sizes.map((size) =\u003e {\n    return add(`Raw JS ${size} elements`, () =\u003e {\n      const input = Array.from({ length: size }, (_, i) =\u003e i)\n\n      return () =\u003e input.reduce((a, b) =\u003e a + b)\n    })\n  })\n\nmodule.exports = suite(\n  'Reduce',\n\n  ...testManySizes(10, 1000, 1000000),\n\n  cycle(),\n)\n```\n\nSimilarly, you can test many implementations of the same function:\n\nInstead of:\n\n```js\nconst { add, cycle, suite } = require('benny')\nconst A = require('@arrows/array')\nconst R = require('ramda')\nconst _ = require('lodash/fp')\n\nconst input = Array.from({ length: 100 }, (_, i) =\u003e i)\n\nmodule.exports = suite(\n  'Reduce',\n\n  add('Ramda', () =\u003e {\n    R.reduce((a, b) =\u003e a + b, 0)(input)\n  }),\n\n  add('Arrows', () =\u003e {\n    A.reduce((a, b) =\u003e a + b, 0)(input)\n  }),\n\n  add('Lodash', () =\u003e {\n    _.reduce((a, b) =\u003e a + b, 0)(input)\n  }),\n\n  cycle(),\n)\n```\n\nYou can auto-generate cases like this:\n\n```js\nconst { add, cycle, suite } = require('benny')\nconst A = require('@arrows/array')\nconst R = require('ramda')\nconst _ = require('lodash/fp')\n\nconst input = Array.from({ length: 100 }, (_, i) =\u003e i)\n\nconst testManyImplementations = (...cases) =\u003e\n  cases.map(([name, fn]) =\u003e {\n    return add(name, () =\u003e {\n      fn((a, b) =\u003e a + b, 0)(input)\n    })\n  })\n\nmodule.exports = suite(\n  'Reduce',\n\n  ...testManyImplementations(\n    ['Ramda', R.reduce],\n    ['Arrows', A.reduce],\n    ['Lodash', _.reduce],\n  ),\n\n  cycle(),\n)\n```\n\n---\n\nThese are the three most useful techniques - you can combine them together to achieve less repetition in your benchmark code. Just remember to not overuse them - benchmarks, just like tests, should remain straightforward.\n\n\u003ca id=\"snippets\"\u003e\u003c/a\u003e\n\n## Snippets\n\nIf you are using Visual Studio Code or [VSCodium](https://github.com/VSCodium/vscodium), you can use following code snippets -\u003e [click](snippets.json)\n\nTo add them, open `File -\u003e Preferences -\u003e User Snippets`, chose a language (JS, TS or both) and paste additional keys from the snippets file.\n\nYou can see how they work in the demo GIF.\n\n\u003ca id=\"additional-examples\"\u003e\u003c/a\u003e\n\n## Additional examples\n\nFor more examples check out the [/examples](examples) folder.\n\nYou can run all the examples locally if you want. Just remember to run `npm i` or `yarn` in the examples folder first.\n\n\u003ca id=\"license\"\u003e\u003c/a\u003e\n\n## License\n\nProject is under open, non-restrictive [ISC license](LICENSE.md).\n","funding_links":[],"categories":["Repository","TypeScript","Rambda","Commit hooks"],"sub_categories":["Testing","caderek/benny","Server-rendered React"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaderek%2Fbenny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaderek%2Fbenny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaderek%2Fbenny/lists"}