JavaScript Performance Q's: Dictionary Mode, The Global Object, Objects as Namespaces

114 views
Skip to first unread message

Patrick Rutkowski

unread,
Aug 19, 2016, 5:23:24 PM8/19/16
to v8-users

Greetings v8-users,


I'm coming from the world of C, C++, x86_64 assembler, and native C-based OpenGL. I've switched gears recently and I'm doing a lot of JavaScript WebGL work now. Soon I'll be playing with V8 embedding so that I can run my JavaScript 3D projects in a native desktop environment. Other engines like SpiderMonkey, JavaScriptCore, and Chakra interest me as well.


I'm extraordinarily confused about the performance characteristics surrounding the objects-are-also-maps duality in JavaScript. I understand what hidden classes are in V8, and I get that there is no lookup overhead if you have something like a `Point` object and you access `this.x` or `this.y`. An engine like V8 will just do constant-offset access via the hidden class. My understanding of this is very V8-specific, but I imagine that other JavaScript engines play similar tricks.


What I'm confused about is the more complicated cases, and the subtleties. Here's a list of my various confusions. Points may overlap:


1) When exactly do objects go into dictionary mode? Are there general rules of thumb about this that can be applied to all engines that I'm likely to care about?


2) If global variables all exist as properties of the global object, then do JavaScript engines try hard to keep the global object out of dictionary mode? Top-level functions live on the global object too, I think, so if the global object is in dictionary mode then wouldn't a simple function call incur a dictionary lookup? That seems insane to me, but based on some IRC conversions I get the sense that this actually happens, a lot. Am I wrong here? I sure hope I'm wrong here.


3) I often see projects using objects in place of namespace. You'll see code like `new MyCorp.SomeClass()` and `MyCorp.DoIt()`. If the `MyCorp` object goes into dictionary mode then this would be pretty terrible. Fortunately namespace-y objects aren't likely to ever see a delete call, nor to be used as a dictionary-like way, so is it safe to assume that most engines will keep namespace-y objects in non-dictionary mode? Is this assumption safe even if the namespace-y object has upward of a thousand properties?


4) What do you do if the answer to #3 is "Yes, namespace-y objects often go into dictionary mode and incur lookup overhead"? My first thought is to put functions in the top-level instead, and just prefix them with the namespace: `new MyCorpSomeClass()` or `MyCorpDoIt()`. But then you're just using the global object itself as your namespace-y object, and if that can go into dictionary mode too then you're hosed either way. What's the solution here?


Based on previous unproductive conversations on IRC I feel the need to add the following warning to this post:


If your answer contains the phrases like "benchmark *then* optimize" or "care not about such things, crazy C programmer, this is JavaScript," then I'm going to reach out over the internet and steal your keyboard's enter key.


Thanks in advance for any help and advice!

-Patrick

Toon Verwaest

unread,
Aug 19, 2016, 5:46:12 PM8/19/16
to v8-u...@googlegroups.com
Everything below is based on what V8 currently does, without context of other VMs. Any of this is internal and will likely change at one point or another. But you asked for it ... :)

1) When exactly do objects go into dictionary mode? Are there general rules of thumb about this that can be applied to all engines that I'm likely to care about?


Too many properties added; limits are different (much lower) for [key] than .key.

Property deletion.

One additional thing: properties stay fast up to the max limit of 2^10-2 if the object is used as __proto__ somewhere; but the object's hidden class is detached from the transition tree. We assume prototypes behave like class objects, and its properties are methods to which you want fast access.

2) If global variables all exist as properties of the global object, then do JavaScript engines try hard to keep the global object out of dictionary mode? Top-level functions live on the global object too, I think, so if the global object is in dictionary mode then wouldn't a simple function call incur a dictionary lookup? That seems insane to me, but based on some IRC conversions I get the sense that this actually happens, a lot. Am I wrong here? I sure hope I'm wrong here.

Global objects are dictionaries where all values are wrapped in "cells". Property access to the global object can directly inline this cell to avoid subsequent dictionary lookup. The cell tracks the type, including whether it's constant. If it's constant, the optimizing compiler can generate great code. 


3) I often see projects using objects in place of namespace. You'll see code like `new MyCorp.SomeClass()` and `MyCorp.DoIt()`. If the `MyCorp` object goes into dictionary mode then this would be pretty terrible. Fortunately namespace-y objects aren't likely to ever see a delete call, nor to be used as a dictionary-like way, so is it safe to assume that most engines will keep namespace-y objects in non-dictionary mode? Is this assumption safe even if the namespace-y object has upward of a thousand properties?

Such objects behave the same as outlined above. Upper limit on fast properties of 2**10-2. With the way we currently treat prototypes, and what you want from namespaces, it may be a good idea to use them as a __proto__ somewhere to get them into that mode. (This behavior can change at any time though; the behavior has already significantly changed in the past.)

4) What do you do if the answer to #3 is "Yes, namespace-y objects often go into dictionary mode and incur lookup overhead"? My first thought is to put functions in the top-level instead, and just prefix them with the namespace: `new MyCorpSomeClass()` or `MyCorpDoIt()`. But then you're just using the global object itself as your namespace-y object, and if that can go into dictionary mode too then you're hosed either way. What's the solution here?

That would work. The global object doesn't have the 2**10-2 limit. 


HTH
Toon
Reply all
Reply to author
Forward
0 new messages