Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

benchmarkify

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

benchmarkify - npm Package Compare versions

Comparing version 3.0.0 to 4.0.0

src/benchmarkify.js

3

.eslintrc.js

@@ -13,3 +13,4 @@ module.exports = {

"parserOptions": {
"sourceType": "module"
"sourceType": "module",
"ecmaVersion": "2020"
},

@@ -16,0 +17,0 @@ "rules": {

@@ -1,664 +0,3 @@

const _ = require("lodash");
const kleur = require("kleur");
const humanize = require("tiny-human-time");
"use strict";
const ora = require("ora");
/**
* Formatting number
*
* @param {any} value Number value
* @param {number} [decimals=0] Count of decimals
* @param {boolean} [sign=false] Put '+' sign if the number is positive
* @returns
*/
function formatNumber(value, decimals = 0, sign = false) {
let res = Number(value.toFixed(decimals)).toLocaleString();
if (sign && value > 0.0) res = "+" + res;
return res;
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Test case class
*
* @class TestCase
*/
class TestCase {
/**
* Creates an instance of TestCase.
*
* @param {Suite} suite
* @param {String} name
* @param {Function} fn
* @param {Object} opts
*
* @memberOf TestCase
*/
constructor(suite, name, fn, opts) {
this.suite = suite;
this.name = name;
this.fn = fn;
this.async = fn.length > 0;
this.opts = opts || {};
this.skip = false;
this.done = false;
this.running = false;
this.time = this.opts.time || this.suite.time || 5000;
this.cycles = this.opts.cycles || this.suite.cycles || 1000;
this.minSamples = this.opts.minSamples || this.suite.minSamples || 5;
this.timer = null;
this.startTime = null;
this.startHrTime = null;
this.stat = {
duration: null,
cycle: 0,
count: 0,
avg: null,
rps: null
};
}
/**
*
*
* @returns
*
* @memberOf TestCase
*/
run() {
const self = this;
return new Promise((resolve, reject) => {
// Start test
self.start();
// Run
if (self.async) {
self.cyclingAsyncCb(resolve, reject);
} else {
self.cycling(resolve);
}
});
}
/**
*
*
*
* @memberOf TestCase
*/
start() {
this.running = true;
this.stat.count = 0;
this.startTime = Date.now();
this.startHrTime = process.hrtime();
}
/**
*
*
*
* @memberOf TestCase
*/
finish() {
const diff = process.hrtime(this.startHrTime);
const count = this.stat.count;
const duration = diff[0] + diff[1] / 1e9;
_.assign(this.stat, {
duration,
avg: duration / count,
rps: count / duration
});
this.done = true;
this.running = false;
}
/**
*
*
* @param {any} resolve
*
* @memberOf TestCase
*/
cycling(resolve) {
if (Date.now() - this.startTime < this.time || this.stat.count < this.minSamples) {
for (let i = 0; i < this.cycles; i++) {
this.fn();
this.stat.count++;
}
this.stat.cycle++;
setImmediate(() => this.cycling(resolve));
} else {
this.finish();
resolve(this);
}
}
/**
*
*
* @param {any} resolve
*
* @memberOf TestCase
*
cyclingAsync(resolve, reject) {
const self = this;
const fn = self.fn;
let c = 0;
function cycle() {
return fn().then(() => {
self.stat.count++;
c++;
if (c >= self.cycles) {
if (Date.now() - self.startTime < self.time || self.stat.count < self.minSamples) {
c = 0;
return new Promise(resolve => {
setImmediate(() => resolve(cycle()));
});
}
} else {
return cycle();
}
});
}
return cycle()
.then(() => {
self.finish();
resolve(self);
}).catch(reject);
}*/
/**
*
*
* @param {any} resolve
*
* @memberOf TestCase
*/
cyclingAsyncCb(resolve) {
const self = this;
const fn = self.fn;
let c = 0;
function cycle() {
fn(function () {
self.stat.count++;
c++;
if (c >= self.cycles) {
if (
Date.now() - self.startTime < self.time ||
self.stat.count < self.minSamples
) {
// Wait for new cycle
c = 0;
setImmediate(() => cycle());
} else {
// Finished
self.finish();
resolve(self);
}
} else {
// Next call
cycle();
}
});
}
// Start
cycle();
}
}
/**
*
*
* @class Suite
*/
class Suite {
/**
* Creates an instance of Suite.
* @param {Benchmarkify} parent
* @param {String} name
* @param {Object} opts
*
* @memberOf Suite
*/
constructor(parent, name, opts = {}) {
this.parent = parent;
this.name = name;
this.description = opts.description;
this.meta = opts.meta || {};
this.logger = this.parent.logger;
this.onlyTest = null;
this.done = false;
this.running = false;
this.locals = {};
this.tests = [];
_.assign(
this,
{
time: 5000,
minSamples: 0
},
opts
);
if (!this.cycles) this.cycles = this.minSamples > 0 ? this.minSamples : 1000;
}
/**
* Add a "setup" function to be run before test.
*
* @param {Function} fn
*/
setup(fn) {
this.setupFn = fn;
return this;
}
/**
* Add a "tearDown" function to be run after test.
*
* @param {Function} fn
*/
tearDown(fn) {
this.tearDownFn = fn;
return this;
}
/**
*
*
* @param {any} name
* @param {any} fn
* @param {any} opts
* @returns
*
* @memberOf Suite
*/
appendTest(name, fn, opts) {
const test = new TestCase(this, name, fn, opts);
this.tests.push(test);
return test;
}
/**
*
*
* @param {any} name
* @param {any} fn
* @param {any} [opts={}]
* @returns
*
* @memberOf Suite
*/
add(name, fn, opts = {}) {
this.appendTest(name, fn, opts);
return this;
}
/**
*
*
* @param {any} name
* @param {any} fn
* @param {any} [opts={}]
* @returns
*
* @memberOf Suite
*/
only(name, fn, opts = {}) {
this.onlyTest = this.appendTest(name, fn, opts);
return this;
}
/**
*
*
* @param {any} name
* @param {any} fn
* @param {any} [opts={}]
* @returns
*
* @memberOf Suite
*/
skip(name, fn, opts = {}) {
const test = this.appendTest(name, fn, opts);
test.skip = true;
return this;
}
/**
*
*
* @param {any} name
* @param {any} fn
* @param {any} [opts={}]
* @returns
*
* @memberOf Suite
*/
ref(name, fn, opts = {}) {
const test = this.appendTest(name, fn, opts);
test.reference = true;
return this;
}
/**
*
*
* @returns
*
* @memberOf Suite
*/
run() {
let self = this;
self.maxTitleLength =
this.tests.reduce((max, test) => Math.max(max, test.name.length), 0) + 2;
if (this.onlyTest) {
this.tests.forEach(test => (test.skip = test !== this.onlyTest));
}
return Promise.resolve()
.then(() => {
if (_.isFunction(self.setupFn)) return self.setupFn.call(self);
else if (Array.isArray(self.setupFn))
return Promise.all(self.setupFn.map(fn => fn.call(self)));
})
.then(() => {
return new Promise(resolve => {
self.running = true;
self.logger.log(kleur.magenta().bold(`Suite: ${self.name}`));
this.runTest(Array.from(this.tests), resolve);
});
})
.then(() => {
if (_.isFunction(self.tearDownFn)) return self.tearDownFn.call(self);
else if (Array.isArray(self.tearDownFn))
return Promise.all(self.tearDownFn.map(fn => fn.call(self)));
})
.then(() => {
if (self.parent.spinner) self.parent.spinner.stop();
self.logger.log("");
// Generate results
return self.calculateResult();
});
}
/**
*
*
* @param {any} list
* @param {any} resolve
* @returns
*
* @memberOf Suite
*/
runTest(list, resolve) {
const self = this;
const test = list.shift();
function printAndRun(type, msg, err) {
if (self.parent.spinner) self.parent.spinner[type](msg);
else self.logger.log("››", msg);
if (err) self.logger.error(err);
return list.length > 0 ? self.runTest(list, resolve) : resolve();
}
if (test.skip) {
// Skip test
return printAndRun("warn", kleur.yellow(`[SKIP] ${test.name}`));
}
if (this.parent.spinner) {
// Refresh spinner
self.parent.spinner.text = `Running '${test.name}'...`;
self.parent.spinner.start();
}
// Run test
return test
.run()
.then(() => delay(200))
.then(() => {
const flag = test.async ? "*" : "";
let msg =
_.padEnd(test.name + flag, self.maxTitleLength) +
_.padStart(formatNumber(test.stat.rps) + " rps", 20);
return printAndRun("succeed", msg);
})
.catch(err => {
test.error = err;
return printAndRun("fail", kleur.red("[ERR] " + test.name), err);
});
}
/**
*
*
* @returns
*
* @memberOf Suite
*/
calculateResult() {
let maxRps = 0;
let maxTitleLength = 0;
let fastest = null;
let reference = null;
this.tests.forEach(test => {
if (test.skip) return;
if (test.reference) reference = test;
if (test.stat.rps > maxRps) {
maxRps = test.stat.rps;
fastest = test;
}
if (test.name.length > maxTitleLength) maxTitleLength = test.name.length;
});
//this.tests.sort((a, b) => b.stat.rps - a.stat.rps);
let pe = _.padEnd;
let ps = _.padStart;
this.tests.forEach(test => {
if (test.skip) {
this.logger.log(kleur.yellow(` ${test.name} (skipped)`));
return;
}
if (test.error) {
this.logger.log(kleur.red(` ${test.name} (error: ${test.error.message})`));
return;
}
const baseRps = reference ? reference.stat.rps : fastest.stat.rps;
const c = test == fastest ? kleur.green() : kleur.cyan();
test.stat.percent = (test.stat.rps / baseRps) * 100 - 100;
let flag = test.async ? "*" : "";
if (test == reference) flag += " (#)";
let line = [
" ",
pe(test.name + flag, maxTitleLength + 5),
ps(formatNumber(test.stat.percent, 2, true) + "%", 8),
ps(" (" + formatNumber(test.stat.rps) + " rps)", 20),
" (avg: " + humanize.short(test.stat.avg * 1000) + ")"
];
this.logger.log(c.bold(line.join(" ")));
});
this.logger.log(
"-----------------------------------------------------------------------\n"
);
// Generate result to return
const result = this.tests.map(test => {
let item = {
name: test.name,
meta: test.meta || {}
};
if (test === fastest) item.fastest = true;
if (test.reference) item.reference = true;
if (test.error) item.error = test.error.toString();
if (!test.skip) item.stat = test.stat;
else item.skipped = true;
return item;
});
return result;
}
}
/**
*
*
* @class Benchmarkify
*/
class Benchmarkify {
/**
* Creates an instance of Benchmarkify.
* @param {any} name
* @param {any} logger
*
* @memberOf Benchmarkify
*/
constructor(name, opts = {}) {
this.name = name;
this.description = opts.description;
this.meta = opts.meta || {};
this.logger = opts.logger || console;
if (opts.spinner !== false) {
this.spinner = ora({
text: "Running benchmark...",
spinner: {
interval: 400,
frames: [". ", ".. ", "...", " ..", " .", " "]
}
});
}
this.Promise = Promise;
this.suites = [];
}
/**
*
*
*
* @memberOf Benchmarkify
*/
printPlatformInfo() {
require("./platform")(this.logger);
this.logger.log("");
}
/**
*
*
* @param {boolean} [platformInfo=true]
*
* @memberOf Benchmarkify
*/
printHeader(platformInfo = true) {
let title = " " + this.name + " ";
let lines = "=".repeat(title.length);
this.logger.log(kleur.yellow().bold(lines));
this.logger.log(kleur.yellow().bold(title));
this.logger.log(kleur.yellow().bold(lines));
this.logger.log("");
if (platformInfo) this.printPlatformInfo();
return this;
}
/**
*
*
* @param {String} name
* @param {any} opts
* @returns
*
* @memberOf Benchmarkify
*/
createSuite(name, opts) {
const suite = new Suite(this, name, opts);
this.suites.push(suite);
return suite;
}
/**
*
*
* @param {any} suites
* @returns
*
* @memberOf Benchmarkify
*/
run(suites) {
const self = this;
let list = Array.from(suites || this.suites);
let results = [];
let start = Date.now();
/**
*
*
* @param {any} suite
* @returns
*/
function run(suite) {
return suite.run().then(res => {
results.push({
name: suite.name,
description: suite.description,
meta: suite.meta,
tests: res
});
if (list.length > 0) return run(list.shift());
return {
name: self.name,
description: self.description,
meta: self.meta,
suites: results,
timestamp: Date.now(),
generated: new Date().toString(),
elapsedMs: Date.now() - start
};
});
}
return run(list.shift());
}
}
module.exports = Benchmarkify;
module.exports = require("./src/benchmarkify");
{
"name": "benchmarkify",
"version": "3.0.0",
"version": "4.0.0",
"description": "Benchmark runner for NodeJS",

@@ -22,5 +22,6 @@ "main": "index.js",

"dependencies": {
"kleur": "^4.1.4",
"kleur": "^4.1.5",
"lodash": "^4.17.21",
"ora": "^5.4.1",
"qs": "^6.11.2",
"tiny-human-time": "^1.2.0"

@@ -33,4 +34,4 @@ },

"engines": {
"node": ">=10.0.0"
"node": ">=12.0.0"
}
}
# :zap: benchmarkify
Benchmark framework for NodeJS for measure the execution time of JS codes.
Benchmark framework for Node.js for measure the execution time of JS codes. It can generate JSON result, chart image or draw a simple bar chart to the console.
# Installation
```
$ npm install benchmarkify --save-dev
$ npm i benchmarkify
```

@@ -18,20 +18,22 @@

// information from the OS/PC to the console.
const benchmark = new Benchmarkify("Simple example").printHeader();
const benchmark = new Benchmarkify("Simple example", { description: "This is a common benchmark", chartImage: true }).printHeader();
let i = 0;
// Create a test suite
const bench1 = benchmark.createSuite("Increment integer");
benchmark.createSuite("String concatenate", { time: 1000, description: "Concatenate string in different ways" })
// Add first func
bench1.add("Increment with ++", () => {
i++;
});
.add("Concat with '+'", () => {
let s = "";
for (let i = 0; i < 1000; i++)
s += "test" + i;
return s;
})
// Add second func. This result will be the reference
bench1.ref("Increment with i + 1", () => {
i = i + 1;
});
.ref("Concat with array & join", () => {
let s = [];
for (let i = 0; i < 1000; i++)
s.push("test" + i);
return s.join();
});
bench1.run();
benchmark.run();
```

@@ -47,51 +49,86 @@

==============
Windows_NT 6.1.7601 x64
Node.JS: 6.10.0
V8: 5.1.281.93
Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz × 8
Windows_NT 10.0.19045 x64
Node.JS: 18.16.0
V8: 10.2.154.26-node.26
CPU: 13th Gen Intel(R) Core(TM) i5-13500 × 20
Memory: 32 GB
Suite: Increment integer
√ Increment with ++ 98,878,885 rps
√ Increment with i + 1 89,930,539 rps
Suite: String concatenate
=========================
Increment with ++ +9.95% (98,878,885 rps) (avg: 10ns)
Increment with i + 1 (#) 0% (89,930,539 rps) (avg: 11ns)
√ Concat with '+' 105 533 ops/sec
√ Concat with array & join 57 987 ops/sec
Concat with '+' +81,99% (105 533 ops/sec) (avg: 9μs)
Concat with array & join (#) 0% (57 987 ops/sec) (avg: 17μs)
┌──────────────────────────┬────────────────────────────────────────────────────┐
│ Concat with '+' │ ██████████████████████████████████████████████████ │
├──────────────────────────┼────────────────────────────────────────────────────┤
│ Concat with array & join │ ███████████████████████████ │
└──────────────────────────┴────────────────────────────────────────────────────┘
Chart: https://image-charts.com/chart.js/2.8.0?bkg=white&c=%7B%22type%22%3A%22bar%22%2C%22data%22%3A%7B%22labels%22%3A%5B%22Concat%20with%20%27%2B%27%22%2C%22Concat%20with%20array%20%26%20join%22%5D%2C%22datasets%22%3A%5B%7B%22label%22%3A%22Dataset%201%22%2C%22backgroundColor%22%3A%22rgba%2854%2C%20162%2C%20235%2C%200.5%29%22%2C%22borderColor%22%3A%22rgb%2854%2C%20162%2C%20235%29%22%2C%22borderWidth%22%3A1%2C%22data%22%3A%5B105532.65917212216%2C57986.883366982394%5D%7D%5D%7D%2C%22options%22%3A%7B%22responsive%22%3Afalse%2C%22legend%22%3A%7B%22display%22%3Afalse%2C%22position%22%3A%22top%22%7D%2C%22title%22%3A%7B%22display%22%3Atrue%2C%22text%22%3A%22String%20concatenate%7C%28ops%2Fsec%29%22%7D%2C%22layout%22%3A%7B%22padding%22%3A20%7D%7D%7D
-----------------------------------------------------------------------
```
**Example chart image**
![Example chart image](https://image-charts.com/chart.js/2.8.0?bkg=white&c=%7B%22type%22%3A%22bar%22%2C%22data%22%3A%7B%22labels%22%3A%5B%22Concat%20with%20%27%2B%27%22%2C%22Concat%20with%20array%20%26%20join%22%5D%2C%22datasets%22%3A%5B%7B%22label%22%3A%22Dataset%201%22%2C%22backgroundColor%22%3A%22rgba%2854%2C%20162%2C%20235%2C%200.5%29%22%2C%22borderColor%22%3A%22rgb%2854%2C%20162%2C%20235%29%22%2C%22borderWidth%22%3A1%2C%22data%22%3A%5B105320.73392654078%2C57369.423976363796%5D%7D%5D%7D%2C%22options%22%3A%7B%22responsive%22%3Afalse%2C%22legend%22%3A%7B%22display%22%3Afalse%2C%22position%22%3A%22top%22%7D%2C%22title%22%3A%7B%22display%22%3Atrue%2C%22text%22%3A%22String%20concatenate%7C%28ops%2Fsec%29%22%7D%2C%22layout%22%3A%7B%22padding%22%3A20%7D%7D%7D)
**JSON result**
If you need the results in JSON use `.then` after `run()`
```js
bench1.run().then(res => console.log(res));
benchmark.run().then(res => console.log(res));
```
Result on console:
**Result on the console:**
```js
[
{
name: 'Increment with ++',
fastest: true,
stat: {
duration: 4.999651845,
cycle: 492086,
count: 492086000,
avg: 1.0160118038310376e-8,
rps: 98424053.36525989,
percent: 9.95071720945748
}
},
{
name: 'Increment with i + 1',
reference: true,
stat: {
duration: 4.999535403,
cycle: 447541,
count: 447541000,
avg: 1.117112265244972e-8,
rps: 89516517.82112603,
percent: 0
}
}
]
{
name: 'Simple example',
description: 'This is a common benchmark',
meta: {},
suites: [
{
name: 'String concatenate',
description: 'Concatenate string in different ways',
meta: {},
unit: 'ops/sec',
tests: [
{
name: "Concat with '+'",
meta: {},
unit: 'ops/sec',
fastest: true,
stat: {
duration: 1.0064495,
cycle: 106,
count: 106000,
avg: 0.000009494806603773585,
rps: 105320.73392654078,
percent: 83.58339098878338
}
},
{
name: 'Concat with array & join',
meta: {},
unit: 'ops/sec',
reference: true,
stat: {
duration: 1.0109915,
cycle: 58,
count: 58000,
avg: 0.000017430887931034482,
rps: 57369.423976363796,
percent: 0
}
}
]
}
],
timestamp: 1693594301782,
generated: 'Fri Sep 01 2023 20:51:41 GMT+0200 (közép-európai nyári idő)',
elapsedMs: 2466
}
```

@@ -113,2 +150,4 @@

* `meta` - To store any meta information. Result JSON contains it.
* `chartImage` - Generate chart image url and print to the console after every suite.
* `drawChart` - Draw a bar chart to the console after every suite. Default: `true`

@@ -130,2 +169,3 @@ ### Methods

* `description` - Custom description field.
* `unit` - Measurement unit. Default: `"ops/sec"`.
* `meta` - To store any meta information. Result JSON contains it.

@@ -147,3 +187,3 @@

bench.add("Async call test", done => {
asyncFunction(data).then(() => done());
asyncFunction(data).then(() => done());
});

@@ -156,4 +196,4 @@ ```

bench.add("Async call test", async done => {
await asyncFunction(data)
done();
await asyncFunction(data)
done();
});

@@ -167,4 +207,4 @@ ```

Copyright (C) 2021 Icebob
Copyright (C) 2023 Icebob
[![@icebob](https://img.shields.io/badge/github-icebob-green.svg)](https://github.com/icebob) [![@icebob](https://img.shields.io/badge/twitter-Icebobcsi-blue.svg)](https://twitter.com/Icebobcsi)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc