Hi Conrad, I'll make an attempt at answering, though I'm not an expert on OSR, so others like Jakob or Mythri may have more precise answers.
1. Why would this property access be polymorphic? If you're talking about polymorphic access, you're probably familiar with hidden classes (also called "object shapes" or "Maps but not *those* Maps <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map>"). Regardless, I'll include a link: the most complete and accurate description I've found of how hidden classes work in V8 is Fast properties in V8 <https://v8.dev/blog/fast-properties>. In this example, the object {a: 0} has a hidden class which says it has one property named "a". The object {a: 0, b:1} has a different hidden class which says it has two properties, named "a" and "b". After the function getA has performed a lookup to get property "a" from {a: 0}, it keeps a pointer to that object's hidden class and another value indicating where the property can be found (for this case, the first in-object property slot). When running getA({a:0, b:1}), V8 checks whether the object {a:0, b:1} has the same hidden class as {a: 0}, which it does not. So V8 remembers a second pair of values: the hidden class for {a:0, b:1} and where its "a" property can be found (also the first in-object property slot). Now the feedback state for that load operation is polymorphic. The "mono" in monomorphic refers to a single hidden class, not to a single result of where the property can be found. This behavior tends to be particularly problematic in codebases with a lot of class inheritance, because loading a field defined by a base class is often a megamorphic operation, even if that base class's constructor always sets the same properties in the same order. 2. Why would polymorphic code optimized by turbofan be a full 3x slower than unoptimized bytecode? It seems that you may be misunderstanding the somewhat cryptic output from --trace-opt. In particular, OSR means on-stack replacement <https://v8.dev/blog/v8-release-79#osr-caching>. Copying some text from that link: *When V8 identifies that certain functions are hot it marks them for optimization on the next call. When the function executes again, V8 compiles the function using the optimizing compiler and starts using the optimized code from the subsequent call. However, for functions with long running loops this is not sufficient. V8 uses a technique called on-stack replacement (OSR) to install optimized code for the currently executing function. This allows us to start using the optimized code during the first execution of the function, while it is stuck in a hot loop.* Iterating through an array of 100 million items certainly counts as a "hot loop", so the vast majority of the time in *all* of your measurements is spent in optimized code produced by Turbofan, not in the interpreter. You can try running unoptimized code by passing the command-line flag --no-opt, which I expect will go much more slowly than what you've measured thus far. I've added some possibly more human-readable annotations to the output you provided: # This call started in the interpreter, but was replaced by optimized code while running (this process is referred to as OSR). [compiling method 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) using TurboFan OSR] [optimizing 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) - took 0.000, 0.541, 0.000 ms] array1: 115.701ms # This call reused the OSR code from the first call. [found optimized code for 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) at OSR bytecode offset 35] array1: 113.721ms # At this point, the function got compiled normally (not using OSR), so future calls will use this optimized code. [compiling method 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) using TurboFan] [optimizing 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) - took 0.000, 0.500, 0.041 ms] # These three calls used fully optimized code. array1: 80.069ms array1: 79.72ms array1: 79.245ms # This call mostly used optimized code, until it bailed out to the interpreter for the last four items. array2: 78.906ms # This call reused the OSR code from the very first call. This is surprising to me; I didn't realize that the OSR code was still available at this point, after the non-OSR version of the function has bailed out. However, it seems to work nicely in this case. Once again, it bailed out to the interpreter for the last four items. [found optimized code for 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) at OSR bytecode offset 35] array2: 112.758ms # At this point, the function got compiled normally again, so future calls will use this optimized code. [compiling method 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) using TurboFan] [optimizing 0x3b21df5b9ad9 <JSFunction sum (sfi = 0x10c4d4712831)> (target TURBOFAN) - took 0.000, 0.500, 0.042 ms] # These three calls used that newly compiled version of the code, which uses a megamorphic load. array2: 350.273ms array2: 351.822ms array2: 357.311ms In closing, I'll just echo Ryan: "JS perf is extremely hard to reason about". Best, Seth On Friday, May 20, 2022 at 10:11:37 AM UTC-7 [email protected] wrote: > I'm looking at a perf example shared by Ryan Cavanaugh of Typescript, and > I'm very much failing to understand what is happening and why. The > particular contradictions upset my entire mental model of how to write > performant javascript. What's going on internally? > > Here is the example: > https://gist.github.com/conartist6/642dcfbd6fa444da92f211bcb405692b > > The two specific things I don't understand are: > > 1. If I have this code: > > ```js > function getA(o) { > // Why would this property access be polymorphic? > // Isn't the offset for the `a` property always the same? > return o.a; > } > getA({ a: 0 }) > getA({ a: 0, b: 1 }) > ``` > > 2. Why would polymorphic code optimized by turbofan be a full 3x slower > than unoptimized bytecode? > > -- -- v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev --- You received this message because you are subscribed to the Google Groups "v8-dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/v8-dev/6857a76a-8c97-445d-9f92-413cc9f99c67n%40googlegroups.com.
