Skip to content

Instantly share code, notes, and snippets.

@DerekZiemba
Last active December 2, 2021 22:10
Show Gist options
  • Save DerekZiemba/10afe81fe96191b0c43303dedfc30f10 to your computer and use it in GitHub Desktop.
Save DerekZiemba/10afe81fe96191b0c43303dedfc30f10 to your computer and use it in GitHub Desktop.
JS Field access performance comparison depending on how Object constructed #jsbench #jsperf (http://jsbench.github.io/#10afe81fe96191b0c43303dedfc30f10) #jsbench #jsperf
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>JS Field access performance comparison depending on how Object constructed #jsbench #jsperf</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/1.0.0/benchmark.min.js"></script>
<script src="./suite.js"></script>
</head>
<body>
<h1>Open the console to view the results</h1>
<h2><code>cmd + alt + j</code> or <code>ctrl + alt + j</code></h2>
</body>
</html>
"use strict";
(function (factory) {
if (typeof Benchmark !== "undefined") {
factory(Benchmark);
} else {
factory(require("benchmark"));
}
})(function (Benchmark) {
var suite = new Benchmark.Suite;
Benchmark.prototype.setup = function () {
const testDepth = 1_000;
const sharedTestProcedure = (obj) => {
--obj.depth;
obj.done = obj.depth <= 0;
obj.bool = !obj.bool;
obj.tmp = obj.float - 0.69 + obj.self.self.float;
obj.integer = obj.integer + obj.string.length + obj.array.length;
obj.self.object.some = obj.tmp + obj.object.some;
if (!obj.bool) {
obj.tmp = obj.string;
obj.string = obj.stringB;
obj.stringB = obj.integer.toString();
obj.self.self.self.array.push(obj.tmp);
} else {
obj.tmp = obj.array.pop();
obj.stringB = obj.self.self.string;
obj.string = obj.tmp;
}
obj.string = obj.self.self.string;
obj.self.self.array = obj.array;
return obj.self.object;
};
const callTestProcedureMemo = function callTestProcedureMemo() { return this.testProcedure(this); }
const runMemo = function runMemo() { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
const inlineFactory = () => {
const obj = {
depth: testDepth,
bool: true,
done: false,
integer: 42,
float: 420.69,
tmp: null,
string: 'test string',
stringB: 'some other string',
array: ['array', 'of', 'strings'],
object: { some: 1 },
testProcedure: sharedTestProcedure,
callTestProcedure: () => obj.testProcedure(obj),
run: () => { while (!obj.done) { obj.self = obj.callTestProcedure().this; }; return obj.integer + obj.float; }
};
obj.self = obj;
obj.object.this = obj;
return obj;
};
const memoInlineFactory = () => {
const obj = {
depth: testDepth,
bool: true,
done: false,
integer: 42,
float: 420.69,
tmp: null,
string: 'test string',
stringB: 'some other string',
array: ['array', 'of', 'strings'],
object: { some: 1 },
testProcedure: sharedTestProcedure,
callTestProcedure: callTestProcedureMemo,
run: runMemo
};
obj.self = obj;
obj.object.this = obj;
return obj;
};
const proceduralFactory = () => {
const obj = {};
obj.self = obj;
obj.depth = testDepth;
obj.bool = true;
obj.done = false;
obj.integer = 42;
obj.float = 420.69;
obj.tmp = null;
obj.string = 'test string';
obj.stringB = 'some other string';
obj.array = ['array', 'of', 'strings'];
obj.object = { some: 1, this: obj };
obj.testProcedure = sharedTestProcedure;
obj.callTestProcedure = () => obj.testProcedure(obj);
obj.run = () => { while (!obj.done) { obj.self = obj.callTestProcedure().this; }; return obj.integer + obj.float; };
return obj;
};
const memoProceduralFactory = () => {
const obj = {};
obj.self = obj;
obj.depth = testDepth;
obj.bool = true;
obj.done = false;
obj.integer = 42;
obj.float = 420.69;
obj.tmp = null;
obj.string = 'test string';
obj.stringB = 'some other string';
obj.array = ['array', 'of', 'strings'];
obj.object = { some: 1, this: obj };
obj.testProcedure = sharedTestProcedure;
obj.callTestProcedure = callTestProcedureMemo;
obj.run = runMemo;
return obj;
};
const noProtoProceduralFactory = () => {
const obj = Object.create(null);
obj.self = obj;
obj.depth = testDepth;
obj.bool = true;
obj.done = false;
obj.integer = 42;
obj.float = 420.69;
obj.tmp = null;
obj.string = 'test string';
obj.stringB = 'some other string';
obj.array = ['array', 'of', 'strings'];
obj.object = Object.create(null); obj.object.some = 1; obj.object.this = obj;
obj.testProcedure = sharedTestProcedure;
obj.callTestProcedure = () => obj.testProcedure(obj);
obj.run = () => { while (!obj.done) { obj.self = obj.callTestProcedure().this; }; return obj.integer + obj.float; };
return obj;
};
const memoNoProtoProceduralFactory = () => {
const obj = Object.create(null);
obj.self = obj;
obj.depth = testDepth;
obj.bool = true;
obj.done = false;
obj.integer = 42;
obj.float = 420.69;
obj.tmp = null;
obj.string = 'test string';
obj.stringB = 'some other string';
obj.array = ['array', 'of', 'strings'];
obj.object = Object.create(null); obj.object.some = 1; obj.object.this = obj;
obj.testProcedure = sharedTestProcedure;
obj.callTestProcedure = callTestProcedureMemo;
obj.run = runMemo;
return obj;
};
function ES3Class() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
}
ES3Class.prototype.testProcedure = sharedTestProcedure;
ES3Class.prototype.callTestProcedure = function () { return this.testProcedure(this); };
ES3Class.prototype.run = function () { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
function ClosureES3Class() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = sharedTestProcedure;
this.callTestProcedure = function () { return this.testProcedure(this); };
this.run = function () { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
}
function MemoES3Class() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = this.testProcedure;
this.callTestProcedure = this.callTestProcedure;
this.run = this.run;
}
MemoES3Class.prototype.testProcedure = sharedTestProcedure;
MemoES3Class.prototype.callTestProcedure = function () { return this.testProcedure(this); };
MemoES3Class.prototype.run = function () { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
function PlainMemoES3Class() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = sharedTestProcedure;
this.callTestProcedure = callTestProcedureMemo;
this.run = runMemo;
}
function NoProtoMemoES3Class() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = sharedTestProcedure;
this.callTestProcedure = callTestProcedureMemo;
this.run = runMemo;
}
NoProtoMemoES3Class.prototype = Object.create(null);
function NullProtoMemoES3Class() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = sharedTestProcedure;
this.callTestProcedure = callTestProcedureMemo;
this.run = runMemo;
}
NullProtoMemoES3Class.prototype = null;
function ClosureES5Class() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = sharedTestProcedure;
this.callTestProcedure = () => this.testProcedure(this);
this.run = () => { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; };
}
class ES6Class {
constructor() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
}
testProcedure = sharedTestProcedure;
callTestProcedure() { return this.testProcedure(this); }
run() { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
}
class MemoES6Class {
constructor() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = this.testProcedure;
this.callTestProcedure = this.callTestProcedure;
this.run = this.run;
}
testProcedure = sharedTestProcedure;
callTestProcedure() { return this.testProcedure(this); }
run() { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
}
class ClosureES6Class {
constructor() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = sharedTestProcedure;
this.callTestProcedure = () => this.testProcedure(this);
this.run = () => { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; };
}
}
function NullClass(){};NullClass.prototype = null;
class NoProtoES6Class extends NullClass {
constructor() {
super();
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
}
testProcedure = sharedTestProcedure;
callTestProcedure() { return this.testProcedure(this); }
run() { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
}
class MemoNoProtoES6Class extends NullClass {
constructor() {
super();
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
this.testProcedure = this.testProcedure;
this.callTestProcedure = this.callTestProcedure;
this.run = this.run;
}
testProcedure = sharedTestProcedure;
callTestProcedure() { return this.testProcedure(this); }
run() { while (!this.done) { this.self = this.callTestProcedure().this; }; return this.integer + this.float; }
}
const functionalCallTestProcedure = (obj)=> { return sharedTestProcedure(obj); };
const functionalRun = (obj)=> { while (!obj.done) { obj.self = functionalCallTestProcedure(obj).this; }; return obj.integer + obj.float; }
class NoProtoFunctionalES6Class extends NullClass {
constructor() {
super();
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
}
}
class FunctionalES6Class {
constructor() {
this.self = this;
this.depth = testDepth;
this.bool = true;
this.done = false;
this.integer = 42;
this.float = 420.69;
this.tmp = null;
this.string = 'test string';
this.stringB = 'some other string';
this.array = ['array', 'of', 'strings'];
this.object = { some: 1, this: this };
}
}
};
suite.add("inlineFactory", function () {
// inlineFactory
return inlineFactory().run();
});
suite.add("memoInlineFactory", function () {
// memoInlineFactory
return memoInlineFactory().run();
});
suite.add("proceduralFactory", function () {
// proceduralFactory
return proceduralFactory().run();
});
suite.add("memoProceduralFactory", function () {
// memoProceduralFactory
return memoProceduralFactory().run();
});
suite.add("noProtoProceduralFactory", function () {
// noProtoProceduralFactory
return noProtoProceduralFactory().run();
});
suite.add("memoNoProtoProceduralFactory", function () {
// memoNoProtoProceduralFactory
return memoNoProtoProceduralFactory().run();
});
suite.add("ES3Class", function () {
// ES3Class
return (new ES3Class()).run();
});
suite.add("ClosureES3Class", function () {
// ClosureES3Class
return (new ClosureES3Class()).run();
});
suite.add("MemoES3Class", function () {
// MemoES3Class
return (new MemoES3Class()).run();
});
suite.add("PlainMemoES3Class", function () {
// PlainMemoES3Class
return (new PlainMemoES3Class()).run();
});
suite.add("NoProtoMemoES3Class", function () {
// NoProtoMemoES3Class
return (new NoProtoMemoES3Class()).run();
});
suite.add("NullProtoMemoES3Class", function () {
// NullProtoMemoES3Class
return (new NullProtoMemoES3Class()).run();
});
suite.add("ClosureES5Class", function () {
// ClosureES5Class
return (new ClosureES5Class()).run();
});
suite.add("ES6Class", function () {
// ES6Class
return (new ES6Class()).run();
});
suite.add("MemoES6Class", function () {
// MemoES6Class
return (new MemoES6Class()).run();
});
suite.add("ClosureES6Class", function () {
// ClosureES6Class
return (new ClosureES6Class()).run();
});
suite.add("NoProtoES6Class", function () {
// NoProtoES6Class
return (new NoProtoES6Class()).run();
});
suite.add("MemoNoProtoES6Class", function () {
// MemoNoProtoES6Class
return (new MemoNoProtoES6Class()).run();
});
suite.add("NoProtoFunctionalES6Class", function () {
// NoProtoFunctionalES6Class
return functionalRun(new NoProtoFunctionalES6Class());
});
suite.add("FunctionalES6Class", function () {
// FunctionalES6Class
return functionalRun(new FunctionalES6Class());
});
suite.on("cycle", function (evt) {
console.log(" - " + evt.target);
});
suite.on("complete", function (evt) {
console.log(new Array(30).join("-"));
var results = evt.currentTarget.sort(function (a, b) {
return b.hz - a.hz;
});
results.forEach(function (item) {
console.log((idx + 1) + ". " + item);
});
});
console.log("JS Field access performance comparison depending on how Object constructed #jsbench #jsperf");
console.log(new Array(30).join("-"));
suite.run();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment