{"id":23280976,"url":"https://github.com/nicholaswmin/timerify","last_synced_at":"2026-03-11T05:02:27.669Z","repository":{"id":248689664,"uuid":"829170837","full_name":"nicholaswmin/timerify","owner":"nicholaswmin","description":"tiny performance-testing utility","archived":false,"fork":false,"pushed_at":"2024-07-21T05:45:41.000Z","size":120,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-06T20:56:08.035Z","etag":null,"topics":["performance-measurement","performance-testing","testing"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nicholaswmin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-15T22:47:25.000Z","updated_at":"2024-07-21T05:45:44.000Z","dependencies_parsed_at":"2024-07-19T20:05:09.970Z","dependency_job_id":"78bb8863-1e62-41e5-b346-2e3023bdc31b","html_url":"https://github.com/nicholaswmin/timerify","commit_stats":null,"previous_names":["nicholaswmin/timerify"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/nicholaswmin/timerify","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicholaswmin%2Ftimerify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicholaswmin%2Ftimerify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicholaswmin%2Ftimerify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicholaswmin%2Ftimerify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nicholaswmin","download_url":"https://codeload.github.com/nicholaswmin/timerify/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicholaswmin%2Ftimerify/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30372126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"online","status_checked_at":"2026-03-11T02:00:07.027Z","response_time":84,"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":["performance-measurement","performance-testing","testing"],"created_at":"2024-12-19T23:39:48.896Z","updated_at":"2026-03-11T05:02:27.655Z","avatar_url":"https://github.com/nicholaswmin.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![test-workflow][test-badge]][test-workflow] [![coverage-workflow][coverage-badge]][coverage-report] [![codeql-workflow][codeql-badge]][codeql-workflow] [![size-report][size-badge]][size-report]\n\n# timerify\n\n\u003e like [`performance.timerify()`][perf_timerify] but:\n\u003e - metrics are included in the instrumented function\n\u003e - metrics are in [*milliseconds*][ms] as well as [*nanoseconds*][ns].\n\n## Usage\n\n### Install\n\n```bash\nnpm i @nicholaswmin/timerify\n```\n\n### `timerify(fn)`\n\nInstruments a `function` and returns it.  \nYou then use the instrumented function as usual \u0026 every time it's called,\nthe function durations are logged.\n\n\u003e example: log the `mean` runtime durations of a `fibonacci` function,  \n\u003e computing the [10th fibonacci number][fib]\n\n```js\nimport { timerify } from 'timerify'\n\n// function\nconst fibonacci = n =\u003e n \u003c 1 ? 0 : n \u003c= 2\n  ? 1 : fibonacci(n - 1) + fibonacci(n - 2)\n\n// same function but instrumented\nconst timed_fibonacci = timerify(fibonacci)\n\ntimed_fibonacci(10)  // recorded a run\ntimed_fibonacci(10)  // recorded another\ntimed_fibonacci(10)  // recorded another\n\nconsole.log(timed_fibonacci.stats_ms.count)\n// 3 (times called)\n\nconsole.log(timed_fibonacci.stats_ms.mean)\n// 2.94 (milliseconds, per run, on average)\n```\n\n#### [`Promise`][promise]/[`async`][async] functions\n\nsame as above, just `await` the returned function:\n\n```js\nconst sleep = ms =\u003e new Promise((resolve =\u003e setTimeout(resolve, ms)))\n\nconst timed_sleep = timerify(sleep)\n\nawait timed_sleep(100)\nawait timed_sleep(100)\nawait timed_sleep(100)\n\nconsole.log(timed_sleep.stats_ms.count)\n// 3 (times called)\n\nconsole.log(timed_sleep.stats_ms.mean)\n// 100 (milliseconds)\n```\n\n### Recorded data\n\nTimerified functions contain statistics in:\n\n#### [*nanoseconds (ns)*][ns]\n\n`timerified.stats_ns`\n\n#### [*milliseconds (ms)*][ms]\n\n`timerified.stats_ms`\n\n\nBoth contain the following:\n\n| property      \t| description                                      \t  |\n|---------------\t|----------------------------------------------------\t|\n| `count`       \t| count of function invocations                      \t|\n| `min`         \t| fastest recorded duration                          \t|\n| `mean`        \t| statistical [mean][mean] of all durations          \t|\n| `max`         \t| slowest recorded duration                          \t|\n| `stddev`      \t| [standard deviation][stddev] of all durations      \t|\n| `percentiles` \t| [k-th percentiles][percentiles] of all durations   \t|\n\n\u003e example: log running time of `foo`, in `nanoseconds`:\n\n```js\nconst timed_foo = timerify(foo)\n\ntimed_foo()\ntimed_foo()\ntimed_foo()\n\nconsole.log(timed_foo.stats_ns)\n\n//  count: 3,\n//  min: 3971072,\n//  max: 4030463,\n//  mean: 4002406.4,\n//  exceeds: 0,\n//  stddev: 24349.677891914707,\n//  percentiles: { '75': 4020224, '100': 4028416, '87.5': 4028416 }\n```\n\n\u003e example: same as above, this time in `milliseconds`:\n\n```js\nconst timed_foo = timerify(foo)\n\ntimed_foo()\ntimed_foo()\ntimed_foo()\n\nconsole.log(timed_foo.stats_ms)\n\n//  count: 3,\n//  min: 3.97,\n//  max: 4.03,\n//  mean: 4,\n//  exceeds: 0,\n//  stddev: 0.02,\n//  percentiles: {  '75': 4.02, '100': 4.03, '87.5': 4.03 }\n```\n\n\u003e both are derived from an internal [`perf_hooks: Histogram`][node_hgram].\n\n### `timerified.reset()`\n\nresets recorded stats to zero.\n\n\u003e example: run `foo` 2 times, reset recorded stats to `0` \u0026 continue recording:\n\n```js\nconst timed_foo = timerify(foo)\n\ntimed_foo()\ntimed_foo()\n\nconsole.log(timed_foo.stats_ms.max)\n// 2.01\n\ntimed_foo.reset()\n\nconsole.log(timed_foo.stats_ms.max)\n// 0\n\ntimed_foo()\ntimed_foo()\n\nconsole.log(timed_foo.stats_ms.max)\n// 1.99\n```\n\n### `log([fn,fn...])`\n\nPretty-prints the recorded durations of one or more timerified functions.\n\n\u003e example: pretty-print the stats of `foo` and `bar`:\n\n```js\nimport { timerify, log } from '@nicholaswmin/timerify'\n\nconst foo = () =\u003e new Promise((resolve =\u003e setTimeout(resolve, 5)))\nconst bar = () =\u003e new Promise((resolve =\u003e setTimeout(resolve, 15)))\n\nconst timed_foo = timerify(foo)\nconst timed_bar = timerify(bar)\n\nfor (let i = 0; i \u003c 30; i++)\n  await timed_foo()\n\nfor (let i = 0; i \u003c 50; i++)\n  await timed_bar()\n\nlog([ timed_foo, timed_bar ])\n```\n\nlogs:\n\n```console\n┌────────────────┬───────┬──────────┬───────────┬──────────┬─────────────┐\n│ (index)        │ count │ min (ms) │ mean (ms) │ max (ms) │ stddev (ms) │\n├────────────────┼───────┼──────────┼───────────┼──────────┼─────────────┤\n│ timerified foo │ 30    │ 4.56     │ 5.68      │ 6.25     │ 0.25        │\n│ timerified bar │ 50    │ 15.14    │ 16.04     │ 16.21    │ 0.23        │\n└────────────────┴───────┴──────────┴───────────┴──────────┴─────────────┘\n```\n\n## Usage with test runners\n\nJust assert the result in any test runner using any assertion library\n\n\u003e example: using [node test-runner][node-test]:\n\n\u003e requires Node.js v20+\n\n```js\nimport test from 'node:test'\nimport assert from 'node:assert'\n\nimport { timerify } from '@nicholaswmin/timerify'\n\nconst fibonacci = n =\u003e n \u003c 1 ? 0 : n \u003c= 2\n  ? 1 : fibonacci(n - 1) + fibonacci(n - 2)\n\ntest('perf: #fibonacci(20) x 10 times', async t =\u003e {\n  t.beforeEach(() =\u003e {\n    for (let i = 0; i \u003c 10; i++)\n      timed_fibonacci(20)\n  })\n\n  await t.test('called 10 times', () =\u003e {\n    const callCount = timed_fibonacci.stats_ms.count\n\n    assert.strictEqual(callCount, 10)\n  })\n\n  await t.test('runs quickly, on average', () =\u003e {\n    const mean = timed_fibonacci.stats_ms.mean\n\n    assert.ok(mean \u003c 30, `mean: ${mean} ms exceeded 30ms threshold`)\n  })\n\n  await t.test('has consistent running times', () =\u003e {\n    const dev = timed_fibonacci.stats_ms.stddev\n\n    assert.ok(dev \u003c 2, `deviation: ${dev} ms exceeded 30ms threshold`)\n  })\n})\n```\n\nIn the examples above, I specifically omit testing for the statistical\n`min`/`max`, opting instead for `mean` and `deviation`.\n\nThis is *intentional*. \n\n`min`/`max` times are not useful metrics unless you're\nbuilding a pacemaker or the chronometer that launches the Space Shuttle, in\nwhich case you probably wouldn't be looking at this page.\n\nThey are also very susceptible to environmental events that are outside\nyour control hence they can make your tests [brittle][brittle].\n\nPerformance-testing shouldn't ever be included as part of unit-testing.\nAt best my advice is to keep them around in a CI workflow and have them\nserve as performance regression [canaries][canaries] that you check\nevery now and then.\n\n## Tests\n\nInstall deps\n\n```bash\nnpm ci\n```\n\nRun unit tests\n\n```bash\nnpm test\n```\n\nRun test coverage\n\n```bash\nnpm run test:coverage\n```\n\n## Authors\n\n[@nicholaswmin][nicholaswmin]\n\n## License\n\n[MIT-0 \"No Attribution\" License][license]\n\n\n[test-badge]: https://github.com/nicholaswmin/automap/actions/workflows/test:unit.yml/badge.svg\n[test-workflow]: https://github.com/nicholaswmin/automap/actions/workflows/test:unit.yml\n\n[coverage-badge]: https://coveralls.io/repos/github/nicholaswmin/timerify/badge.svg?branch=main\n[coverage-report]: https://coveralls.io/github/nicholaswmin/timerify?branch=main\n\n[codeql-badge]: https://github.com/nicholaswmin/timerify/actions/workflows/codeql.yml/badge.svg\n[codeql-workflow]: https://github.com/nicholaswmin/timerify/actions/workflows/codeql.yml\n\n[size-report]: https://bundlephobia.com/package/@nicholaswmin/timerify\n[size-badge]: https://img.shields.io/badge/size-950%20bytes-b.svg\n\n[hgram]: https://en.wikipedia.org/wiki/Histogram\n[mean]: https://en.wikipedia.org/wiki/Mean\n[stddev]: https://en.wikipedia.org/wiki/Standard_deviation\n[percentiles]: https://en.wikipedia.org/wiki/Percentile\n\n[perf_hooks]: https://nodejs.org/api/perf_hooks.html\n[node_hgram]: https://nodejs.org/api/perf_hooks.html#class-histogram\n[perf_timerify]: https://nodejs.org/api/perf_hooks.html#performancetimerifyfn-options\n\n[fib]: https://en.wikipedia.org/wiki/Fibonacci_sequence\n[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\n[async]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function\n[console-table]: https://nodejs.org/api/console.html#consoletabletabulardata-properties\n[ns]: https://en.wikipedia.org/wiki/Nanosecond\n[ms]: https://en.wikipedia.org/wiki/Millisecond\n\n[node-test]: https://nodejs.org/api/test.html#test-runner\n[brittle]: https://softwareengineering.stackexchange.com/a/356238/108346\n[indeterminacy]: https://en.wikipedia.org/wiki/Indeterminacy_in_computation\n[canaries]: https://review.gale.com/2020/09/08/canaries-in-the-coal-mine/\n\n[nicholaswmin]: https://github.com/nicholaswmin\n[license]: ./LICENSE\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicholaswmin%2Ftimerify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnicholaswmin%2Ftimerify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicholaswmin%2Ftimerify/lists"}