-
Notifications
You must be signed in to change notification settings - Fork 309
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
OrderedDictionary.values and OrderedSet Performance Problems when SwiftUI.List is paired with SwiftUI.NavigationStack #334
Comments
The OrderedSet equality operator simply calls elementsEqual. It does not test for referential equality as array does. It probably should though. Could this be the cause of the issue? |
@ingoem Hmm… the underlying storage of the Maybe it's a good idea… if Be that as it may… it's still a little weird that the "modern" |
@ingoem It looks like And the default implementation of |
@lorentey Any thoughts about migrating these equality checks to forward through to |
This diff fixes my perf issues and still passes the |
#335 was merged and these equality checks are now fast when comparing equal identities… I'm still not clear why SwiftUI would need dozens of equality checks to compare a |
https://developer.apple.com/forums/thread/741484
Hi! I'm copying this information over from Apple DevForums. My guess here is that there is a potential optimization inside the SwiftUI library itself that can clean up these perf issues… but I'll go ahead and post the details here just in case there is something else inside the
OrderedDictionary
itself that should be optimized. Thanks!Information
Checklist
main
branch of this package.Steps to Reproduce
https://gist.github.com/vanvoorden/1c7c6ed08898de7f4b8619147537c0eb
Hi! Has anyone here seen weird performance problems when passing values from a
Collections.OrderedDictionary
to aSwiftUI.List
insideSwiftUI.NavigationStack
? I see some performance problems that go away when switching back toSwiftUI.NavigationView
. At this point, I'm not sure if it's a problem in theOrderedDictionary
implementation or not. Anyone want to take a look and see if you can help track this down?For a simple backing store to drive a simple SwiftUI app displaying a
List
of data, theOrderedDictionary
(fromswift-collections
) has the advantage of giving us the option to access (by an arbitrarykey
) in constant time. TheOrderedDictionary
exposes avalues
property that conforms toRandomAccessCollection
. Since we can pass aRandomAccessCollection
as data to generate aList
, we should hope to be able to pass theOrderedDictionary.values
the same way we would pass anArray
.This is leading to some strange performance problems that seem to happen when our
List
is contained in aNavigationStack
(and we push and pop a detail view on the stack). Here is a simple example:In this example, we start by defining a
NavigationDemoRange
that we use to generate 1MM data elements. Our data element is a simpleInt
, which we mark asIdentifiable
to make it easier to pass to aList
. We then construct a simpleOrderedDictionary
instance, where everykey
simply maps to itself (and thevalues
property preserves the intended ordering).We then create (and present) a
NavigationStack
that wraps aList
that displays every element from ourOrderedDictionary.values
. We add a simpleNavigationLink
(and pair with anavigationDestination
) to display a "detail view" (which is just aText
element to display theInt
).When we launch this app (on simulator or device), we see a significant hang when tapping an item from the list (pushing a detail view). Popping back appears not to hang, but then pushing another item hangs again.
When we inspect instruments to troubleshoot this hang, we see that our app is spending a lot of time testing our
OrderedDictionary.values
for equality:Adding a breakpoint here and running our app shows that this equality check happens 7 times when the app is launched, 5 times when the detail is pushed, and 5 times when the detail is popped. That is 17 total equality checks to display a list, push a detail, and pop back all driven from data that we defined as immutable (a
let
constant).If we try and implement our app on the alternative
NavigationLink
API (before the introduction ofnavigationDestination
), we see different results. Now, our app launches and we see a significant hang on both pushing and popping a detail view. Instruments confirms we are still spending a lot of the on equality checks. Adding a breakpoint on the check shows us this equality check happens 1 time when the app launches, 24 times when the detail view is pushed, and 12 times when the app is popped. That is 37 total equality checks against data that is (still) an immutablelet
constant.We set our
NavigationDemoRange
to 1MM elements to exaggerate performance problems, but for any amount of data we still see the equality checks occurring the same amount of times.The
OrderedDictionary.values
type exposes anelements
property that wraps thevalues
with anArray
. When we passOrderedDictionary.values.elements
to ourList
, our performance problems go away:FWIW, we see the exact same performance problems (number of equality checks) when passing in
Collections.OrderedSet
(and the problems go away when we pass theOrderedSet.elements
property).That was
NavigationStack
. What happens if we try this same test withNavigationView
?This looks much faster with no noticeable performance problems. Setting a breakpoint confirms that equality is checked once on app launch. Pushing and popping perform no equality checks.
Here is what we know (so far):
OrderedDictionary.Values
performs poorly whenList
is paired withNavigationStack
.OrderedDictionary.Values
performs well whenList
is paired withNavigationView
.Array
performs well whenList
is paired withNavigationStack
orNavigationView
.What could be causing these performance problems? It's not clear. Most of the sample code from Apple passes an
Array
when constructing aList
, but theList
constructor just takes an arbitraryRandomAccessCollection
without telling us there are performance implications from rolling our ownRandomAccessCollection
outside the system Swift types.Is there some kind of bug in the implementation of
OrderedDictionary.Values
(andOrderedSet
) that would lead to this performance problem? If that was true, why would that performance problem only seem to show up inNavigationStack
(and notNavigationView
)?If it's not safe to pass arbitrary
RandomAccessCollection
types toList
, should we always wrap any collection in anArray
before constructing aList
?Any more thoughts about this? Any ideas where the performance problem could be caused from?
These have all been tested on
17.0
iPhone simulator and device. Theswift-collections
version is1.0.5
Thanks!
Expected behavior
OrderedDictionary.Values
performs well whenList
is paired withNavigationStack
.Actual behavior
OrderedDictionary.Values
performs poorly whenList
is paired withNavigationStack
.The text was updated successfully, but these errors were encountered: