Browse Source

Merge pull request #3560 from MarchingCube/bench-style-attach

Benchmark and optimizations for style attach
pull/3581/head
Steven Kirk 7 years ago
committed by GitHub
parent
commit
947e43b5e7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      src/Avalonia.Styling/StyledElement.cs
  2. 4
      src/Avalonia.Styling/Styling/Setter.cs
  3. 35
      src/Avalonia.Styling/Styling/Style.cs
  4. 4
      src/Avalonia.Styling/Styling/Styles.cs
  5. 26
      src/Avalonia.Visuals/Visual.cs
  6. 47
      tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs

27
src/Avalonia.Styling/StyledElement.cs

@ -573,9 +573,12 @@ namespace Avalonia
element._dataContextUpdating = true;
element.OnDataContextBeginUpdate();
foreach (var child in element.LogicalChildren)
var logicalChildren = element.LogicalChildren;
var logicalChildrenCount = logicalChildren.Count;
for (var i = 0; i < logicalChildrenCount; i++)
{
if (child is StyledElement s &&
if (element.LogicalChildren[i] is StyledElement s &&
s.InheritanceParent == element &&
!s.IsSet(DataContextProperty))
{
@ -646,9 +649,15 @@ namespace Avalonia
AttachedToLogicalTree?.Invoke(this, e);
}
foreach (var child in LogicalChildren.OfType<StyledElement>())
var logicalChildren = LogicalChildren;
var logicalChildrenCount = logicalChildren.Count;
for (var i = 0; i < logicalChildrenCount; i++)
{
child.OnAttachedToLogicalTreeCore(e);
if (logicalChildren[i] is StyledElement child)
{
child.OnAttachedToLogicalTreeCore(e);
}
}
}
@ -661,9 +670,15 @@ namespace Avalonia
OnDetachedFromLogicalTree(e);
DetachedFromLogicalTree?.Invoke(this, e);
foreach (var child in LogicalChildren.OfType<StyledElement>())
var logicalChildren = LogicalChildren;
var logicalChildrenCount = logicalChildren.Count;
for (var i = 0; i < logicalChildrenCount; i++)
{
child.OnDetachedFromLogicalTreeCore(e);
if (logicalChildren[i] is StyledElement child)
{
child.OnDetachedFromLogicalTreeCore(e);
}
}
#if DEBUG

4
src/Avalonia.Styling/Styling/Setter.cs

@ -78,8 +78,6 @@ namespace Avalonia.Styling
{
Contract.Requires<ArgumentNullException>(control != null);
var description = style?.ToString();
if (Property == null)
{
throw new InvalidOperationException("Setter.Property must be set.");
@ -107,6 +105,8 @@ namespace Avalonia.Styling
}
else
{
var description = style?.ToString();
var activated = new ActivatedValue(activator, value, description);
return control.Bind(Property, activated, BindingPriority.StyleTrigger);
}

35
src/Avalonia.Styling/Styling/Style.cs

@ -116,13 +116,21 @@ namespace Avalonia.Styling
if (match.IsMatch)
{
var controlSubscriptions = GetSubscriptions(control);
var subs = new CompositeDisposable(Setters.Count + Animations.Count);
if (control is Animatable animatable)
var animatable = control as Animatable;
var setters = Setters;
var settersCount = setters.Count;
var animations = Animations;
var animationsCount = animations.Count;
var subs = new CompositeDisposable(settersCount + (animatable != null ? animationsCount : 0) + 1);
if (animatable != null)
{
foreach (var animation in Animations)
for (var i = 0; i < animationsCount; i++)
{
var animation = animations[i];
var obsMatch = match.Activator;
if (match.Result == SelectorMatchResult.AlwaysThisType ||
@ -133,17 +141,19 @@ namespace Avalonia.Styling
var sub = animation.Apply(animatable, null, obsMatch);
subs.Add(sub);
}
}
}
foreach (var setter in Setters)
for (var i = 0; i < settersCount; i++)
{
var setter = setters[i];
var sub = setter.Apply(this, control, match.Activator);
subs.Add(sub);
}
subs.Add(Disposable.Create((subs, Subscriptions) , state => state.Subscriptions.Remove(state.subs)));
controlSubscriptions.Add(subs);
controlSubscriptions.Add(Disposable.Create(() => Subscriptions.Remove(subs)));
Subscriptions.Add(subs);
}
@ -151,18 +161,23 @@ namespace Avalonia.Styling
}
else if (control == container)
{
var setters = Setters;
var settersCount = setters.Count;
var controlSubscriptions = GetSubscriptions(control);
var subs = new CompositeDisposable(Setters.Count);
var subs = new CompositeDisposable(settersCount + 1);
foreach (var setter in Setters)
for (var i = 0; i < settersCount; i++)
{
var setter = setters[i];
var sub = setter.Apply(this, control, null);
subs.Add(sub);
}
subs.Add(Disposable.Create((subs, Subscriptions), state => state.Subscriptions.Remove(state.subs)));
controlSubscriptions.Add(subs);
controlSubscriptions.Add(Disposable.Create(() => Subscriptions.Remove(subs)));
Subscriptions.Add(subs);
return true;
}

4
src/Avalonia.Styling/Styling/Styles.cs

@ -239,8 +239,10 @@ namespace Avalonia.Styling
/// <inheritdoc/>
public bool Remove(IStyle item) => _styles.Remove(item);
public AvaloniaList<IStyle>.Enumerator GetEnumerator() => _styles.GetEnumerator();
/// <inheritdoc/>
public IEnumerator<IStyle> GetEnumerator() => _styles.GetEnumerator();
IEnumerator<IStyle> IEnumerable<IStyle>.GetEnumerator() => _styles.GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator();

26
src/Avalonia.Visuals/Visual.cs

@ -386,11 +386,18 @@ namespace Avalonia
AttachedToVisualTree?.Invoke(this, e);
InvalidateVisual();
if (VisualChildren != null)
var visualChildren = VisualChildren;
if (visualChildren != null)
{
foreach (Visual child in VisualChildren.OfType<Visual>())
var visualChildrenCount = visualChildren.Count;
for (var i = 0; i < visualChildrenCount; i++)
{
child.OnAttachedToVisualTreeCore(e);
if (visualChildren[i] is Visual child)
{
child.OnAttachedToVisualTreeCore(e);
}
}
}
}
@ -415,11 +422,18 @@ namespace Avalonia
DetachedFromVisualTree?.Invoke(this, e);
e.Root?.Renderer?.AddDirty(this);
if (VisualChildren != null)
var visualChildren = VisualChildren;
if (visualChildren != null)
{
foreach (Visual child in VisualChildren.OfType<Visual>())
var visualChildrenCount = visualChildren.Count;
for (var i = 0; i < visualChildrenCount; i++)
{
child.OnDetachedFromVisualTreeCore(e);
if (visualChildren[i] is Visual child)
{
child.OnDetachedFromVisualTreeCore(e);
}
}
}
}

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

@ -0,0 +1,47 @@
using System;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
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.Attach(_control, UnitTestApplication.Current);
styles.Detach();
}
public void Dispose()
{
_app.Dispose();
}
}
}
Loading…
Cancel
Save