Open Bug 1962778 Opened 16 days ago Updated 4 days ago

4x slower than Chrome when calling getters on Vue 2 reactive objects

Categories

(Core :: JavaScript Engine, defect, P2)

defect

Tracking

()

People

(Reporter: mstange, Unassigned)

References

(Blocks 3 open bugs)

Details

Attachments

(1 file)

On the attached microbenchmark, I get 3600ms in Firefox and 870ms in Chrome.

I came across this issue in bug 1720236, see bug 1720236 comment 3. The website there uses Vue 2, which redefines the properties of any objects it makes "observable", replacing those properties with getters/setters.

Vue 2 is considered end-of-life by the Vue project. Nevertheless, existing pages use it and the performance cliff seems worth fixing.

I also came across a very similar issue in bug 1946264. The website there uses PixiJS, which uses a mixin function to put an eventMode property with a getter on the prototype of a Container class.

The underlying cause of this is probably the same as bug 1961324: if we change a data property to a getter, and it's not the last property, we transition to a dictionary shape. Jan and I talked about this earlier this week; it's not strictly necessary to go to dictionary mode in that case, although it does save some complexity.

Comparing a V8 profile of this microbenchmark to an SM profile, we are slightly slower in prepare, but much slower in benchmark. This is presumably because after going through all this replacement, V8 ends up with a bunch of objects with the same shape, whereas all our objects have unique dictionary shapes.

I've hacked together a quick attempt at avoiding dictionary mode here by building up a new SharedPropMap. It's still got some off-by-one errors in it, but not in a way that should affect performance. If I tweak the testcase to dodge them, I get down to a 2x difference between us and V8. Peeking at that, it looks like the issue is that each of the getters we're calling is a distinct function, and (unlike regular calls) we don't fall back to guarding on the script for getters, so we end up with a megamorphic getprop. If I unsafely comment out the guard, we're 4x faster than V8, although that's not a completely fair comparison because the script guard that we would need to generate there isn't free. Putting the two optimizations together, though, I think we should be able to beat V8 on this microbenchmark (although it's likely that they're still faster on the unmeasured prepare step).

Blocks: sm-js-perf
Severity: -- → S3
Priority: -- → P2
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: