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

[LoAF] is blockingDuration needed? #8

Closed
mmocny opened this issue Nov 29, 2023 · 3 comments
Closed

[LoAF] is blockingDuration needed? #8

mmocny opened this issue Nov 29, 2023 · 3 comments
Assignees

Comments

@mmocny
Copy link

mmocny commented Nov 29, 2023

The goals of blockingDuration I believe are twofold:

  • To help differentiate long animation frames which are due to long-running-tasks vs late/lazy scheduling of rendering work (browser may choose to optimize task throughput vs rendering latency, or there may be times where rendering is paused).
  • It helps provide a value comparable to how Long Tasks duration minus 50ms is used for Total Blocking Time (TBT).

However, I think it may be both redundant and inaccurate/incomplete.


Redundant:

LoAF already reports script attribution for tasks which are >5ms, and reports timestamps for rendering. Perhaps you can already define your own "good enough" blocking duration in terms of these values?

In practice, I find that I mostly see very near numbers, but occasionally am seeing a large divergence in ways I cannot easily spot from from metrics. Perhaps when no scripts are not reported to attribution? Or perhaps I am holding it wrong.

const observer = new PerformanceObserver(list => {
    for (let entry of list.getEntries()) {
        const scriptDurations = new Int32Array(entry.scripts.map(s=>s.duration));
        const longestScriptDuration = scriptDurations.toSorted().at(-1) || 0;
        const renderingDuration = entry.styleAndLayoutStart - entry.renderStart;
        const slDuration = entry.startTime + entry.duration - entry.styleAndLayoutStart;
        // TODO: renderingDuration sometimes is also reported to script attribution?
        const total = longestScriptDuration + renderingDuration + slDuration;
        const blocking = Math.max(total-50, 0);
        const diff = entry.blockingDuration - blocking;
        
        console.log('[LoAF]', entry.duration, diff, {
            blockingDuration: entry.blockingDuration,
            blocking,
            longestScriptDuration,
            renderingDuration,
            slDuration,
            scriptDurations,
            entry,
        });
    }
});

observer.observe({
    type: "long-animation-frame",
    buffered:true,
});

Inaccurate/incomplete:

It may not true that only a single longest task + rendering are the "shortest possible blocking duration". It may be that more than a single task must be dispatched before rendering. (Examples: Multiple event dispatch at equal priority; raf-aligned events; rAF callbacks...).

Perhaps blockingDuration is useful and could evolve into some "shortest possible path". Or perhaps "scripts" attribution could expose task priority, or some "blocking: true" field.

@noamr
Copy link
Collaborator

noamr commented Nov 29, 2023

The thing about inferring blockingDuration from scripts is that you can miss the following contributors to blocking:

  • multiple scripts that are less than 5ms each
  • Time spent in things other than scripts, e.g. serializing messages for postMessage

I see a few alternative ways about this:

  1. Expose "longest work task" instead of blockingDuration (they're interchangeable, and "longest work task" is perhaps simpler to reason about)
  2. Only count the last task before the LoAF as contributing to it. This is where I went originally with LoAF... It's like saying "you didn't really have a rendering opportunity after the earlier tasks, because we don't give you very frequent ones unless there's input/animation".
  3. Forget blockingDuration. Perhaps it doesn't need to be deduced at all... If you have a LoAF, it means that your main thread was congested. The fact that you split it into tasks was nice but the first task that required a presentation update still had to wait before rendering because there was lots of other main-thread stuff going on.

@mmocny
Copy link
Author

mmocny commented Nov 29, 2023

RE: 1 --I like that its a bit more interoperable with the Long Tasks api-- but it's also less flexible to evolve. I could imagine adding more to blockingTime than just one task. But maybe you general point is that exposing a "total blocking duration", without subtracting 50ms or including rendering time automatically, is easier to understand?

RE 2: --Interesting. That makes some sense... On the other hand, today LoAF will already slice tasks at processingEnd whenever we know we don't need rendering (i.e. fallback to LongTask time). And so this proposal would be similar. Tasks which aren't immediately followed by rendering are "just tasks", and not really LoAFs, from the perspective of the "blocking" concept. So we'd have [Task] as distinct entry, then [Task] nested within LoAF but outside "blocking" time, and then [Task+Rendering]. If we believe that is the right way, then maybe we just split LoAF entries, and make LoAF.duration == blockingDuration?

RE 3: -- I think I am mostly in this camp right now. I do think right now many pages will report long LoAFs which are due to render-throttling and may not be the "fault" of the page, but its also a true representation of UX. So it just comes down to how the data is interpreted...

@noamr
Copy link
Collaborator

noamr commented Dec 4, 2023

Some partners are finding blockingDuration useful, as they correlate to INP better than duration.

@noamr noamr closed this as completed Dec 4, 2023
@noamr noamr transferred this issue from w3c/longtasks Mar 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants