13

The Object.observe() JavaScript API allows any piece of code to receive change notifications for all property changes of any JavaScript object.

Doesn't this severely affect the code generation and performance optimizations that can be performed by the JavaScript Engine (i.e. V8)? It seems like the generated native code now has to check for every single write to the object if a change notification must be generated. It is not possible to statically determine if a given object has notifications set up or not. So the checks cannot be optimized out.

It seems like any conforming JavaScript engine is now locked in to a permanent and severe loss in performance due to this API.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
usr
  • 168,620
  • 35
  • 240
  • 369
  • "*It is not possible to statically determine if a given object has notifications set up*" - Why? – Bergi Feb 27 '13 at 15:05
  • @Bergi how would you do it? Statically means without running the code or inspecting an object. – usr Feb 27 '13 at 17:09

2 Answers2

17

Modern JavaScript engines utilize inline caching and adaptive recompilation techniques to minimize impact of the dynamic dispatch on the generated code.

If we are speaking about V8 then the fact whether object is observed or not is encoded in its hidden class. Both inline caches stubs and optimized code already check hidden class against some expected value to determine whether an object has an expected shape or not. The very same check gives information about the fact whether the object is observed or not. So nothing changes on the code paths that work with non-observed objects. Starting to observe the object is treated the same way as changing it shape: object's hidden class is switched to a different one, with an observed bit set: you can read Runtime_SetIsObserved to see this.

Similar reasoning applies to the parts of the system that omit guards in the optimized code and instead deoptimize code dependant on "shape" assumptions: once an object becomes observed all optimized code depending on the assumption that such object was not observed will be deoptimized. Thus again no price is paid for unobserved objects.

That said, current implementation of Object.observe in V8 makes observed objects pay a high price because it normalizes them (turns them into dictionary representation) and requires round trips through runtime system for observation recording. But there are no inherent technical difficulties in significantly reducing this cost later.

Vyacheslav Egorov
  • 10,302
  • 2
  • 43
  • 45
  • Still, this *requires* engines to take a dynamic "hidden class" approach forever. Right now it would be possible to solve more of these specialization optimizations using static analysis or type annotations (in a future version of JavaScript). That will now be impossible forever. JS can now never be as fast as, say, Java or .NET even with perfect type information. – usr Feb 27 '13 at 18:34
  • First let me note that static type analysis for the full JavaScript in its current form is way too expensive and impractical; and type annotations are a hypothetical thing of tomorrow and even when introduced engines will have to keep hidden classes to run "oldschool" unannotated code fast for backwards compatibility. Second comparing some abstract notion of speed of language X vs. language Y is not constructive, you should specify benchmark, language patterns we are discussing etc. And finally I specifically outlined approach where executing code contains *no* checks whatsoever. – Vyacheslav Egorov Feb 27 '13 at 21:07
  • This last approach relies on Object.observe deoptimizing the code that can potentially write into observed objects. It's quite similar to deoptimizing code when, for example, debugger is attached. Depending on how things are implemented it can deoptimize everything or only affected functions. – Vyacheslav Egorov Feb 27 '13 at 21:10
  • There is no way to deoptimize only affected functions because any object at all (except if proven by escape analysis to never escape into observe code, not even with the help of eval) could be observed (and that can change at any event loop iteration).; Don't get me wrong, your answer is very useful and will be accepted in a few days. I just feel like this particular feature provides a kind of performance lock-in that we never had before and that is irreversible. – usr Feb 27 '13 at 23:11
  • Surely there is no way to know precisely affected functions. Please excuse my sloppy wording in the last sentence. The first sentence is the one that states it correctly: "deoptimize the code that can *potentially* write". Current JITs already have the means to do it: they can deoptimize only those functions that are specialized to work with objects of the given hidden class. In the hypothetical future with type annotations it would be enough to deoptimize those functions that contained type annotation matching type of observed object in their bodies (or bodies inlined into them). – Vyacheslav Egorov Feb 28 '13 at 08:29
  • 6
    What I am trying to illustrate here is that if you don't use `Object.observe` you pay **absolutely** nothing. When you use it then particular cost will depend on the implementation and the application itself. – Vyacheslav Egorov Feb 28 '13 at 08:38
2

Doesn't this severely affect the code generation and performance optimizations that can be performed by the JavaScript Engine (i.e. V8)?

Yes. Just the same as Proxies, Getters/Setters and maybe even prototype objects - all of them are dynamic in JavaScript.

However, due to their asynchronity new (and better) optimisations will be possible; and they could make other, more inefficient code obsolete. Citing the Goals from the harmony draft:

  • No wrapper or proxy objects needed, providing memory efficiency and object identity
  • Change notifications on add/delete of a property on an object
  • Change notifications on modifications to property descriptor of properties on an object
  • The ability for an object to manually indicate when an accessor property has changed
  • Efficiently implementable in engines
  • Simple, targeted, extension to current ES
  • Asynchronous notification of changes, but allow synchronous fetching of changes pending delivery
Bergi
  • 630,263
  • 148
  • 957
  • 1,375