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.testsis renamed toprofile.functionsfunction.description(formerlytest.description) now contains a nice human readable description.function.codeSamplenow contains a short pseudo-code sample of the function under test.- use
function.codeto access the full source code of the function under test. function.keywordscontains keywords associated with the function under test.profile.keywordscontains 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 } = objconst { a = i } = objconst [a, b] = arrconst [a = i, b] = dconst [a, b, ...tail] = dconst a = arr[i]const a = arr[i] || jconst a = obj.bconst a = obj.b || iconst [a, b] = [b, a]const c = b; b = a; a = c
Variable comparison operators.
Profiled operations:
a > ba >= ba < ba <= 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!varisNaN(var)Number.isNaN(var)!isNaN(var)!Number.IsNaN(var)prop in objobj.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 = valObject.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 referencerecursive sumtail recursive sum