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.
- Installation
- Updates
- Usage
- Profiles
- array concatenation
- array copying
- comparison operators
- (de-)composition
- guards
- loops
- map access
- map creation
- object iteration
- recursion
- Documentation
- Changelog
- License
npm i [-gS] js-profiler
v2.5.0: New profile: (de-)composition.
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.
v2.2.0: Migrate to Node.js Performance Hooks
As of version 2.2.0 js-profiler gathers function timing information via the Performance Hooks API instead of process.hrtime().
profile.tests
is renamed toprofile.functions
function.description
(formerlytest.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.
// 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"
}
}
]
}
// 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"
}
}
]
}
If installed with the -g
flag you can simply run js-profiler from your command line:
For further information please refer to the CLI documentation and the man page.
// 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.
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