Skip to content

Instantly share code, notes, and snippets.

@twokul
Last active April 23, 2024 22:02
Show Gist options
  • Star 70 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save twokul/9501770 to your computer and use it in GitHub Desktop.
Save twokul/9501770 to your computer and use it in GitHub Desktop.
Hidden classes in JavaScript and Inline Caching

Hidden classes in JavaScript and Inline Caching

Knowing how internals work is always a good. Pretty much for everything. Cars, trains, computers, you name it. It gives you an insight on what happens under the hood. You also act/react differently based on this knowledge.

As you might have guessed, it’s also true for web development. Knowledge of CSS transitions allows you to achieve better performance and not to use JavaScript in most cases. Knowledge of V8 internals allows you to write more performant JavaScript code. So let’s talk about V8 a little.

A little about V8

V8 is a JavaScript engine built by Google. Firefox built SpiderMonkey, Opera built Carakan and Microsoft built Chakra. One very important difference between V8 and other JavaScript engines is that V8 doesn’t generate any intermediate code. It compiles JavaScript to machine code at execution.

Hidden Classes

It’s rather hard to optimize a dynamically typed, prototype-based language, such as JavaScript. Objects can change their type during runtime and it happens implicitly. To track types of JavaScript object and variables, V8 introduced the concept of hidden classes. During runtime V8 creates hidden classes that get attached to each and every object to track its shape/layout. That allows to optimize the property access time.

Consider the example:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName  = lastName;
}

var ironMan        = new Person('Tony',  'Stark');
var captainAmerica = new Person('Steve', 'Rogers');
captainAmerica.age = 87;

When Person function gets invoked to initialize ironMan object, a hidden class (let's call it C0) gets created along with invocation. C0 is an empty class because Person doesn't have any properties yet. Next, this.firstName gets assigned and hidden class C1 gets created. C1 is based on C0 with firstName property. Next, this.lastName gets assigned and guess what? Hidden class C2 gets created. C2 based on C1 with lastName property.

When Person gets invoked to initialize captainAmerica, the hidden class chain (C0 -> C1 -> C2) is going to reused and captainAmerica and ironMan are going to have the same hidden class. But as soon as the shape(layout) of the object changes, it gets a new hidden class. By assigning 87 to the age property, we created a new hidden class C2 that is going to be based on C2 with age property on it. It's very important to maintain the shape of your objects so V8 can optimize the code more efficiently.

Inline Cache

In order to optimize property access, V8 uses an old technique called Inline Caching. You can think of Inline Cache as a fast path (shortcut) to the value/property.

Consider the example:

this.puppies[0]
push [ebp+0x8]
mov eax,[ebp+0xc]
mov edx, eax
mov ecx, 0x60b55dd1
call LoadIC_Initialize  ;; this.puppies

When you try to access this.puppies, V8 is going do a call out to Inline Cache. And as your code keeps running and/or getting "hotter", V8 will make an optimistic but usually valid assumption that the value is going to stay the same. Therefore, the call out to Inline Cache can be replaced with just an address in the memory, like so:

push [ebp+0x8]
mov eax,[ebp+0xc]
mov edx, eax
mov ecx, 0x60b55dd1
call 0x311286e0  ;; this.puppies

It results in a huge (~20x) speed improvement.

Deoptimizations and Bailouts

V8 is generally pretty optimistic about your code and tries to speed it up as much as it can. But sometimes the assumptions that it makes are not valid (a hidden class wasn't the one expected). In this case, V8 will replace Inline Cache fast path code with full non-optimized code.

Bailouts happen when V8 can't optimize some parts of your code because it doesn't support some language features. V8 won't optimize functions with try {} catch() or with with {} statements in them.

@alisherabd
Copy link

Is Age will be C3 based on C2 or it's a typo?

@etsung
Copy link

etsung commented Jan 12, 2017

I think it is C3 based on C2. Great article btw! I'm guessing that inline caching is about memorizing endpoints and just substituting already known endpoints when possible. Kinda like memorizing a bunch of different chess ending patterns.

@Llorx
Copy link

Llorx commented Jul 7, 2017

The try {} catch() assumption should be updated, as new additions to V8 helps a lot with this and it may confuse new developers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment