Skip to content

JavaScript profiling tool, library and benchmark collection.

License

Notifications You must be signed in to change notification settings

haensl/js-profiler

Repository files navigation

https://js-profiler.com

JavaScript profiling tool and library of profiling modules and benchmarks.

JS-Profiler allows you to compare different techniques, operators and functions regarding execution speed and memory consumption. It reports results either in text or JSON format.

JS-Profiler powers https://js-profiler.com.

NPM

CircleCI

Table of contents

Installation

npm i [-gS] js-profiler

Updates

v2.3.0: A new contributor and a new profile: shallow array copying.

We are happy to welcome Josh Howe as a contributor to JS-Profiler! He added a new profile comparing ways to shallow copy arrays.

Big thank you and shout out to Josh Howe!

Due to updated dependencies, JS-Profiler now requires a minimum Node.js version of 10.12.0.

As of version 2.2.0 js-profiler gathers function timing information via the Performance Hooks API instead of process.hrtime().

New in version 2 & Migration from v1.x.y to v2.x.y

  • profile.tests is renamed to profile.functions
  • function.description (formerly test.description) now contains a nice human readable description.
  • function.codeSample now contains a short pseudo-code sample of the function under test.
  • use function.code to access the full source code of the function under test.
  • function.keywords contains keywords associated with the function under test.
  • profile.keywords contains keywords associated with this profile.

Comparison of a v1 vs. v2 profile object

Version 1.x.y profile object
// v1
{
  "name" : "recursion",
  "description" : "Recursion variations: Calculating sum of array of integers. Profile contains a simple for-loop for reference.",
  "tests" : [
      {
          "description" : "for loop sum for reference",
          "time" : {
              "average" : "1.4923μs",
              "minimum" : "1.0970μs",
              "maximum" : "38.8230μs"
          }
      }, 
      {
          "description" : "recursive sum",
          "time" : {
              "average" : "1080.3024μs",
              "minimum" : "703.3320μs",
              "maximum" : "10215.1650μs"
          }
      }, 
      {
          "description" : "tail recursive sum",
          "time" : {
              "average" : "1041.0375μs",
              "minimum" : "704.2790μs",
              "maximum" : "16476.7110μs"
          }
      }
  ],
  "fastest" : [
      {
          "description" : "for loop sum for reference",
          "time" : {
              "average" : "1.4923μs",
              "minimum" : "1.0970μs",
              "maximum" : "38.8230μs"
          }
      }
  ]
}
Version 2.x.y profile object
// v2
{
  "name": "recursion",
  "description": "Recursion.",
  "keywords": [
    "for",
    "loop",
    "recursion",
    "sum",
    "tail",
    "tailrecursion"
  ],
  "functions": [
    {
      "description": "for loop sum for reference",
      "keywords": [
        "for",
        "loop",
        "sum"
      ],
      "codeSample": "for (...) { sum += d[i] }",
      "code": "(d) => {\n    let sum = 0;\n    for (let i = 0; i < d.length; i++) {\n      sum += d[i];\n    }\n\n    return sum;\n  }",
      "time": {
        "average": "3.8774µs"
      }
    },
    {
      "description": "recursive sum",
      "keywords": [
        "recursion",
        "sum"
      ],
      "codeSample": "const f = (d) => (d && d.length && (d[0] + f(d.slice(1)))) || 0",
      "code": "(d) => (d && d.length && (d[0] + recursiveSum.f(d.slice(1)))) || 0",
      "time": {
        "average": "733.7537µs"
      }
    },
    {
      "description": "tail recursive sum",
      "keywords": [
        "recursion",
        "sum",
        "tail",
        "tailrecursion"
      ],
      "codeSample": "const f = (d, i = 0) => (!d.length && i) || f(d.slice(1), i + d[0])",
      "code": "(d, i = 0) => (!d.length && i)\n    || tailRecursiveSum.f(d.slice(1), i + d[0])",
      "time": {
        "average": "769.7328µs"
      }
    }
  ],
  "fastest": [
    {
      "description": "for loop sum for reference",
      "keywords": [
        "for",
        "loop",
        "sum"
      ],
      "codeSample": "for (...) { sum += d[i] }",
      "code": "(d) => {\n    let sum = 0;\n    for (let i = 0; i < d.length; i++) {\n      sum += d[i];\n    }\n\n    return sum;\n  }",
      "time": {
        "average": "3.8774µs"
      }
    }
  ]
}

