Interesting benchmarks for writing performance-sensitive javascript. Benchmark's here can be used with node's official benchmark.js scripts. The last number is the rate of operations measured in ops/sec (higher is better).
note: node's common.js benchmark file uses one or two commonjs-specific things and those must be updated before the benchmark will run successfully
Checking for primitives, use strict equality
primitives-regexp-test-vs-strict-equality
import common from '../node-v22.9.0/benchmark/common.js'
const TYPEREGEXPLOOSE = 'regexp loose'
const TYPEREGEXPSTRICT = 'regexp strict'
const TYPEEQUALITY = 'equality'
const bench = common.createBenchmark(main, {
n: [2000],
mod: 3,
type: [
TYPEREGEXPLOOSE,
TYPEREGEXPSTRICT,
TYPEEQUALITY
]
})
async function main(conf) {
let obj = ''
const type = conf.type
const mod = conf.mod
const regexpLooseRe = /string|boolean|number/
const regexpStrictRe = /^(string|boolean|number)$/
if (type === TYPEREGEXPLOOSE) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = true
else if (n === 2) n = i
else n = 'value' + i
obj += regexpLooseRe.test(n)
}
bench.end(conf.n)
}
if (type === TYPEREGEXPSTRICT) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = true
else if (n === 2) n = i
else n = 'value' + i
obj += regexpStrictRe.test(typeof n)
}
bench.end(conf.n)
}
if (type === TYPEEQUALITY) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = true
else if (n === 2) n = i
else n = 'value' + i
const t = typeof n
obj += (t === 'string' || t === 'boolean' || t === 'number')
}
bench.end(conf.n)
}
return obj
}
const isPrimitiveEquality = t =>
t === 'string' || t === 'boolean'
|| t === 'number'
const isPrimitiveRegExpLoose = t =>
/string|boolean|number/.test(t)
const isPrimitiveRegExpStrict = t =>
/^(string|boolean|number)$/.test(t)
Test |
Ops/sec |
is primitive equality |
5,455,715.95 |
is primitive RegExp, loose |
3,841,802.27 |
is primitive RegExp, strict |
3,429,425.85 |
Checking for plain object, use loose strict equality
object-loose-test-vs-strict-equality
import common from '../node-v22.9.0/benchmark/common.js'
const TYPEOBJLOOSE = 'object loose'
const TYPEOBJSTRICT = 'object strict'
const TYPEOBJPROTO = 'object proto'
const bench = common.createBenchmark(main, {
n: [2000],
mod: 3,
type: [
TYPEOBJLOOSE,
TYPEOBJSTRICT,
TYPEOBJPROTO
]
})
async function main(conf) {
let obj = ''
const getproto = Object.getPrototypeOf
const proto = Object.getPrototypeOf({})
const type = conf.type
const mod = conf.mod
if (type === TYPEOBJLOOSE) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = { anobj: true }
else if (n === 2) n = ['array']
else n = 'value' + i
obj += n && typeof n === 'object'
&& !Array.isArray(n)
}
bench.end(conf.n)
}
if (type === TYPEOBJSTRICT) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = { anobj: true }
else if (n === 2) n = ['array']
else n = 'value' + i
obj += n && typeof n === 'object'
&& !Array.isArray(n)
&& n instanceof Date === false
&& n instanceof RegExp === false
}
bench.end(conf.n)
}
if (type === TYPEOBJPROTO) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = { anobj: true }
else if (n === 2) n = ['array']
else n = 'value' + i
obj += (getproto(n) === proto)
}
bench.end(conf.n)
}
return obj
}
const isObjLoose = n => n
&& typeof n === 'object'
&& !Array.isArray(n)
const isObjStrict = n => isObjLoose(n)
&& n instanceof Date === false
&& n instanceof RegExp === false
const isObjProto = ((getp, p = getp({})) =>
obj => obj && (getp(obj) === p)
)(Object.getPrototypeOf)
const isObjProtoStr = ((toString, str = toString.({})) =>
obj => obj && (toString(obj) === str)
)(Object.prototype.toString)
Test |
Ops/sec |
is object, loose |
2,709,289.20 |
is object, strict |
2,751,311.69 |
is object, proto |
2,631,139.27 |
is object, proto str |
2,313,421.13 |
Replacing a matched end character, use endsWith
remove-endslash-regexp-vs-char
import common from '../node-v22.9.0/benchmark/common.js'
const TYPEREGEXP = 'regexp'
const TYPEENDSWITH = 'endsWith'
const TYPECHARAT = 'charAt'
const bench = common.createBenchmark(main, {
n: [2000],
mod: 3,
type: [
TYPEREGEXP,
TYPEENDSWITH,
TYPECHARAT
]
})
async function main(conf) {
let obj = ''
const type = conf.type
const mod = conf.mod
if (type === TYPEREGEXP) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = 'stringwith/'
else if (n === 2) n = 'stringwithout'
else n = 'value' + i
obj += n.replace(/\/$/, '')
}
bench.end(conf.n)
}
if (type === TYPECHARAT) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = 'stringwith/'
else if (n === 2) n = 'stringwithout'
else n = 'value' + i
obj += n.endsWith('/') ? n.slice(0, -1) : n
}
bench.end(conf.n)
}
if (type === TYPEENDSWITH) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = 'stringwith/'
else if (n === 2) n = 'stringwithout'
else n = 'value' + i
obj += n.charCodeAt(n.length - 1) === 47 ? n.slice(0, -1) : n
}
bench.end(conf.n)
}
return obj
}
const removeEndSlashRegExp = n =>
n.replace(/\/$/, '')
const removeEndSlashEndsWith = n =>
n.endsWith('/') ? n.slice(0, -1) : n
const removeEndSlashCharAt = n =>
n.charCodeAt(n.length - 1) === 47
? n.slice(0, -1) : n
Test |
Ops/sec |
remove end slash, RegExp |
1,333,148.47 |
remove end slash, endsWith |
3,982,025.14 |
remove end slahs, charCodeAt |
3,822,301.22 |
Checking if property in object
property-in-vs-property-lookup
import common from '../node-v22.9.0/benchmark/common.js'
const TYPELOOKUP = 'exists lookup'
const TYPEINOBJ = 'exists in obj'
const bench = common.createBenchmark(main, {
n: [4000],
mod: 3,
type: [
TYPELOOKUP,
TYPEINOBJ
]
})
async function main(conf) {
let obj = ''
const type = conf.type
const mod = conf.mod
if (type === TYPELOOKUP) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = { anobj: true }
else if (n === 2) n = ['array']
else n = {}
obj += n['anobj']
}
bench.end(conf.n)
}
if (type === TYPEINOBJ) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = { anobj: true }
else if (n === 2) n = ['array']
else n = {}
obj += 'anobj' in n
}
bench.end(conf.n)
}
return obj
}
const propertyIn = obj =>
'property' in obj
const propertyLookup = obj =>
obj['property']
Test |
Ops/sec |
property in obj |
2,281,780.50 |
obj[property] |
3,030,011.51 |
Array destructuring vs array lookup
array-destructure-vs-array-lookup
import common from '../node-v22.9.0/benchmark/common.js'
const TYPEDESTRUCTURE = 'destructure'
const TYPELOOKUP = 'lookup'
const bench = common.createBenchmark(main, {
n: [2000],
mod: 3,
type: [
TYPEDESTRUCTURE,
TYPELOOKUP
]
})
async function main(conf) {
let obj = ''
const type = conf.type
const mod = conf.mod
const arrayLookup = arr => {
const val0 = arr[0]
const val1 = arr[1]
const val2 = arr[2]
return val1 + val0 + val2
}
const arrayDestructure = arr => {
const [val0, val1, val2] = arr
return val1 + val0 + val2
}
if (type === TYPELOOKUP) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = ['arg' + n, n, i]
else if (n === 2) n = ['arg' + n, i, n]
else n = ['value' + i, i, i]
obj += arrayLookup(n)
}
bench.end(conf.n)
}
if (type === TYPEDESTRUCTURE) {
bench.start()
for (let i = conf.n; i--;) {
let n = (mod ? i % mod : i)
if (n === 1) n = ['arg' + n, n, i]
else if (n === 2) n = ['arg' + n, i, n]
else n = ['value' + i, i, i]
obj += arrayDestructure(n)
}
bench.end(conf.n)
}
return obj
}
const arrayLookup = arr => {
const val0 = arr[0]
const val1 = arr[1]
const val2 = arr[2]
return val1 + val0 + val2
}
const arrayDestructure = arr => {
const [val0, val1, val2] = arr
return val1 + val0 + val2
}
Test |
Ops/sec |
array lookup |
1,200,048.00 |
array destructure |
708,328.67 |