Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does extending the prototype chain increase performance in this case?

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?

Setup

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;

Test 1

(control, without prototype)

result = object1.bar;

Test 2

(experiment, with prototype)

result = object2.bar;

Outcome

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.

Test 1

79,323,722 ops/s ±0.34%

Test 2

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)

like image 549
Patrick Roberts Avatar asked Feb 20 '18 20:02

Patrick Roberts


People also ask

Does prototype improve memory optimization?

'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.

What is the prototype chain?

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.

Which object we get at the end of prototype chain?

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.


1 Answers

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.

like image 190
Travis J Avatar answered Sep 22 '22 14:09

Travis J