Search code examples
javascriptoptimizationv8javascriptcorespidermonkey

Do JavaScript engines have any specific optimizations for classes?


I want to document the differences between classes and closures in JavaScript. Both class-based, object-oriented and functional JavaScript exist in the wild. For example in React, both class components and function components with hooks are supported.

I don't want to assert that you would ever choose one programming paradigm over another purely based on their performance, but is it possible that in modern JavaScript engines classes have a better chance to be optimized due to their language support?


I was under the impression that classes were just syntactic sugar for inheritance implemented with a prototype chain, but while this still appears to be a popular point of view, it is not correct.

In addition, their are new proposals such as decorators and private fields which continue to put more distance between classes and their alternatives. While decorators are just the composition of higher-order functions and private fields can be emulated, I expect that JavaScript engines may not perform identical optimization in all cases.

I am also aware that JavaScript engines employ techniques such as hidden classes to improve their performance. To my knowledge this optimization is not class specific. However, this made me curious...

Are their any optimizations that current JavaScript engines perform on classes, that are may not be applied in cases where classes are avoided? Would their be invariants that might be easier to identify and exploit due to the consistent nature of the class-based syntax?


I did try to read through the source code of the V8 engine, but it is large and complex, and I expect would take me a significant amount of time before I could even create a hypothesis. Therefore, I think this question is better left to someone with more experience than me in this domain.

So far my quest has led me from the parser to the abstract syntax tree and then to the bytecode generator used by the interpreter. I am aware that the interpreter (Ignition) is just the first stage of the JIT, and is followed by the compilers Sparkplug and Turbofan, which then transform the bytecode into machine code.

Indeed, classes do appear to have specific bytecode generation as seen in the BuildClassLiteral in the BytecodeGenerator class. However, I am unaware if this could yield better performance further into the pipeline of compilers.


Solution

  • (V8 developer here.)

    I think this can largely be answered with "no", but the question is rather broad.

    One issue is that I'm not sure what you're comparing. Are you comparing classes to non-class-based (e.g. pre-ES6) OOP, or to some definition of "functional programming" that avoids using objects entirely? If the latter, then the the answer is both trivial (yes, engines have lots and lots of optimizations that deal with objects and hence are only applicable to objects) and useless (because that doesn't say anything about whether using objects or not using objects will be faster overall).

    Comparing classes to class-avoiding OOP, there isn't a big difference in optimization potential, because classes don't provide additional guarantees or invariants. For example, you can still add and remove properties of class instances, you can change which prototype they point at, and what fields and methods are present on that prototype. Locking down these things might give more optimization opportunity, or at least would make existing optimizations more reliable and less speculative, but that's not what classes do today. As it stands, JavaScript classes are mostly for the benefit of JS programmers, not for the benefit of JS engines. If they affect JS performance, then that's probably mostly by making a few particularly inefficient coding patterns less likely (such as installing methods as fields in the constructor).

    "Is it possible...?", sure, everything is possible. I'm not aware of an example, but that doesn't mean that there isn't or can't ever be one: as you note, the V8 source code is large and complex, and I'm far from knowing all of it in detail. I could try to make a few guesses and toy around with some examples, but even if I managed to find some artificially crafted case where class-based code is optimized better than some non-class-based alternative, would that really help you?


    As a general note, keep in mind that "optimization" isn't something binary, in the sense that the situation is far more nuanced than "feature X is optimized, feature Y is not". To make an analogy: when cleaning up your house, there are clearly many more states than "this room is cleaned up" and "this room is not cleaned up". Even a statement like "I've spent the entire day cleaning up the kitchen" doesn't imply that the kitchen is the best-cleaned-up room now: maybe it was such a mess to begin with that it's still less clean than the bedroom, even though nobody has been working on the latter today. The existence of kitchen-specific clean-up techniques (such as running the dishwasher) doesn't imply that kitchens have a general tendency to be cleaner than other types of rooms. And also, there's always something more that one could do, if only there was a little more time left in the day...