I've had a long-standing assumption that deep prototype
chains resulted in performance deterioration for property accessors. I was trying to explain that on hide the getter or add in the proto Object when a quick benchmark I threw together resulted in quite the opposite outcome from what I was expecting.
What is going on here? Am I missing something obvious, or does this outright demonstrate that my (and others') assumption about the performance of property accessors on the prototype
chain was wrong?
const object1 = {
foo: 'Hello, World!',
get bar () { return this.foo }
};
const object2 = Object.assign(Object.create({
get bar () { return this.foo }
}), {
foo: 'Hello, World!'
});
let result;
(control, without prototype
)
result = object1.bar;
(experiment, with prototype
)
result = object2.bar;
Test 1 ran 92.85% slower than Test 2, which means that placing get bar () {}
in the prototype
chain rather than in the object's own properties results in a 14x speed increase for the property accessor. See Object.create()
to understand how the layout of the object is different.
79,323,722 ops/s ±0.34%
1,108,762,737 ops/s ±0.15%
Tested on Windows 10 Intel i7-7700K CPU @ 4.20GHz using Google Chrome 63.0.3239.132 (Official Build) (64-bit)
'Prototype' helps remove code redundancy which helps boost your app's performance. If you are seeking to optimize resources or memory on your application, you should use prototype . Declaring this in your constructor can cause object redundancy, especially when the properties are methods.
Every object in JavaScript has a built-in property, which is called its prototype. The prototype is itself an object, so the prototype will have its own prototype, making what's called a prototype chain. The chain ends when we reach a prototype that has null for its own prototype.
That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. By definition, null has no prototype, and acts as the final link in this prototype chain.
To my knowledge, these details only apply to the V8 engine, I am not sure how directly this maps to the Firefox's implementation.
Without the prototype, V8 is creating hidden classes to support the properties of your object. For each new property, a new hidden class is created, and then a transition from the previous hidden class to the new one is created.
However, this does not happen with prototypes, and is kind of a little known fact from the conversations I have had with regards to the topic. In other words, yes, prototypes are faster.
To optimize prototypes, V8 keeps track of their shape differently from regular transitioning objects. Instead of keeping track of the transition tree, we tailor the hidden class to the prototype object, and always keep it fast -Toon Verwaest (V8 dev)
This setup all happens during Dynamic Machine Code Generation. The difference between the two setups that you are seeing is the difference between the more complex hidden class path versus the more custom path. Or, by name, the difference between the fastPropertiesWithPrototype object and the slowProperties object, the latter of which uses dictionary mode.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With