-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
[controls] improve "setter specificity" performance #17527
[controls] improve "setter specificity" performance #17527
Conversation
object oldContext = bindable._inheritedContext?.Target; | ||
|
||
if (ReferenceEquals(oldContext, value)) | ||
if (ReferenceEquals(bindable._inheritedContext?.Target, value)) | ||
return; | ||
|
||
if (bpContext != null && oldContext == null) | ||
oldContext = bpContext.Values.LastOrDefault().Value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found oldContext
wasn't used below, Roslyn actually recommended removing this.
This also seems to help startup, a
So maybe ~15ms improved for this call? |
A customer just shared some results comparing their app startup 7 vs 8: https://discord.com/channels/732297728826277939/732297837953679412/1154161770169114654 |
can't we get the same perf bump by just avoiding calling Last() ? |
I'd go with that, and if you have a chart indicating where the time is spend, we can look at how to improve this further |
Something like https://source.dot.net/#System.Linq/System/Linq/Last.cs,76 I think that Some other ideas, we could look at next:
|
Can you also just store last? So first, second and last. |
I have tested some benchmarks, but haven't proven it 100% helps yet. I think we should do this in a future PR. |
/rebase |
Context: dotnet#13818 (review) Context: jonathanpeppers/lols#4 Fixes: dotnet#17520 A customer noticed my LOLs per second sample was slower in .NET 8 than .NET 7. I could reproduce their results. Digging in, `dotnet-trace` showed one culprit was: .NET 7 8.5% microsoft.maui.controls!Microsoft.Maui.Controls.BindableObject.GetValue 1.2% microsoft.maui.controls!Microsoft.Maui.Controls.BindableObject.SetValue .NET 8 11.0% microsoft.maui.controls!Microsoft.Maui.Controls.BindableObject.GetValue 2.8% microsoft.maui.controls!Microsoft.Maui.Controls.BindableObject.SetValue I knew that dotnet#13818 had some performance impact, as I noted when reviewing the change. Drilling in further, most of the time is spent calling `SortedList.Last()`. Which makes sense, as `BindableObject.GetValue()` is called *a lot* in a typical .NET MAUI application. Adding some logging, I found my LOLs app most commonly had the following specificity values when `BindableProperty`'s are set: * 5,284 - a single specificity value * 34,306 - two specificity values No `BindableProperty`'s in this app had more than two specificity values. So, an improvement here would be to: * Avoid `SortedList` for the most common calls * Make fields that store up to two specificity values * If a *third* specificity value is required, fall back to using `SortedList`. I introduced a new, internal `SetterSpecificityList` class for this logic. The results of running `BindingBenchmarker`: > .\bin\dotnet\dotnet.exe run --project .\src\Core\tests\Benchmarks\Core.Benchmarks.csproj -c Release -- --filter Microsoft.Maui.Benchmarks.BindingBenchmarker.* ... Before: | Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | |----------------- |---------:|---------:|---------:|-------:|-------:|----------:| | BindName | 31.67 us | 0.689 us | 2.009 us | 1.7395 | 1.7090 | 14.45 KB | | BindChild | 42.18 us | 0.864 us | 2.548 us | 2.4414 | 2.3804 | 20.16 KB | | BindChildIndexer | 78.37 us | 1.564 us | 3.266 us | 3.5400 | 3.4180 | 29.69 KB | After: | Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | |----------------- |---------:|---------:|---------:|-------:|-------:|----------:| | BindName | 27.13 us | 0.521 us | 1.016 us | 1.3733 | 1.3428 | 11.33 KB | | BindChild | 37.77 us | 0.845 us | 2.437 us | 2.0752 | 2.0142 | 17.03 KB | | BindChildIndexer | 69.45 us | 1.356 us | 2.859 us | 3.1738 | 3.0518 | 26.56 KB | My original numbers (before specificity changes in dotnet#13818) were: | Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | |----------------- |---------:|---------:|---------:|-------:|-------:|----------:| | BindName | 24.46 us | 0.554 us | 1.624 us | 1.2512 | 1.2207 | 10.23 KB | | BindChild | 33.21 us | 0.743 us | 2.192 us | 1.9226 | 1.8921 | 15.94 KB | | BindChildIndexer | 61.59 us | 1.209 us | 1.952 us | 3.1128 | 3.0518 | 25.47 KB | This gets *some* of the performance back, but not all. The LOLs per second app, testing these changes on a Pixel 5: Before: 376.98 LOLs/s After: 391.44 LOLs/s
c33c0eb
to
ad4b4d1
Compare
Context: #13818 (review)
Context: jonathanpeppers/lols#4
Fixes #17520
A customer noticed my LOLs per second sample was slower in .NET 8 than .NET 7. I could reproduce their results.
Digging in,
dotnet-trace
showed one culprit was:I knew that #13818 had some performance impact, as I noted when reviewing the change.
Drilling in further, most of the time is spent calling
SortedList.Last()
. Which makes sense, asBindableObject.GetValue()
is called a lot in a typical .NET MAUI application.Adding some logging, I found my LOLs app most commonly had the following specificity values when
BindableProperty
's are set:No
BindableProperty
's in this app had more than two specificity values.So, an improvement here would be to:
Avoid
SortedList
for the most common callsMake fields that store up to two specificity values
If a third specificity value is required, fall back to using
SortedList
.I introduced a new, internal
SetterSpecificityList
class for this logic.The results of running
BindingBenchmarker
(smaller ms/KB is better):My original numbers (before specificity changes in #13818) were:
This gets some of the performance back, but not all.
The LOLs per second app, testing these changes on a Pixel 5 (higher is better):