w3c / long-animation-frames

Long Animation Frame API specification repository
Other
10 stars 1 forks source link

[LoAF] is blockingDuration needed? #8

Closed mmocny closed 11 months ago

mmocny commented 11 months ago

The goals of blockingDuration I believe are twofold:

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 commented 11 months ago

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

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 commented 11 months ago

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 commented 11 months ago

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