{"id":18593284,"url":"https://github.com/acromedia/sloth","last_synced_at":"2026-05-03T12:38:37.481Z","repository":{"id":40256464,"uuid":"368385638","full_name":"AcroMedia/sloth","owner":"AcroMedia","description":"Benchmark and profile JavaScript code and functions in isolated environments!","archived":false,"fork":false,"pushed_at":"2022-05-19T23:34:13.000Z","size":387,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-20T06:48:22.056Z","etag":null,"topics":["benchmark","javascript","memory","nodejs","profiling","testing"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@acromedia/sloth","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AcroMedia.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-18T03:07:32.000Z","updated_at":"2022-05-18T16:48:56.000Z","dependencies_parsed_at":"2022-08-22T23:10:09.783Z","dependency_job_id":null,"html_url":"https://github.com/AcroMedia/sloth","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AcroMedia%2Fsloth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AcroMedia%2Fsloth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AcroMedia%2Fsloth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AcroMedia%2Fsloth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AcroMedia","download_url":"https://codeload.github.com/AcroMedia/sloth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254513067,"owners_count":22083501,"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","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":["benchmark","javascript","memory","nodejs","profiling","testing"],"created_at":"2024-11-07T01:11:58.758Z","updated_at":"2026-05-03T12:38:37.434Z","avatar_url":"https://github.com/AcroMedia.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eSloth\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"version\" src=\"https://img.shields.io/github/package-json/v/AcroMedia/sloth\"\u003e\n  \u003cimg alt=\"deps\" src=\"https://img.shields.io/librariesio/github/AcroMedia/sloth\"\u003e\n  \u003cbr /\u003e\n  \u003cimg alt=\"GitHub Workflow Status\" src=\"https://img.shields.io/github/workflow/status/AcroMedia/sloth/main\"\u003e\n  \u003cimg alt=\"GitHub last commit\" src=\"https://img.shields.io/github/last-commit/AcroMedia/sloth?label=updated\"\u003e\n \u003c/p\u003e\n\n# Description\n\nSloth is a Node module created with the intention of allowing easy and versatile memory profiling in NodeJS scripts, projects, and tests.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Documentation](#documentation)\n  - [Using the Profiler class](#using-the-profiler-class)\n  - [Using the bench function](#using-the-bench-function)\n  - [Benchmarking a file](#benchmarking-a-file)\n  - [ProfileResults](#profileresults)\n- [Automated Testing Notes](#automated-testing-notes)\n  - [Jest](#jest)\n\n# Installation\n\n- With npm:\n  ```sh\n  npm install sloth\n  ```\n\n- With yarn:\n  ```sh\n  yarn add sloth\n  ```\n\n# Usage\n\nES6 `import`s:\n```js\nimport * as sloth from 'sloth'\n\n// OR\n\nimport { Profiler, bench } from 'sloth'\n```\n\nUsing `require`s:\n```js\nconst sloth = require('sloth')\n\n// OR\n\nconst { Profiler, bench, benchFile } = require('sloth')\n```\n\nFor more detailed descriptions and code examples, see below:\n\n---\n\n## Documentation\n- [Using the Profiler class](#using-the-profiler-class)\n- [Using the bench function](#using-the-bench-function)\n- [Benchmarking a file](#benchmarking-a-file)\n- [ProfileResults](#profileresults)\n\n## Using the Profiler class\nThe `Profiler` class is provided as a quick and easy way of profiling any application, provided you have it's PID, although it's aimed more towards profiling the application it's created in.\n\n### Creating an instance\n```js\nconst { Profiler } = require('sloth')\nconst profiler = new Profiler(pid, options)\n```\n\nOptions are described below, all are optional:\n\n| Option | Type | Description |\n|--|--|--|\n| toFile | `Boolean` (optional) | Whether to export the results to a file or not |\n| timestep | `Number` (optional) | How long, in milliseconds, the memory monitor will check memory usage |\n| wait | `Number` (optional) | How long, in milliseconds, the profiler should wait before returning results |\n| trimNodeProcessUsage | `Boolean` (optional) | Takes the memory usage of the node process before anything has happened, and removes that from the data. Should allow for yielding more accurate results in most cases |\n\nMethods are described below, all are async:\n\n| Method | Description |\n|--|--|\n| start | Begins the profiling, returns itself |\n| end | Stops profiling, returns instance of `ProfileResults`. See more about `ProfileResults` [here](#profileresults) |\n### Examples\n\nCreating a new Profiler instance that will keep track of it's own process's usage:\n```js\nconst { Profiler } = require('sloth')\nconst profiler = new Profiler(process.pid, {\n  timestep: 100,\n  wait: 1000,\n  // Helps since the profiler will likely skew the results otherwise\n  trimNodeProcessUsage: true\n})\n```\n\nProfiling the creation of a large array:\n```js\nawait profiler.start()\n\n// 100,000,000 zeros\nlet myHugeArr = new Array(1e8).fill(0)\n\nconst results = (await profiler.end()).results\nconsole.log(results)\n```\n\nUsing the Profiler within Jest\n```js\nconst { Profiler } = require('sloth')\n\n// Your Jest test suite\ndescribe('test test', () =\u003e {\n  // To make the tests cleaner, you should probably make them async\n  it ('does something', async () =\u003e {\n    const profiler = new Profiler(process.pid, {\n      timestep: 100,\n      wait: 1000,\n      // This will shave off all of the memory currently\n      // taken up by the Jest test. This way, it'll be more\n      // accurate in case you just did a bunch of crazy\n      // stuff before and didn't clean it all up properly.\n      trimNodeProcessUsage: true\n    })\n\n    await profiler.start()\n\n    // 100,000,000 zeros\n    let myHugeArr = new Array(1e8).fill(0)\n\n    const results = (await profiler.end()).results\n    \n    // Should've taken less than 5 seconds\n    // (I doubt it'd actually take that little time, but it's just an example)\n    expect(results.time_elapsed \u003c 5000).toBeTruthy()\n  })\n})\n```\n\n### Extra Notes\n\nDepending on how large the functionality being profiled is, you may want the keep the [Node garbage collector](https://rollout.io/blog/understanding-garbage-collection-in-node-js/) in mind. Consider the following example:\n\n```js\nawait profiler.start()\n\n// 100,000,000 zeros\nlet myHugeArr = new Array(1e8).fill(0)\nmyHugeArr = null\n\nconst results = (await profiler.end()).results\nconsole.log(results)\n```\n\nWhile the array is set to null, the memory won't actually change unless the garbage collector is run. To change this, run your script with the `--expose-gc` flag, like so:\n\n```sh\nnode --expose-gc index\n```\n\nAnd call the garbage collector:\n```js\nawait profiler.start()\n\n// 100,000,000 zeros\nlet myHugeArr = new Array(1e8).fill(0)\nmyHugeArr = null\n\n// Important:\nglobal.gc()\n\nconst results = (await profiler.end()).results\nconsole.log(results)\n```\n\n## Using the bench function\n\nThe `bench()` function takes a function, throws it into a separate process, runs the profiler on the process, and wraps it all together complete with a bow on top\\*.\n\n\u003csub\u003e* Disclaimer: does not actually provide a bow on top.\u003c/sub\u003e\n\n### Calling bench()\nCalling the `bench()` function is done like so:\n```js\nconst { bench } = require('sloth')\nawait bench(function, arguments, options)\n```\n\nIt will return a `ProfileResults` instance (see more about `ProfileResults` [here](#profileresults)).\n\nFor details on options, see [Using the Profiler class](#using-the-profiler-class), as this function uses the exact same options with one important addition:\n\n| Option | Type | Description |\n|--|--|--|\n| setup | `Function` (optional) | Code to run in the \"global\" scope. Useful for `require()`s and other otherwise globally defined variables |\n| requirements | `Array` (optional) | A nicer alternative to `setup`. Each item should be an object with a `name` (what it's defined as) and `path` (what is actually `require`ed) |\n\n### Examples\n\n\\* FYI: The word \"global\" is often in quotes, as it technically isn't global, but instead in the outer scope of the function.\n\nSetting up a test that measures the creation of a large array:\n```js\nfunction f() {\n  let arr = new Array(1e8).fill(0)\n}\n\n// OR\n\nconst f = () =\u003e {\n  let arr = new Array(1e8).fill(0)\n}\n\nconst results = await bench(f, [], {\n  // Less useful, since the process is basically isolated, but still a good idea\n  trimNodeProcessUsage: true\n})\n```\n\nTesting out an anonymous function:\n```js\nconst results = bench(() =\u003e {\n  console.log('I work!')\n})\n\n// OR\n\nconst results = bench(function() {\n  console.log('I work!')\n})\n```\n\nUsing arguments:\n```js\nfunction log(text) {\n  console.log(text)\n}\n\nconst results = bench(log, ['I work!'])\n\n// OR \n\nconst results = bench((text) =\u003e console.log(text), ['I work!'])\n```\n\nUsing a setup function:\n```js\n// Logging a \"globally\" defined variable\nfunction f() {\n  console.log(myVar)\n}\n\nconst results = await bench(f, [], {\n  setup: () =\u003e {\n    let myVar = 'I work!'\n  }\n})\n```\n```js\n// Using a module imported using require()\nfunction f(data) {\n  fs.writeFileSync('text.txt', data, 'utf8')\n}\n\nconst results = await bench(f, ['I work!'], {\n  setup: () =\u003e {\n    const fs = require('fs')\n  }\n})\n```\n\\* Linters will probably get pretty pissy at your setup functions, given they define variables that aren't used. Just a heads up.\n\nUsing a requirements array\n```js\n// Using a package\nfunction f() {\n  filesystem.writeFileSync('test.txt', 'test')\n}\n\nconst results = await bench(f, [], {\n  requirements: [\n    {\n      // Parsed as `const filesystem = require('fs')`\n      name: 'filesystem',\n      path: 'fs'\n    }\n  ]\n})\n```\n\n### Extra Notes\n\nWhen a thread is spawned, it is automatically run with the `--expose-gc` option and will always run `global.gc()` once everything has completed, but before the profiler is finished. This is to give a better insight on ending memory usage (`end_usage_bytes` in the `results` object.).\n\nObviously there are security implications when it comes to running code in a serialized-to-unserialized way, even if it's in a separate process and you have complete control of the code going in. Be careful as to how and where you use `bench()`, sometimes using the `Profiler` class will be safer.\n\n## Benchmarking a file\n\nThe `benchFile()` function takes the existing `bench()` function and all it's fun function-wrappy goodness, and implements that for an entire file. It allows you to input a path as well as Node *AND* CLI options, which should allow you control over what exactly is run. This also returns an instance of `ProfileResults`.\n\nIt take three arguments:\n\n| Option | Type | Description |\n|--|--|--|\n| path | `String` | The path to the file. This can be relative or absolute, although absolute is much more ideal. |\n| nodeArgs | `Array` (optional) | An array of Node options, like `--expose-gc`. These are options passed to the Node process itself |\n| cliArgs | `Array` (optional) | An array of CLI options. These are options likely found or used in your own code. |\n\n### Examples\n\nBenchmarking a single file\n\n```js\nconst { benchFile } = require('sloth')\n\nawait benchFile('/home/project/myFile.js')\n```\n\nBenchmarking a file and providing some Node arguments\n\n```js\nawait benchFile(__dirname + '/myFile.js/', [\n  '--expose-gc',\n  '--no-warnings'\n])\n```\n\nBenchmarking a file and providing some CLI arguments\n\n```js\nawait benchFile(__dirname + '/myFile.js', [], [\n  '--do-ten-times',\n  '-f',\n  '--silent',\n  '--input-dir=' + getSomeDir()\n])\n```\n\n## ProfileResults\n\nOnce `Profiler.end()` or `bench()` is called, it will return a `ProfileResults` instance. This contains all of the profiling data, as well as some extra functions that should help aid in viewing and understanding the data.\n\nThe values in the `ProfileResults.data` property are outlined below:\n\n| Property | Type | Description |\n|----------|-------------|--|\n| start | `Number` | The timestamp in milliseconds the profiling was started |\n| end | `Number` | The timestamp in milliseconds the profiling finished |\n| time_elapsed | `Number` | The amount of time profiling took, in milliseconds |\n| timestep_ms | `Number` | The amount of milliseconds per memory check |\n| mem_list | `Array` | List of memory values collected |\n| start_usage_bytes | `Number` | The amount of bytes being used before or as profiling began |\n| peak_usage_bytes | `Number` | The largest amount of memory being used at one time |\n| end_usage_bytes | `Number` | The amount of memory being used by the end of the profile |\n| base_process_bytes | `Number` | The amount of memory used by the process without anything having been done |\n\nThere are a few methods provided that should help make sense of some of the data:\n\n| Method | Description |\n|--------|-------------|\n| averageMemoryUsage() | Get average memory usage throughout the whole profile |\n| medianMemoryUsage() | Get middle memory usage, when list is sorted least to greatest or greatest to least |\n| modeMemoryUsage() | Get most frequently occuring memory value |\n| memoryAtElapsed(ms) | Get the amount of memory being used at a certain point in the profile |\n\n### Extra Notes\n\nIf you intend on using this module in any automated testing, keep in mind that profiling may take different amounts of time on different machines. This could possibly skew averages and such, so stick to testing on more concrete values like `peak_usage_bytes`.\n\n## Automated Testing Notes\n\n### Jest\n\nJest coverage, handled by Istanbul, causes any code that is imported to be processed through a coverage watcher, which breaks the `bench` function when trying to bench a function from an outside module.\n\nTo fix `bench` breaking due to the coverage serialization, you will have to disable code coverage for your benchmarking tests. That, or use the `Profiler`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facromedia%2Fsloth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facromedia%2Fsloth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facromedia%2Fsloth/lists"}