Skip to content

[wasm] Reduce number of calls to setTimer #62433

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

Merged
merged 10 commits into from
Dec 10, 2021

Conversation

pavelsavara
Copy link
Member

@pavelsavara pavelsavara commented Dec 6, 2021

Problem description

  • When each new timer‡ is created it would start self-sustaining setTimeout loop.
  • The loop instance would only stop when ALL timers are done.
  • Many scheduled loops could be running in parallel to each other.
  • I added calls to mono_set_timeout_exec in Net6.0 to prevent timer throttling. That made the underlying problem worse and that's how it was detected.

‡ TimerQueue is group of timers, so only shorter than current in the same TimerQueue does that.

Fix

  • changes in TimerQueue.Browser.Mono.cs
    • we will always have only one, the shortest due setTimeout pending for all TimerQueue instances.
    • we remember it's due time in global s_shortestDueTimeMs
    • we will not schedule new setTimeout if current pending is earlier than requested one . Even from different TimerQueue instance.
    • if there is new timer which need to be scheduled earlier than the existing one, we would clear the previous one and register new one. Replace, rather than add the loop.
  • changes in scheduling.ts
    • fix of negated condition bug prevent_timer_throttling, only introduced in Net7.0
    • removed support for engines without setTimeout for better code readability. Removed timeout_queue.
    • added lastSchedule which is the previous registration of pending timer. We call clearTimeout on it, any time new timer is registered.

Testing

  • on V8 setTimeout ignores delay and behaves as it was always 0. V8 could not be used to test timers.
  • created simple test which could be executed on NodeJS console.
  • the test would monkey patch globalThis.setTimeout to be able to observe number of setTimeout calls and number of callback executions.
  • it covers few basic test scenarios with expectations on number of calls mono does

Fix for #62227

TODO

@pavelsavara pavelsavara added the arch-wasm WebAssembly architecture label Dec 6, 2021
@pavelsavara pavelsavara requested a review from kg December 6, 2021 14:44
@ghost
Copy link

ghost commented Dec 6, 2021

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

Issue Details

work in progress

Trying fix #62227

Author: pavelsavara
Assignees: -
Labels:

arch-wasm

Milestone: -

@pavelsavara
Copy link
Member Author

/azp run runtime-manual

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@pavelsavara pavelsavara requested a review from maraf December 8, 2021 08:06
@pavelsavara pavelsavara added this to the 6.0.x milestone Dec 8, 2021
@pavelsavara pavelsavara requested a review from kg December 8, 2021 10:40
@pavelsavara
Copy link
Member Author

This is test with the repro sample, when running this PR (net 7.0)

It shows that

  • prevent_timer_throttling would hit every second and steal the work from the next timer
  • every other hit is just mono_wasm_set_timeout_exec of the 500ms Timer in the repro code
blazor.webassembly.js:1         Delay 00:00:00.5040000 avg rate 00:00:00.5012456
dotnet.6.0.0.siibt7wbrl.js:3373 prevent_timer_throttling cb
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
dotnet.6.0.0.siibt7wbrl.js:3352 pump_message
dotnet.6.0.0.siibt7wbrl.js:3355 mono_background_exec
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.4970000 avg rate 00:00:00.5011724
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.4960000 avg rate 00:00:00.5010847
dotnet.6.0.0.siibt7wbrl.js:3373 prevent_timer_throttling cb
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
dotnet.6.0.0.siibt7wbrl.js:3352 pump_message
dotnet.6.0.0.siibt7wbrl.js:3355 mono_background_exec
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.5100000 avg rate 00:00:00.5012333
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.4900000 avg rate 00:00:00.5010492
dotnet.6.0.0.siibt7wbrl.js:3373 prevent_timer_throttling cb
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
dotnet.6.0.0.siibt7wbrl.js:3352 pump_message
dotnet.6.0.0.siibt7wbrl.js:3355 mono_background_exec
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.5050000 avg rate 00:00:00.5011129
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.5010000 avg rate 00:00:00.5011111
dotnet.6.0.0.siibt7wbrl.js:3373 prevent_timer_throttling cb
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
dotnet.6.0.0.siibt7wbrl.js:3352 pump_message

Current code on Net6.0 has hundred calls to mono_wasm_set_timeout_exec each tick.

When the page is out of focus and the browser applies timer throttling

  • the timer rate is 1 second
  • we see the call triggered by prevent_timer_throttling doing all the work
  • followed by mono_set_timeout loop immediately, just once
blazor.webassembly.js:1         Delay 00:00:00.9930000 avg rate 00:00:00.5299002
dotnet.6.0.0.siibt7wbrl.js:3373 prevent_timer_throttling cb
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
dotnet.6.0.0.siibt7wbrl.js:3352 pump_message
dotnet.6.0.0.siibt7wbrl.js:3355 mono_background_exec
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.9960000 avg rate 00:00:00.5310047
dotnet.6.0.0.siibt7wbrl.js:3373 prevent_timer_throttling cb
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.0030000 avg rate 00:00:00.5297565
dotnet.6.0.0.siibt7wbrl.js:3352 pump_message
dotnet.6.0.0.siibt7wbrl.js:3355 mono_background_exec
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.9970000 avg rate 00:00:00.5308585
dotnet.6.0.0.siibt7wbrl.js:3373 prevent_timer_throttling cb
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.0020000 avg rate 00:00:00.5296141
dotnet.6.0.0.siibt7wbrl.js:3352 pump_message
dotnet.6.0.0.siibt7wbrl.js:3355 mono_background_exec
dotnet.6.0.0.siibt7wbrl.js:3389 mono_wasm_set_timeout_exec
dotnet.6.0.0.siibt7wbrl.js:3387 mono_set_timeout
blazor.webassembly.js:1         Delay 00:00:00.9960000 avg rate 00:00:00.5307089

@pavelsavara pavelsavara marked this pull request as ready for review December 8, 2021 11:29
@pavelsavara
Copy link
Member Author

cc @m-chandler

@pavelsavara
Copy link
Member Author

/azp run runtime-manual

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@pavelsavara
Copy link
Member Author

/azp run runtime-manual

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Member

@kg kg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks right to me.

@pavelsavara pavelsavara merged commit 806a06e into dotnet:main Dec 10, 2021
maraf added a commit to maraf/runtime that referenced this pull request Dec 13, 2021
Test is only manual. Xharness version on this branch
triggers timer queue in a way that makes the test useless.

Co-authored-by: Pavel Savara <[email protected]>
safern pushed a commit that referenced this pull request Jan 3, 2022
* Port changes to the TimerQueue from #62433.

Test is only manual. Xharness version on this branch
triggers timer queue in a way that makes the test useless.

Co-authored-by: Pavel Savara <[email protected]>

* Replace main method with plain static method for running timer tests.

* Rename index.html to simple.html to avoid collision with generated index.html for unit tests.

Co-authored-by: Pavel Savara <[email protected]>
@pavelsavara pavelsavara deleted the wasm_fix_timers branch January 4, 2022 13:06
@ghost ghost locked as resolved and limited conversation to collaborators Feb 3, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants