Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Marking and measuring style computation, layout calculations, etc #88

Open
andyearnshaw opened this issue Mar 22, 2022 · 6 comments
Open

Comments

@andyearnshaw
Copy link

My team and I have recently undertaken some work to try and improve the performance of a component that triggers a lot of DOM mutations in a single animation frame. We've instrumented the code to make use of marks and measures, but one area that is a fairly big problem for us is style computation/recalculation and layout.

Take the following (simplified) code, for example:

requestAnimationFrame(() => {
  performance.mark("updateElements:start");
  updateElements(cachedElements, nextData);
  performance.mark("updateElements:end");
});

If updateElements() does simple things with the elements like updating attributes or text content, then our performance measurements are fine. However, if they do things like change class names, position or other styles, our performance measurements do not capture the side effects caused by this and it's difficult to automate collection of such measurements. This is somewhat understandable, since other code on the page could have triggered those side effects rather than our code. However, if we're running in an isolated, predictable environment, then we can know that only our code triggered a style/layout computation.

This is sort of how we're tackling this at the moment:

requestAnimationFrame(() => {
  performance.mark("updateElements:start");
  updateElements(cachedElements, nextData);
  performance.mark("updateElements:end");
  setTimeout(() => {
    performance.mark("frame:end");
  });
});

Measuring between updateElements:end and frame:end gives us a rough idea of the time taken for the browser to compute all the changes, but it's missing important detail and it can run quite late. Is there scope for something that could help with this?

@yoavweiss
Copy link
Contributor

Thanks for the detailed issue! I think that reporting layout times is something that can be useful to help people understand the cost of their styling decisions.
/cc @chrishtr for opinions on that front

I suspect that figuring out the layout costs of specific DOM changes may be a harder sell, as it may not align with how implementations are performing layout.

@mmocny
Copy link

mmocny commented May 5, 2022

I love this request. I don't know how viable it is to expose, but I think it would be very useful.

Ideally we could expose the total amount of time spend after ALL requestAnimationFrame() calls finish (which the script above doesn't guarantee) until the COMMIT stage of rendering is complete (i.e. the main thread rendering Task is finished).

(FWIW insights into this stage specifically would be especially useful alongside Event Timing and Paint Timing as well.)

@noamr
Copy link

noamr commented May 11, 2022

I like the idea! Need to take a close look to see if exposing something so fine-grained may expose user environment or cross-origin resources via timing attacks.
But I think this type of information, that is exposed today in DevTools, could also benefit RUM a lot if we get it right.

@nhelfman
Copy link

I feel this idea has some relation to items on other proposals:

  1. EventTiming API - Proposal: add more main thread activity information to PerformanceEventTiming entry event-timing#109
  2. JS-Self-Profiling - markers - https://github.com/WICG/js-self-profiling/blob/main/markers.md

Being able to measure frame end time after some code execution is valuable and will help with certain investigations we are encounter on a regular basis (Microsoft Office Online). From our experience and production measurements of many millions of samples, using the mentioned setTimeout() technique is very inaccurate since sometimes the callback may fire over 100ms after the paint happened since it has relatively low priority.

Another technical solution have been proposed, which is possibly more accurate is using MessageChannel (https://www.webperf.tips/tip/measuring-paint-time/). This method, although more accurate still not guaranteeing high accuracy and requires relatively high effort from developers.

In general, I think addressing the need of frame timing measurements is important if we can do it without introducing any new security risks.

@mmocny
Copy link

mmocny commented May 12, 2022

Great conversation at Web Perf WG today, clearly there is appetite here.

One question that came up: Is the context of this request specific to User Interactions (Event Timing) and/or specific Element paints (Element Timing), or is it necessary to expose this information for every single Animation Frame?

Exposing something for the former could be easier, and could come as additions to existing specs. It may be something specific to focus on as a first deliverable here.

@nicjansma
Copy link

Summary of the May 12 W3C WebPerf discussion:

  • Might be able to split this into two problems, an easy one (when layout thrashing or sync layout happens in the middle of a task) and a hard one (linking DOM operations that happen in various tasks that were up to the point layout was happening to the layout that happens after that)
  • Security concerns around exposing detailed paint timing information
  • May want to follow up with layout exports to understand more about what would be easy/hard to track

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants