Opting-out JS timers with nesting level ≤ 5 from intensive throttling

159 views
Skip to first unread message

François Doray

unread,
Sep 8, 2020, 3:10:02 PM9/8/20
to Alexander Timin, scheduler-dev
Hi Alexander,

So far, I received positive feedback on my proposal to opt-out JS timers with nesting level ≤ 5 from intensive throttling discussion.

The implementation is not straightforward, because the Blink scheduler doesn't know the nesting level of JS timers tasks.

Implementation idea #1
  • Create a new TaskType: kJavascriptTimerDelayed_LowNesting. DOMTimer obtains a TaskRunner for that TaskType when the nesting level is  ≤ 5.
  • A WakeUpBudgetPool with a 1 second wake up interval is used to control the TaskQueue associated with kJavascriptTimerDelayed_LowNesting.
  • To ensure proper execution order, the TaskQueue for kJavascriptTimerDelayed_HighNesting will be a child ot the TaskQueue for kJavascriptTimerDelayed_HighNesting  are "linked". TaskQueueThrottler will have additional logic so that whenever a parent TaskQueue is allowed to run, its child TaskQueues are also allowed to run, irrespective of what their BudgetPools attempt to enforce.
Implementation idea #2:
  • Keep the existing TaskTypes.
  • Change FrameSchedulerImpl::GetTaskRunner() to return a TimerTaskRunner (new class) when the TaskType is  kJavascriptTimerDelayed.
  • When a task is posted to TimerTaskRunner, it infers the nesting level from context->Timers()->TimerNestingLevel() (accessed via FrameScheduler::Delegate). If the nesting level is  ≤ 5, it queues a control task that will temporarily move the TaskQueue to a WakeUpBudgetPool with a 1 second wake up interval when the timer is ripe for execution. Then, it posts the actual timer task.
 What do you think of these implementation ideas? Do you have other ideas?

Thanks!

François

Sami Kyostila

unread,
Sep 9, 2020, 6:42:25 AM9/9/20
to François Doray, Alexander Timin, scheduler-dev
It seems timers with low and high nesting aren't really independent execution-order-wise, so splitting them off to separate task queues might not work too well. For this reason maybe adjusting the wake up budgets when a new low nesting task comes along seems more doable.

(Alternatively, if we could truly separate low and high nested timers, then I'd be tempted to try and also implement the spec'd minimum posting delay for deeply nested tasks using the same scheduler primitives. That's a much bigger change however.)

- Sami

--
You received this message because you are subscribed to the Google Groups "scheduler-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scheduler-de...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/scheduler-dev/CAGD3t5FaxGeBpfz6swgfuCoX0O2M_aRUk-NvFLUEPzWfG9yjSQ%40mail.gmail.com.

Alexander Timin

unread,
Sep 9, 2020, 4:36:44 PM9/9/20
to Sami Kyostila, Scott Haseley, François Doray, scheduler-dev
Thanks François! 

(I'm away for two weeks starting next Monday, so let me defer here to +Scott Haseley here to avoid blocking you)

My main question here would be whether we have any data on the distribution of timer nesting levels in the wild? In particular, I'm somewhat worried that a repeated pattern of timer - promise - timer 
will be considered non-nested and exempted from throttling. I'd recommend getting some metrics here.

From the implementation perspective, having two separate queues feels somewhat error-prone and I think one task queue would work better. Also I wonder if we need a separate TaskRunner subclass here
or if we can use primitives like OnTaskPostedHandler to implement this logic.

Scott Haseley

unread,
Sep 9, 2020, 8:50:57 PM9/9/20
to Alexander Timin, Sami Kyostila, François Doray, scheduler-dev
My biggest question is whether or not we want to try to keep the existing ordering guarantees, as Sami brought up. IIUC, the ordering would only be a problem between timers started in different tasks since timers started within the same task would all have the same nesting level? Relying on ordering between timers started in different tasks seems brittle to me, although I'm sure it happens in practice. Are there use cases folks have come across where this makes sense?

My understanding of Safari's version of this is that they already break the spec by treating timers that haven't reached the max nesting level differently (ignoring ordering guarantees). If that's the case, I'm inclined to say we should also break this and simplify things, and update the timer spec to reflect this. But of course I don't know what sites are relying on ordering between timers that we'd break by doing this.

On Wed, Sep 9, 2020 at 1:36 PM Alexander Timin <alt...@chromium.org> wrote:
Thanks François! 

(I'm away for two weeks starting next Monday, so let me defer here to +Scott Haseley here to avoid blocking you)

My main question here would be whether we have any data on the distribution of timer nesting levels in the wild? In particular, I'm somewhat worried that a repeated pattern of timer - promise - timer 
will be considered non-nested and exempted from throttling. I'd recommend getting some metrics here.

Re: timer-promise-timer, do you mean something like setTimeout(() => Promise.resolve().then(() => setTimeout(...))), or if there's a non-setTimeout async step? For the former case, I believe we run a microtask checkpoint after every callback which should leave the nesting level intact (it would be good to double-check this).

Alexander Timin

unread,
Sep 10, 2020, 11:36:31 AM9/10/20
to Scott Haseley, Sami Kyostila, François Doray, scheduler-dev
On Thu, 10 Sep 2020 at 01:50, Scott Haseley <shas...@chromium.org> wrote:
My biggest question is whether or not we want to try to keep the existing ordering guarantees, as Sami brought up. IIUC, the ordering would only be a problem between timers started in different tasks since timers started within the same task would all have the same nesting level? Relying on ordering between timers started in different tasks seems brittle to me, although I'm sure it happens in practice. Are there use cases folks have come across where this makes sense?

My understanding of Safari's version of this is that they already break the spec by treating timers that haven't reached the max nesting level differently (ignoring ordering guarantees). If that's the case, I'm inclined to say we should also break this and simplify things, and update the timer spec to reflect this. But of course I don't know what sites are relying on ordering between timers that we'd break by doing this.

On Wed, Sep 9, 2020 at 1:36 PM Alexander Timin <alt...@chromium.org> wrote:
Thanks François! 

(I'm away for two weeks starting next Monday, so let me defer here to +Scott Haseley here to avoid blocking you)

My main question here would be whether we have any data on the distribution of timer nesting levels in the wild? In particular, I'm somewhat worried that a repeated pattern of timer - promise - timer 
will be considered non-nested and exempted from throttling. I'd recommend getting some metrics here.

Re: timer-promise-timer, do you mean something like setTimeout(() => Promise.resolve().then(() => setTimeout(...))), or if there's a non-setTimeout async step? For the former case, I believe we run a microtask checkpoint after every callback which should leave the nesting level intact (it would be good to double-check this).

Yeah, I was thinking about some sort of promise-based API (e.g. fetch) which leads to a continuous code execution in a background tab. That's a speculation, however, and having some hard data would help :) 

François Doray

unread,
Sep 14, 2020, 5:42:24 PM9/14/20
to Alexander Timin, Scott Haseley, Sami Kyostila, scheduler-dev
I fuzzed Javascript timers in Safari 13.1.2 and confirmed that it does not always respect the execution order described in the spec. Knowing that, I would like to simplify the intensive throttling policy to have no ordering guarantee between timers with different nesting levels, as suggested by Scott:

In a Window whose top Window has been hidden for 5 minutes, timers are executed:
1. on 1-second aligned intervals, if the nesting level is <= 5
2. on 1-minute aligned intervals, if the nesting level is > 5

It is easier to describe and implement a policy that does not attempt to honor ordering between timers with different nesting levels. Also, if we ship this policy, we would modify the spec to correctly document the ordering guarantees in Chrome and Safari, and that would open the door for more experiments in the future.

We would monitor impact on 1P and 3P sites when launching the policy, and adjust if we find valid use cases that depend on the current ordering guarantees.

All: What do you think of this policy change?

Scott:
  • Could you help us update the spec to correctly document the weaker ordering guarantees?
  • What is the timeline for updating the spec? Is it reasonable to ship the intensive throttling policy before the spec change is completed?
Thanks!

Sami Kyostila

unread,
Sep 15, 2020, 8:23:27 AM9/15/20
to François Doray, Alexander Timin, Scott Haseley, scheduler-dev
Thanks for checking Safari's behavior here. I think lifting the ordering requirement makes the overall throttling scheme a lot simpler and also more effective.

Just to confirm, aligning to 1s means allowing at most one wakeup per second, but potentially letting multiple timers execute per wakeup -- is that right? Does that match Safari too?

In terms of spec updates, I think the (lack of) ordering guarantee is important to mention, but I'm not sure we should put the exact numbers there in case we want to iterate on the policy.

- Sami

Scott Haseley

unread,
Sep 24, 2020, 2:49:30 PM9/24/20
to Sami Kyostila, François Doray, Alexander Timin, scheduler-dev
Quick follow-up on spec discussions: I filed a spec issue about this last week. There's a little bit of info there on Gecko's throttling approach (and a related revert), but we're still waiting on WebKit to respond, which is most relevant.

François Doray

unread,
Oct 5, 2020, 3:31:17 PM10/5/20
to Scott Haseley, Sami Kyostila, Alexander Timin, scheduler-dev
Hi Scott,

WebKit hasn't responded yet. Should we take some actions to get their input?

Thanks,

François

Scott Haseley

unread,
Oct 5, 2020, 6:56:03 PM10/5/20
to François Doray, Sami Kyostila, Alexander Timin, scheduler-dev
On Mon, Oct 5, 2020 at 12:31 PM François Doray <fdo...@google.com> wrote:
Hi Scott,

WebKit hasn't responded yet. Should we take some actions to get their input?

Thanks for the reminder. I'll reach out on WebKit's Slack and report back.
Reply all
Reply to author
Forward
0 new messages