wokwi / avr8js

Arduino (8-bit AVR) simulator, written in JavaScript and runs in the browser / Node.js
https://blog.wokwi.com/avr8js-simulate-arduino-in-javascript/
MIT License
481 stars 78 forks source link

Performance bottleneck due to setTimeout(fn, 0) #18

Closed gfeun closed 4 years ago

gfeun commented 4 years ago

I think I identified a potential performance bottleneck due to the way task queing works in JS event loop

Disclaimer, I'm not familiar with JS internals, just compiling some search results

This setTimeout(fn, 0) is potentially "rate limited" by browsers : https://github.com/wokwi/avr8js/blob/3b7a122e9ca5252ea20376ce436809ef85602120/demo/src/execute.ts#L50

From https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout

In modern browsers, setTimeout()/setInterval() calls are throttled to a minimum of once every 4 ms when successive calls are triggered due to callback nesting (where the nesting level is at least a certain depth), or after certain number of successive intervals.

While it states that this applies to "recursive" setTimeout calls, it is not clear to me if it applies to consecutive setTimeout calls. It may explain performance differences between Chrome and Firefox because they may handle this differently.

The "recommended" workaround I found is to use the window.postMessage API to schedule next tasks.

This old posts (maybe not relevant anymore) details the throttling happening: https://dbaron.org/log/20100309-faster-timeouts

And the associated test page gives me better performances for the postMessage (setZeroTimeout) method (https://dbaron.org/mozilla/zero-timeout)

100 iterations of setZeroTimeout took 12 milliseconds.
100 iterations of setTimeout(0) took 215 milliseconds.

This SO answer: https://stackoverflow.com/questions/18826570/settimeout0-vs-window-postmessage-vs-messageport-postmessage with a corresponding jsfiddle http://jsfiddle.net/Noseratio/wPCN4/6/ points in the same direction

2000 iterations of setTimeoutPM took 124 milliseconds.
2000 iterations of setTimeoutMC took 67 milliseconds.
2000 iterations of setTimeout(0) took 5187 milliseconds.
urish commented 4 years ago

Sounds like a good direction and possibly another quick win. Have you tried changing the demo code to use this method?

gfeun commented 4 years ago

Yes I'm on it. I'm slow since i don't know typescript but I should have some results today

urish commented 4 years ago

Excellent. I'd love to see how it affects the performance on Firefox