PurpleKingdomGames / tyrian

Elm-inspired Scala UI library.
https://tyrian.indigoengine.io/
MIT License
346 stars 26 forks source link

Investigate: Tyrian apps seem CPU intensive #254

Closed davesmith00000 closed 5 months ago

davesmith00000 commented 7 months ago

If you run the standard g8 template, and then check the task manager in Chrome, you can see the Tyrian is using a surprising amount of CPU even when idling.

If may by an unavoidable overhead of the architecture, but maybe we just introduced a regression somewhere. Worth looking into.

davesmith00000 commented 7 months ago

We speculate that this is due to the hot render loop.

https://github.com/PurpleKingdomGames/tyrian/pull/255

davesmith00000 commented 5 months ago

I've spent some time butchering the runtime and trying out different things.

My own linux machine seems to show even higher CPU load than I recall the original reporter talking about.

Some findings:

  1. If I remove the render loop, CPU usage when the app is at rest, is zero as expected. So that's good.
  2. If I remove the normal render loop, and bolt in a "does nothing" loop based on pure Scala.js (no Cats Effect, does not render with Snabbdom), then the CPU load is back.

So fundamentally, the CPU load seems to be coming from requestAnimationFrame. Below you can see the "does nothing" version in the screenshot, note the CPU load.

image

Surely this can't be the case?! How does Elm cope? Well on my machine... Elm has almost the same problem. Maybe slightly better, depending on the type of app your running, but not much. Certainly when I'm interacting with an app there is no real difference

Still, is there anything we can do?

Well, we render on frame tick if there has been a model change. We do that because if you're doing heavy updates you don't want to be re-rendering for nothing, but it adds constant load. If you render only on Msg then you can have zero overhead while the app is idle, but it can go nuts during use - for instance it would render even on a No-Op.

The only other thing I can think of is to do a hybrid. In this model you render on requestAnimationFrame, as long as you saw a message in the last, say, ~2 seconds. So if your app is idle, the rendering will go idle, but while stuff is happening (whether triggered by the user or anything else) the rendering continues.

armanbilge commented 5 months ago

The only other thing I can think of is to do a hybrid. In this model you render on requestAnimationFrame, as long as you saw a message in the last

That is what my PR https://github.com/PurpleKingdomGames/tyrian/pull/255 attempted to do. I got side-tracked but I intend to take a second look and fix it :)

davesmith00000 commented 5 months ago

No worries @armanbilge.

I've been refactoring / toying with the runtime on and off over the last couple of days, a) because it feels like the only bit of the code base I'm slightly uncomfortable with - I'd like to fix that, and b) because I'd like to see if an improvement is possible before I cut another release.

I am looking at it, but if you feel like you really want to jump back on it, do let me know. Your time is always appreciated. Equally I'm sure you're doing a bazillion other things, so don't worry over it either. :smile: