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

Improves performance of GetLayoutHandlerIndex #18499

Merged
merged 1 commit into from Nov 6, 2023

Conversation

albyrock87
Copy link
Contributor

@albyrock87 albyrock87 commented Nov 3, 2023

Description of Change

While examining Lols Benchmark results by @jonathanpeppers I've noticed that 6.4% of the total time is consumed by LayoutExtensions.GetLayoutHandlerIndex.

image

This function is being called every time a layout child is added while the layout is already attached to an handler.
The function uses OrderBy which internally creates a SortedMap, this has a cost in terms of memory (array allocation and resize/copy) and CPU (may vary depending on the sorting algorithm used there).

This PR replaces that OrderBy + IndexOf with a simple O(N) algorithm which counts views with a lower zIndex.
That count matches the actual zIndex of the view.

I've added a benchmark to test the internal method performance change.
Unit test of this method already exist.

Before

Method Mean Error StdDev Gen0 Gen1 Gen2 Allocated
GetLayoutHandlerIndex 27.24 us 0.525 us 0.645 us 4.0283 3.9978 0.0916 24.86 KB

After

Method Mean Error StdDev Gen0 Gen1 Gen2 Allocated
GetLayoutHandlerIndex 24.61 us 0.478 us 0.511 us 3.1128 3.0823 0.0610 19.23 KB

@albyrock87 albyrock87 requested a review from a team as a code owner November 3, 2023 09:11
@ghost ghost added the community ✨ Community Contribution label Nov 3, 2023
@ghost
Copy link

ghost commented Nov 3, 2023

Hey there @albyrock87! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@jsuarezruiz jsuarezruiz added area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter legacy-area-perf Startup / Runtime performance labels Nov 3, 2023
jsuarezruiz
jsuarezruiz previously approved these changes Nov 3, 2023
jonathanpeppers
jonathanpeppers previously approved these changes Nov 3, 2023
Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

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

Otherwise, the changes do look straightforward here: a single for-loop O(N) should be better than OrderBy(...).IndexOf(...) which is probably O(N^2). 👍

@jonathanpeppers
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@albyrock87 albyrock87 force-pushed the get-layout-handler-index-perf branch 3 times, most recently from 5a1e88b to bb7a186 Compare November 3, 2023 18:23
@jonathanpeppers
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 2 pipeline(s), but failed to run 1 pipeline(s).

@hartez hartez enabled auto-merge (squash) November 3, 2023 22:00
{
case 0:
return -1;
case 1:
return view == layout[0] ? 0 : -1;
default:
return layout.OrderByZIndex().IndexOf(view);
var found = false;
Copy link
Contributor

@espenrl espenrl Nov 4, 2023

Choose a reason for hiding this comment

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

A suggestion to improve code readability and optimum performance is to use MemoryExtensions.Sort and MemoryExtensions.BinarySearch - with a custom comparer to sort by ZIndex. If one can't sort layout in place one could stackalloc a buffer array to avoid using the heap.

Another idea would be for the layout itself to keep a sorted list of the children.

Copy link
Contributor Author

@albyrock87 albyrock87 Nov 4, 2023

Choose a reason for hiding this comment

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

In this case any kind of sort is still less performant than O(N).

A SortedList though would be beneficial for both methods in this class, but the problem is that both zIndex and childIndex might change in time (i.e. adding or removing children).

I'm thinking about a more general solution for the long term, but I felt this was a low hanging fruit for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's true that the total thing would be O(N log N) which really isn't much more than O(N) for the low numbers found in layout children. My guess it's probably the allocations of the original code that makes it slower. The sorting itself could in fact be negligible.

Copy link
Contributor

Choose a reason for hiding this comment

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

I do think your code is optimal, I was mainly commenting on readability.

@jonathanpeppers
Copy link
Member

/azp run MAUI-UITests-public

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@hartez hartez merged commit 98924fa into dotnet:main Nov 6, 2023
47 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Dec 7, 2023
@albyrock87 albyrock87 deleted the get-layout-handler-index-perf branch December 30, 2023 08:07
@Eilon Eilon added t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.) and removed legacy-area-perf Startup / Runtime performance labels May 10, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter community ✨ Community Contribution t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants