Browse Source

Refactored style attach benchmark.

Now tries to simulate an application with a lot of styles applied at different points in the logical tree.

Make `StyledElement.ApplyStyling` a public API in order to do this.
pull/9411/head
Steven Kirk 4 years ago
parent
commit
0db8d5a2d2
  1. 6
      src/Avalonia.Base/StyledElement.cs
  2. 47
      tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs
  3. 126
      tests/Avalonia.Benchmarks/Styling/Style_Apply_Detach_Complex.cs

6
src/Avalonia.Base/StyledElement.cs

@ -344,10 +344,14 @@ namespace Avalonia
/// Applies styling to the control if the control is initialized and styling is not
/// already applied.
/// </summary>
/// <remarks>
/// The styling system will automatically apply styling when required, so it should not
/// usually be necessary to call this method manually.
/// </remarks>
/// <returns>
/// A value indicating whether styling is now applied to the control.
/// </returns>
protected bool ApplyStyling()
public bool ApplyStyling()
{
if (_initCount == 0 && !_styled)
{

47
tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs

@ -1,47 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.UnitTests;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Styling
{
[MemoryDiagnoser]
public class StyleAttachBenchmark : IDisposable
{
private readonly IDisposable _app;
private readonly TestRoot _root;
private readonly TextBox _control;
public StyleAttachBenchmark()
{
_app = UnitTestApplication.Start(
TestServices.StyledWindow.With(
renderInterface: new NullRenderingPlatform(),
threadingInterface: new NullThreadingPlatform()));
_root = new TestRoot(true, null)
{
Renderer = new NullRenderer(),
};
_control = new TextBox();
}
[Benchmark]
[MethodImpl(MethodImplOptions.NoInlining)]
public void AttachTextBoxStyles()
{
var styles = UnitTestApplication.Current.Styles;
styles.TryAttach(_control, UnitTestApplication.Current);
((IStyleable)_control).DetachStyles();
}
public void Dispose()
{
_app.Dispose();
}
}
}

126
tests/Avalonia.Benchmarks/Styling/Style_Apply_Detach_Complex.cs

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.UnitTests;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Styling
{
[MemoryDiagnoser]
public class Style_Apply_Detach_Complex : IDisposable
{
private readonly IDisposable _app;
private readonly TestRoot _root;
private readonly TextBox _control;
public Style_Apply_Detach_Complex()
{
_app = UnitTestApplication.Start(
TestServices.StyledWindow.With(
renderInterface: new NullRenderingPlatform(),
threadingInterface: new NullThreadingPlatform()));
// Simulate an application with a lot of styles by creating a tree of nested panels,
// each with a bunch of styles applied.
var (rootPanel, leafPanel) = CreateNestedPanels(10);
// We're benchmarking how long it takes to apply styles to a TextBox in this situation.
_control = new TextBox();
leafPanel.Children.Add(_control);
_root = new TestRoot(true, rootPanel)
{
Renderer = new NullRenderer(),
};
}
[Benchmark]
[MethodImpl(MethodImplOptions.NoInlining)]
public void Apply_Detach_Styles()
{
// Styles will have already been attached when attached to the logical tree, so remove
// the styles first.
if ((string)_control.Tag != "TextBox")
throw new Exception("Invalid benchmark state");
((IStyleable)_control).DetachStyles();
if (_control.Tag is not null)
throw new Exception("Invalid benchmark state");
// Then re-apply the styles.
_control.ApplyStyling();
}
public void Dispose()
{
_app.Dispose();
}
private static (Panel, Panel) CreateNestedPanels(int count)
{
var root = new Panel();
var last = root;
for (var i = 0; i < count; ++i)
{
var panel = new Panel();
panel.Styles.AddRange(CreateStyles());
last.Children.Add(panel);
last = panel;
}
return (root, last);
}
private static IEnumerable<IStyle> CreateStyles()
{
var types = new[]
{
typeof(Border),
typeof(Button),
typeof(ButtonSpinner),
typeof(Carousel),
typeof(CheckBox),
typeof(ComboBox),
typeof(ContentControl),
typeof(Expander),
typeof(ItemsControl),
typeof(Label),
typeof(ListBox),
typeof(ProgressBar),
typeof(RadioButton),
typeof(RepeatButton),
typeof(ScrollViewer),
typeof(Slider),
typeof(Spinner),
typeof(SplitView),
typeof(TextBox),
typeof(ToggleSwitch),
typeof(TreeView),
typeof(Viewbox),
typeof(Window),
};
foreach (var type in types)
{
yield return new Style(x => x.OfType(type))
{
Setters = { new Setter(Control.TagProperty, type.Name) }
};
yield return new Style(x => x.OfType(type).Class("foo"))
{
Setters = { new Setter(Control.TagProperty, type.Name + " foo") }
};
yield return new Style(x => x.OfType(type).Class("bar"))
{
Setters = { new Setter(Control.TagProperty, type.Name + " bar") }
};
}
}
}
}
Loading…
Cancel
Save