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

Minor optimization for MergedStyle.SetStyle #21540

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

MartyIX
Copy link
Contributor

@MartyIX MartyIX commented Mar 30, 2024

Description of Change

This PR just removes a few allocations. There is no big idea.

Benchmark

cd src/Core/tests/Benchmarks
dotnet build -c Release --framework net8.0 && dotnet run --framework net8.0 -c Release -- --runtimes net8.0 --filter *StyleBenchmarker*

MAIN

Method Mean Error StdDev Median Gen0 Gen1 Gen2 Allocated
MergedStyle 66.95 us 2.668 us 7.867 us 63.44 us 4.1504 4.0283 1.3428 25.79 KB

PR

Method Mean Error StdDev Median Gen0 Gen1 Gen2 Allocated
MergedStyle 65.98 us 2.538 us 7.443 us 63.34 us 4.1504 4.0283 1.3428 25.79 KB
Old results
MAIN

| Method      | Mean     | Error    | StdDev   | Median   | Gen0   | Gen1   | Gen2   | Allocated |
|------------ |---------:|---------:|---------:|---------:|-------:|-------:|-------:|----------:|
| MergedStyle | 66.18 us | 2.060 us | 5.977 us | 63.38 us | 4.5166 | 4.3945 | 0.7324 |  28.25 KB |


PR

| Method      | Mean     | Error    | StdDev   | Median   | Gen0   | Gen1   | Gen2   | Allocated |
|------------ |---------:|---------:|---------:|---------:|-------:|-------:|-------:|----------:|
| MergedStyle | 64.74 us | 1.820 us | 5.367 us | 62.34 us | 4.5166 | 4.3945 | 0.7324 |  28.25 KB |

The improvement is not really big but I find it better than nothing.

Additional findings

  1. It appears that MergedStyle.ReRegisterImplicitStyles is not in use (at least not in this repo).
  2. CA1859 is reported on line
    static readonly IList<Type> s_stopAtTypes = new List<Type> { typeof(View), typeof(Compatibility.Layout<>), typeof(VisualElement), typeof(NavigableElement), typeof(Element) };
    and it looks like it can be trivially changed and nothing wrong would happen.

Issues Fixed

Fixes #21539

@MartyIX MartyIX requested a review from a team as a code owner March 30, 2024 21:15
@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Mar 30, 2024
public class StyleBenchmarker
{
[Benchmark]
public void MergedStyle()
Copy link
Contributor Author

@MartyIX MartyIX Mar 30, 2024

Choose a reason for hiding this comment

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

@@ -119,7 +119,7 @@ public void FontSizeCanBeSetFromStyle()

Assert.Equal(label.GetDefaultFontSize(), label.FontSize);

label.SetValue(Label.FontSizeProperty, 1.0, new SetterSpecificity(SetterSpecificity.StyleImplicit, 0, 0, 0));
label.SetValue(Label.FontSizeProperty, 1.0, SetterSpecificity.ImplicitSetter);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just use the 'constants' in tests too.

@jsuarezruiz jsuarezruiz added the area-xaml XAML, CSS, Triggers, Behaviors label Apr 1, 2024
@rmarinho
Copy link
Member

rmarinho commented Apr 1, 2024

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@MartyIX
Copy link
Contributor Author

MartyIX commented Apr 12, 2024

@StephaneDelcroix Does this PR make sense to you please?

@MartyIX
Copy link
Contributor Author

MartyIX commented Apr 19, 2024

@jonathanpeppers Does the proposed changes make sense to you please?

ImplicitStyle?.Apply(bindable, new SetterSpecificity(SetterSpecificity.StyleImplicit, 0, 0, 0));
ImplicitStyle?.Apply(bindable, SetterSpecificity.ImplicitSetter);
Copy link
Member

Choose a reason for hiding this comment

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

So what is interesting, in your numbers on the PR description, before & after both say Allocated: 28.25 KB.

SetterSpecificity is a struct, and so maybe that Allocated column is heap allocations only?

The fact the time improved, does indicate your change helps, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SetterSpecificity is a struct, and so maybe that Allocated column is heap allocations only?

https://benchmarkdotnet.org/articles/configs/diagnosers.html#restrictions mentions:

In order to get the number of allocated bytes in cross platform way we are using GC.GetAllocatedBytesForCurrentThread which recently got exposed for netcoreapp1.1.

and GC.GetAllocatedBytesForCurrentThread is described as follows:

The GetAllocatedBytesForCurrentThread method returns the total number of bytes allocated on the managed heap [emphasis mine] during the lifetime of a thread, not the total number of bytes that have survived garbage collection. The returned value also does not include any native allocations.

Comment on lines +18 to +20
public static readonly SetterSpecificity ImplicitSetter = new(StyleImplicit, 0, 0, 0);
public static readonly SetterSpecificity LocalSetter = new(StyleLocal, 0, 0, 0);
public static readonly SetterSpecificity LocalClassSetter = new(StyleLocal, 0, 1, 0);
Copy link
Member

Choose a reason for hiding this comment

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

I think the main question here is if @StephaneDelcroix likes consolidating these into fields, as he is more familiar with this code. It does seem like it cleans up the code to me.

Copy link
Member

Choose a reason for hiding this comment

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

Won't the reference to this field make a copy anyway? There should be no perf difference between new vs copy... Or maybe there is. No need to execute instructions, just copy memory...

Copy link
Member

Choose a reason for hiding this comment

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

There might be some runtime optimization, where it is faster to copy from a field (I guess?). I'm just judging from the benchmark results. This only helps performance a small amount, so the question is if it cleans up the code. If it does, seems worth it to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

internal readonly struct SetterSpecificity : IComparable<SetterSpecificity>, IEquatable<SetterSpecificity>
- it is a readonly struct. I naively expected that it is not copied at all because it's a struct that does not change so it can be on heap and just accessed and not copied at all.

Comment on lines 13 to 31
var buttonStyle = new Style(typeof(Button))
{
Setters = {
new Setter { Property = Button.TextColorProperty, Value = Colors.Pink },
},
Class = "pink",
ApplyToDerivedTypes = true,
};
var labelStyle = new Style(typeof(Label))
{
Setters = {
new Setter { Property = Button.BackgroundColorProperty, Value = Colors.Pink },
},
Class = "pink",
ApplyToDerivedTypes = false,
};
Copy link
Member

Choose a reason for hiding this comment

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

What if the styles were created as member variables in the Benchmark class? It might narrow the work happening in the MergedStyle() method and get us a more accurate benchmark? It will run that method many times.

If you could get the benchmark down to just merging the styles and creating the fewest objects, that would be good.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I modified the benchmark to be as follows: 5dc6577 and updated OP #21540 (comment) with new numbers.

@MartyIX MartyIX force-pushed the feature/2024-03-30-merged-style-optimization branch from 567a201 to 9683b14 Compare May 17, 2024 06:57
@MartyIX MartyIX force-pushed the feature/2024-03-30-merged-style-optimization branch from 9683b14 to a950b75 Compare June 27, 2024 17:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-xaml XAML, CSS, Triggers, Behaviors community ✨ Community Contribution
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MergedStyle.SetStyle appears to be CPU intensive
5 participants