Last active
December 2, 2021 22:10
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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