While benchmarking code in my library I came across very interesting and disappointing finding - it looks like invocation of class method is 20-40 times slower than of the same standalone function.
Shortly, for me it means - "for better performance - do not implement very-frequently called functionality via JS classes". Accessing library functions via "namespace object" also has performance penalty. That problem may affect approach to design a library.
I feel I'm missing something. Please let me know if there is something wrong in perf-tests below.
Here is summary of results, windows + intel 9600k (nodejs 14, Chrome 86, FF82)
ops/s in: node14 slow Ch86 slow FF82 slow
function(a, b) 4195 3934 1625
namespace.method(a, b) 210 x20 2702 x1.5 1305 x1.24
instance.method(a, b) 126 x33 162 x24 89 x18
prototype.method(a, b) 105 x40 154 x25 57 x28
class.method(a, b) 110 x38 153 x25 57 x28
Test script for https://benchmarkjs.com/, portions where also copy/pasted to https://jsbench.me/ for in-browser testing.
const Benchmark = require('benchmark');
const suite = new Benchmark.Suite("function-vs-method");
const nCycles = 1000000;
suite
.add('function(a, b)', function () {
function method(a, b) {
return a + b;
}
for( let i = 0; i < nCycles; i++ ) {
method(i, i)
}
})
.add('namespace.method(a, b)', function () {
function method(a, b) {
return a + b;
}
let namespace = {}
namespace.method = method
for( let i = 0; i < nCycles; i++ ) {
namespace.method(i, i)
}
})
.add('instance.method(a, b)', function () {
function method(a, b) {
return a + b;
}
function Test() {}
let instance = new Test()
instance.method = method
for( let i = 0; i < nCycles; i++ ) {
instance.method(i, i)
}
})
.add('prototype.method(a, b)', function () {
Test.prototype.method = (a, b) => {
return a + b;
}
function Test() {
}
let proto = new Test()
for( let i = 0; i < nCycles; i++ ) {
proto.method(i, i)
}
})
.add('class.method(a, b)', function () {
class Test {
method(a, b) {
return a + b;
}
}
let c = new Test()
for( let i = 0; i < nCycles; i++ ) {
c.method(i, i)
}
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run();