Open Bug 1808403 Opened 3 years ago Updated 2 years ago

Investigate creating a system to inline DOM bindings in Ion

Categories

(Core :: DOM: Bindings (WebIDL), enhancement)

enhancement

Tracking

()

Performance Impact medium

People

(Reporter: alexical, Unassigned)

References

(Blocks 1 open bug)

Details

(Whiteboard: [sp3])

Take a look at this profile of Speedometer 2 as a case study: https://share.firefox.dev/3jBEKD9

We spend a good deal of time in ProxyGetPropertyByValue. This time mostly comes from Ion, which means it came from a VM call, which means a decent chunk of the actual time doing these operations is actually not attributed to ProxyGetPropertyByValue but instead lives in the callers. I think it's plausible that we could improve this situation by creating a mechanism for inlining these calls directly into Ion code. Or, as we've done with megamorphic property access in Ion, if we could identify a fast happy path, we could inline just that path and fall back to the full VM call if it fails.

Further than just avoiding VM call overhead, a lot of what this particular call is doing is calling virtual methods. I suspect that while these are polymorphic at the level of ProxyGetPropertyByValue, if we were to step one level up to the JS call site, they would be very monomorphic, and we could simply inline guards that the object is the expected type in Ion and do the operation specific to that type.

I am very very unfamiliar with this code, so this bug is totally speculative, but I wanted to get it all down in a bug to be tracked.

Component: XPCOM → DOM: Bindings (WebIDL)

It seems like proxies are a special and extra-complicated case right? I think we do some degree of fast-pathing for DOM stuff from the JIT already (e.g. js::jit::CallDOMSetter), but that optimization doesn't work through a proxy.

So it seems like there are three somewhat separate proposals on the table:
(1) Make proxies generically faster.
(2) Add some special-case fast-paths for frequently-occurring proxies (in this case looks primarily to be indexed getters on nodelists).
(3) Make non-proxy DOM operations from the JIT faster.

(In reply to Bobby Holley (:bholley) from comment #1)

It seems like proxies are a special and extra-complicated case right? I think we do some degree of fast-pathing for DOM stuff from the JIT already (e.g. js::jit::CallDOMSetter), but that optimization doesn't work through a proxy.

So it seems like there are three somewhat separate proposals on the table:
(1) Make proxies generically faster.
(2) Add some special-case fast-paths for frequently-occurring proxies (in this case looks primarily to be indexed getters on nodelists).
(3) Make non-proxy DOM operations from the JIT faster.

I'm not sure I understand what about proxies prevents us from improving the situation? We can guard that the proxy's handler is of a specific type, and we can guard that the id being requested is some way that we need it to be (i.e., it's an integer within the bounds we need), and then all the code down from the point where we call the handler's get method is either very general, or it's generated by dom/bindings/Codegen.py. The hope is that we could build a system where we could leverage that infrastructure in dom/bindings/Codegen.py to basically output the equivalent masm to the generated C++, at least for the more well-behaved cases (assuming there are well behaved and less well behaved cases.)

(In reply to Doug Thayer [:dthayer] (he/him) from comment #2)

I'm not sure I understand what about proxies prevents us from improving the situation?

Nothing per se, I'm just pointing out that the handling for for proxies is rather distinct relative to non-proxies in both SM and DOM bindings. They have different overhead characteristics and likely require separate optimization work, so it's worth clarifying where the larger opportunity is.

My prior is that proxies have lots of overhead that could be optimized, but are relatively rare, whereas non-proxies make up the lion's share of traffic but have been heavily optimized already. Are certain proxies (e.g. nodelists) hot enough that we should put work into optimizing them? Do we believe we can significantly reduce the overhead JIT access to non-proxy DOM objects beyond what we've done already? Both?

So, I was using bug 1807159 as a reference for what kind of outcome we might expect from this. It seems to have yielded something like a 5% win for React-Redux-TodoMVC as one example, and this is just from inlining calls to GetNativeDataPropertyPure in Ion. However, after instrumenting a bit more, GetNativeDataPropertyPure is called something like 700k times from React-Redux-TodoMVC, while ProxyGetPropertyByValue is called something like 250k times. Additionally, GetNativeDataPropertyPure is called quite a lot from other subbenchmarks as well, while ProxyGetPropertyByValue seems largely scoped to jQuery. However, what may mitigate this is the fact that GetNativeDataPropertyPure is called via an ABI call, which is much cheaper than the VM call for ProxyGetPropertyByValue. However, I still think that given the scope of the work required and the mildness of the expected outcome, this is probably lower priority, unless there's some clever optimization that inlining GetNativeDataPropertyPure could unlock for us, by making the logic call-site-specific.

Performance Impact: --- → medium

While you're instrumenting, it would be interesting to get a distribution of proxy handlers and operations. If it's like 99% indexed get operations on nodelists/collections, that gives us a very targeted thing to optimize.

And rather than extrapolating too much from GetNativeDataPropertyPure, I might suggest instrumenting ProxyGetPropertyByValue to run the overhead twice to get an estimate of how much we could save by eliminating it (though it will be imperfect due to cache warming).

(In reply to Doug Thayer [:dthayer] (he/him) from comment #0)

Further than just avoiding VM call overhead, a lot of what this particular call is doing is calling virtual methods. I suspect that while these are polymorphic at the level of ProxyGetPropertyByValue, if we were to step one level up to the JS call site, they would be very monomorphic, and we could simply inline guards that the object is the expected type in Ion and do the operation specific to that type.

Aren't a lot of the virtual methods that we call related to wrapping the result value? Since these don't depend on the type of the callee, but on the type of the result value, I'm wondering if these really would be very monomorphic?

Whiteboard: [sp3]
You need to log in before you can comment on or make changes to this bug.