Usage

CLI

If installed with the -g flag you can simply run js-profiler from your command line:

Intro

For further information please refer to the CLI documentation and the man page.

Library

// 1. Import the library
const jsProfiler = require('js-profiler');

// 2. Run the profiler
jsProfiler.run()
  .then((report) => {
    console.log(JSON.stringify(report, null, 2));
  });

For configuration options please refer to the Library documentation.

Available performance profiles:

Array concatenation variations: Combining two arrays using different techniques.

Profiled operations:

  • a.concat(b)
  • for (...) { a.push(b[i])}
  • for (...) { b.unshift(a[i])}
  • a.push.apply(a, b)
  • Array.prototype.unshift.apply(b, a)
  • b.reduce((arr, item) => arr.push(item), a)
  • a.reduceRight((arr, item) => arr.unshift(item), b)
  • [...a, ...b]

Array copying variations: creating a new array with the same elements as an existing array.

Profiled operations:

  • a.slice()
  • [...a]
  • Array.from(a)
  • new Array(...a)
  • a.concat([])
  • [].concat(a)
  • Array.prototype.unshift.apply([], a)
  • Array.prototype.unshift.apply(new Arrray(), a)
  • [].push(...a)
  • (new Array()).push(...a)
  • b = []; for(...){ b.push(a[i]) }
  • b = new Array(); for(...){ b.push(a[i]) }
  • b = new Array(a.length); for(...){ b[i] = a[i] }

(De-)Composition: composing objects, arrays and variables from each other.

Profiled operations:

  • const { a, b } = obj
  • const { a = i } = obj
  • const [a, b] = arr
  • const [a = i, b] = d
  • const [a, b, ...tail] = d
  • const a = arr[i]
  • const a = arr[i] || j
  • const a = obj.b
  • const a = obj.b || i
  • const [a, b] = [b, a]
  • const c = b; b = a; a = c

Variable comparison operators.

Profiled operations:

  • a > b
  • a >= b
  • a < b
  • a <= b
  • ==
  • ===
  • !=
  • !==
  • &&
  • ||

Variable guards: checking whether a variable is defined or of a certain type.

Profiled operations:

  • typeof !== 'undefined'
  • typeof != 'undefined'
  • typeof === 'function'
  • typeof == 'function'
  • typeof === 'number'
  • typeof == 'number'
  • typeof === 'object'
  • typeof == 'object'
  • typeof === 'string'
  • typeof == 'string'
  • Array.isArray
  • !!var
  • !var
  • isNaN(var)
  • Number.isNaN(var)
  • !isNaN(var)
  • !Number.IsNaN(var)
  • prop in obj
  • obj.hasOwnProperty(prop)
  • Object.prototype.hasOwnProperty.call(obj, prop)

Loop variations: Converting an array of integers into an array of booleans satisfying a conjunction of two simple relational operations.

Profiled operations:

  • [].forEach() => []
  • for(i++, i < d.length) => []
  • for(i++, i < len) => []
  • while(i--) => []
  • [].map() => []
  • while(i < d.length) => []
  • while(i < len) => []
  • do { } while (i < d.length)
  • do { } while (i < len)
  • for (prop of [])

Object literal vs. Map: retrieving values.

Profiled operations:

  • Map.get()
  • {}.prop

Object literal vs. Map: creating a map.

Profiled operations:

  • Map.set()
  • new Map([props])
  • {}.prop = val
  • Object.defineProperty({}, prop, desc)
  • Object.defineProperties({}, props)
  • { ...props }

Object iteration: different ways of iterating over properties of an object and concatenating property names into a single string.

Profiled operations:

  • for (const prop in obj) {}
  • Object.keys(obj).forEach()
  • Object.entries(obj).forEach()
  • for (prop of Map.keys())
  • for (prop of Object.keys(obj))
  • for (prop of Object.keys(obj) { obj.hasOwnProperty(prop) && ... })
  • for (prop of Object.getOwnPropertyNames(obj))
  • Object.getOwnPropertyNames(obj).forEach()

Recurstion variations: Calculating sum of array of integers. Profile contains a simple for-loop for reference.

Profiled operations:

  • for loop sum for reference
  • recursive sum
  • tail recursive sum