Security News
New Proposed CISA Mandate Would Require Critical Infrastructure to Report Ransom Payments Within 24 Hours
CISA has proposed a set of new rules that would require critical infrastructure to report cyber incidents and ransom payments.
jsbenchmark
Advanced tools
JavaScript performance benchmarking
Weekly downloads
Readme
JavaScript performance benchmarking
To install, execute:
npm i jsbenchmark
Then, import into a project as:
import * as JSBench from "jsbenchmark";
This package can benchmark individual tests, or a suite of test cases for comparison.
To benchmark multiple test cases for comparison, add them to a test suite.
Each test is executed per the number of passes, not exceeding the maximum runtime.
Each pass calls the function for the specified number of times. For example, ten passes with one million operations invokes the test function one million times per pass for a total of 10,000,000 calls.
Example test suite: Drop decimal from number
import * as JSBench from "jsbenchmark";
new JSBench.TestSuite({
async: true,
passes: 25,
operations: 1000000,
maxRuntime: 1000,
})
.add("double not operator", () => {
~~Math.PI;
})
.add("or operator", () => {
Math.PI | 0;
})
.add("signed right shift operator", () => {
Math.PI >> 0;
})
.add("parseInt", () => {
parseInt(Math.PI);
})
.add("round", () => {
Math.round(Math.PI);
})
.add("floor", () => {
Math.floor(Math.PI);
})
.add("ceil", () => {
Math.ceil(Math.PI);
})
.add("trunc", () => {
Math.trunc(Math.PI);
})
.add("toFixed", () => {
Math.PI.toFixed();
})
.on("test", (event) => {
const test = event.test;
const resultSet = event.resultSet;
console.log(test.name);
resultSet.log();
})
.on("suite", (event) => {
const result = event.result;
result.log();
})
.run();
Output from above example:
double not operator
min: 6.25000 avg: 8.41969 max: 11.04000 ops/second: 123,911,231
or operator
min: 7.30000 avg: 9.52969 max: 10.75000 ops/second: 107,329,250
signed right shift operator
min: 7.07500 avg: 10.00412 max: 10.79500 ops/second: 101,645,033
parseInt
min: 11.74500 avg: 15.21844 max: 16.36500 ops/second: 66,107,377
round
min: 7.74000 avg: 11.05765 max: 11.87000 ops/second: 91,572,037
floor
min: 7.85000 avg: 10.78206 max: 12.00000 ops/second: 93,988,418
ceil
min: 8.81500 avg: 11.52324 max: 12.36500 ops/second: 87,225,180
trunc
min: 8.92000 avg: 11.53265 max: 12.11500 ops/second: 87,089,952
toFixed
min: 247.60500 avg: 258.02000 max: 264.34000 ops/second: 3,878,004
Fastest: double not operator
Slowest: toFixed
The test is executed for 25 passes, with each pass executing the function 1 million times unless maximum runtime exceeds one second.
Test Suite optional parameters:
Type | Property | Description |
---|---|---|
bool | async | Whether tests should be run asynchronously between passes, providing time for garbage collection |
number | passes | Number of passes to execute each test |
number | operations | Number of operations to execute each pass |
number | maxRuntime | Maximum number of milliseconds to execute each test pass |
There are two methods to add tests:
add()
- Add a test function, which is called once per operationaddManual()
- Add a test function, called only once in totalUsing the add()
function wraps a segment of code to be called once per operation, passing the current operation index into the test function.
Example:
let obj = {};
new JSBench.TestSuite({
async: true,
passes: 25,
operations: 1000000,
maxRuntime: 30000,
})
.add("obj[i] = i", (i) => {
obj[i] = i;
})
Above, the segment of code to be benchmarked is obj[i] = i
, where i
is the current index of the operation.
For 25 passes, the function will be called in a loop 1,000,000 times with i
incrementing from 0 to 999,999.
Each pass is timed, with the result set reporting min, max, and average times. Operations per second are calculated based upon the average time.
Method signature: add(name, fn)
Type | Parameter | Description |
---|---|---|
string | name | Name of the test case |
function | fn | Function to be tested |
One advantage of this method is preventing loop optimizations such as hoisting or no-op that effectively render a test invalid.
Example:
let obj = {};
new JSBench.TestSuite()
.add("obj.hasOwnProperty(i);", (i) => {
obj.hasOwnProperty(i);
})
.add("i in obj", (i) => {
i in obj;
})
In the example above:
hasOwnProperty()
will be invoked and measuredi in obj
is effectively a no-op as its return value is never used; therefore, not measuredAn easy solution is to return the value:
.add("i in obj", (i) => {
return i in obj;
})
Advantages:
Disadvantages:
call()
Instead of using the built-in loop, specify your own test loop using addManual()
.
This enables higher resolution for testing fine-grained operations.
Example:
let n = 0;
new JSBench.TestSuite({
async: true,
passes: 25,
operations: 1000000,
maxRuntime: 30000,
})
.addManual(
"++n (manual)",
(operations) => {
for (let i = 0; i < operations; i++) {
++n;
}
},
1000000
)
Above, the segment of code to be benchmarked (++n
) is so small, greater accuracy of operations per second require manually setting up the test loop.
For 25 passes, the test function is executed only once per pass.
Method signature: addManual(name, fn, operations)
Type | Parameter | Description |
---|---|---|
string | name | Name of the test case |
function | fn | Function to be tested |
number | operations | Number of operations being tested |
Operation count is still important for reporting purposes, used to calculate operations per second.
Inside the test function, the operation count is passed to the function. By default, it will use the test suite's operation count:
new JSBench.TestSuite({
operations: 1000000, // <-- Use default of test suite
})
.addManual("name", (operations) => {
for (let i = 0; i < operations; i++) {}
});
Otherwise, explicitly define the operation count:
new JSBench.TestSuite({
operations: 1000000,
})
.addManual(
"name",
(operations) => {
for (let i = 0; i < operations; i++) {}
},
10000 // <!-- Use parameter
);
Advantages:
Disadvantages:
Single test cases can be executed, per the number of operations.
import * as JSBench from "jsbenchmark"
new JSBench.Test("Log 10", () => {
Math.log10(2)
}, {
operations: 1000000
})
.run()
.log();
The above would output:
total time: 10.31500 ops/second: 96,946,195
The test is executed for one pass, executing the function one million times.
Total time for the test was ~10 milliseconds, resulting in ~97 million operations per second
On each pass of a test, use the pass
event:
.on("pass", (event) => {
const test = event.test;
const result = event.result;
console.log(test.name)
result.log();
})
This returns the test, and test result (TestResult
) for that pass.
On completion of each test, use the test
event:
.on("test", (event) => {
const test = event.test;
const resultSet = event.resultSet;
console.log(test.name)
resultSet.log();
})
This returns the test, and test result set (TestResultSet
) for all passes of the test.
On completion of all tests in the test suite, use the suite
event:
.on("suite", (event) => {
const result = event.result;
result.log();
})
This returns the test suite result (TestSuiteResult
) for all tests in in the test suite.
Finally, on completion, use the complete
event:
.on("complete", () => {
console.log("All tests complete.");
})
In benchmarking, comparison of results is relative within the test suite.
Ultimately the goal is to understand which operation is significantly outside the set - the outlier that is much faster, or much slower.
If the intended purpose is to gain insight into true machine operations per second, manually test by defining your own test loops.
Example:
let n = 0;
new JSBench.TestSuite({
async: true,
passes: 25,
operations: 1000000,
maxRuntime: 30000,
})
.add("++n", () => {
return ++n;
})
.addManual(
"++n (manual)",
(operations) => {
for (let i = 0; i < operations; i++) {
++n;
}
},
1000000
)
.on("test", (event) => {
const test = event.test;
const resultSet = event.resultSet;
console.log(test.name);
resultSet.log();
})
.on("suite", (event) => {
const result = event.result;
result.log();
})
.run();
Output from the above example:
++n
min: 8.15500 avg: 11.28820 max: 12.14500 ops/second: 90,034,364
++n (manual)
min: 2.06500 avg: 3.33000 max: 4.76000 ops/second: 311,779,625
By removing function overhead, the manual operation gives greater insight into actual operations per second.
FAQs
JavaScript performance benchmarking
The npm package jsbenchmark receives a total of 4 weekly downloads. As such, jsbenchmark popularity was classified as not popular.
We found that jsbenchmark demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CISA has proposed a set of new rules that would require critical infrastructure to report cyber incidents and ransom payments.
Security News
Redis is no longer OSS, breaking its explicit commitment to remain under the BSD 3-Clause License forever. This has angered contributors who are now working to fork the software.
Product
Socket AI now enables 'AI detected potential malware' alerts by default, ensuring users benefit from AI-powered state-of-the-art malware detection without needing to opt-in.