Skip to content
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

WrappingHStack causes high CPU usage #34

Closed
cvde opened this issue Nov 17, 2022 · 3 comments · Fixed by #36
Closed

WrappingHStack causes high CPU usage #34

cvde opened this issue Nov 17, 2022 · 3 comments · Fixed by #36
Assignees
Labels
enhancement New feature or request

Comments

@cvde
Copy link

cvde commented Nov 17, 2022

Hi Daniel!

First of all: Thanks for your awesome work! :)

I noticed that using WrappingHStack in longer lists is quite CPU intensive (not sure if #4 already addresses my point - if so, feel free to close this issue).

Here is an example:

    var body: some View {
        ScrollView {
            ForEach(1..<300) { index in
                WrappingHStack {
                    Text("This")
                    Text("is")
                    Text("a")
                    Text("test")
                    Text("!1!!")
                }
            }
        }
    }

I checked what is causing the high CPU usage and found that the InternalWrappingHStack.init method is being called hundreds of times. Launching the app without further interaction already results in 597 calls. I made a small video that shows how a very small interaction skyrockets the number of calls:

initCalls.mp4

I have no clue what's going on. I believe that GeometryReader in WrappingHStack.swift triggers these calls. However, as the video shows, the width does not change at all. Is there a possibility to react only to relevant changes to reduce the number of InternalWrappingHStack.init calls significantly?

(I had some success by moving the GeometryReader out of WrappingHStack.swift into the view that uses WrappingHStack and passing the width to WrappingHStack, but my knowledge of SwiftUI is too limited to understand what's going on with the height in WrappingHStack.swift)

Again, thanks a lot! :)

@cvde cvde added the enhancement New feature or request label Nov 17, 2022
@cvde cvde assigned dkk Nov 17, 2022
@dkk
Copy link
Owner

dkk commented Nov 18, 2022

Ill try to inspect it when I can, but I didn't have much luck improving performance so far, since the View generics are very limiting

@dkk
Copy link
Owner

dkk commented Nov 26, 2022

This is what happens:

  1. WrappingHStack is called
  2. Uses GeometryReader to know the width, which will be used to calculate which views go on which lines (initial height for the GeometryReader is 0)
  3. After the lines are calculated, the body of InternalWrappingHStack is defined, which creates a height update.
  4. The update calls onPreferenceUpdate on the .bounds anchor. This allows to set the right height, which in turn redraws the InternalWrappingHStack, resulting in a goto step 2. Which should be ok, since it will not create another update on the anchor because the height stays the same this time.

However, there is a problem where onPreferenceChange might get called because of rounding errors, which should be solved with the if abs(height - $0) > 1, but it is not since onPreferenceChange redraws everything even when there are no state changes, which sometimes creates an additional unnecessary cycle.

Also, in your specific case lazy loading might help, since the really bad performance you are seeing is related to the fact that you load everything at once:

ScrollView {
       LazyVStack {
               ForEach(1..<300) 

I also would appreciate some help from the community ;)

@cvde
Copy link
Author

cvde commented Nov 29, 2022

Hi Daniel!

Thanks for your explanation! I tried the "LazyVStack" approach, and it actually reduces the number of calls a bit (344 initial calls compared to 597 calls without LazyVStack). This ~50% reduction also seems to be the case when interacting with the list.

However, I am still hesitant to use it in production for longer lists. Would be great if someone has an idea how to optimize this further. 🙂🤞

@dkk dkk closed this as completed in 9f5f586 Dec 3, 2022
@dkk dkk closed this as completed in #36 Dec 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants