Browse Source

Avoid initializing properties if there is no observer. Optimize access.

pull/2910/head
Dariusz Komosinski 7 years ago
parent
commit
addc1ddce2
  1. 5
      src/Avalonia.Base/AvaloniaProperty.cs
  2. 74
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  3. 15
      tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs

5
src/Avalonia.Base/AvaloniaProperty.cs

@ -492,6 +492,11 @@ namespace Avalonia
return Name; return Name;
} }
/// <summary>
/// True if <see cref="Initialized"/> has any observers.
/// </summary>
internal bool HasNotifyInitializedObservers => _initialized.HasObservers;
/// <summary> /// <summary>
/// Notifies the <see cref="Initialized"/> observable. /// Notifies the <see cref="Initialized"/> observable.
/// </summary> /// </summary>

74
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -24,8 +24,8 @@ namespace Avalonia
new Dictionary<Type, List<AvaloniaProperty>>(); new Dictionary<Type, List<AvaloniaProperty>>();
private readonly Dictionary<Type, List<AvaloniaProperty>> _attachedCache = private readonly Dictionary<Type, List<AvaloniaProperty>> _attachedCache =
new Dictionary<Type, List<AvaloniaProperty>>(); new Dictionary<Type, List<AvaloniaProperty>>();
private readonly Dictionary<Type, List<KeyValuePair<AvaloniaProperty, object>>> _initializedCache = private readonly Dictionary<Type, List<PropertyInitializationData>> _initializedCache =
new Dictionary<Type, List<KeyValuePair<AvaloniaProperty, object>>>(); new Dictionary<Type, List<PropertyInitializationData>>();
/// <summary> /// <summary>
/// Gets the <see cref="AvaloniaPropertyRegistry"/> instance /// Gets the <see cref="AvaloniaPropertyRegistry"/> instance
@ -286,35 +286,73 @@ namespace Avalonia
property.NotifyInitialized(e); property.NotifyInitialized(e);
} }
if (!_initializedCache.TryGetValue(type, out var items)) if (!_initializedCache.TryGetValue(type, out var initializationData))
{ {
var build = new Dictionary<AvaloniaProperty, object>(); var visited = new HashSet<AvaloniaProperty>();
foreach (var property in GetRegistered(type)) initializationData = new List<PropertyInitializationData>();
foreach (AvaloniaProperty property in GetRegistered(type))
{ {
var value = !property.IsDirect ? if (property.IsDirect)
((IStyledPropertyAccessor)property).GetDefaultValue(type) : {
null; initializationData.Add(new PropertyInitializationData(property, (IDirectPropertyAccessor)property));
build.Add(property, value); }
else
{
initializationData.Add(new PropertyInitializationData(property, (IStyledPropertyAccessor)property, type));
}
visited.Add(property);
} }
foreach (var property in GetRegisteredAttached(type)) foreach (AvaloniaProperty property in GetRegisteredAttached(type))
{ {
if (!build.ContainsKey(property)) if (!visited.Contains(property))
{ {
var value = ((IStyledPropertyAccessor)property).GetDefaultValue(type); initializationData.Add(new PropertyInitializationData(property, (IStyledPropertyAccessor)property, type));
build.Add(property, value);
visited.Add(property);
} }
} }
items = build.ToList(); _initializedCache.Add(type, initializationData);
_initializedCache.Add(type, items); }
foreach (PropertyInitializationData data in initializationData)
{
if (!data.Property.HasNotifyInitializedObservers)
{
continue;
}
object value = data.IsDirect ? data.DirectAccessor.GetValue(o) : data.Value;
Notify(data.Property, value);
}
}
private readonly struct PropertyInitializationData
{
public AvaloniaProperty Property { get; }
public object Value { get; }
public bool IsDirect { get; }
public IDirectPropertyAccessor DirectAccessor { get; }
public PropertyInitializationData(AvaloniaProperty property, IDirectPropertyAccessor directAccessor)
{
Property = property;
Value = null;
IsDirect = true;
DirectAccessor = directAccessor;
} }
foreach (var i in items) public PropertyInitializationData(AvaloniaProperty property, IStyledPropertyAccessor styledAccessor, Type type)
{ {
var value = i.Key.IsDirect ? o.GetValue(i.Key) : i.Value; Property = property;
Notify(i.Key, value); Value = styledAccessor.GetDefaultValue(type);
IsDirect = false;
DirectAccessor = null;
} }
} }
} }

15
tests/Avalonia.Benchmarks/Base/AvaloniaObjectInitializationBenchmark.cs

@ -0,0 +1,15 @@
using Avalonia.Controls;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Base
{
[MemoryDiagnoser]
public class AvaloniaObjectInitializationBenchmark
{
[Benchmark(OperationsPerInvoke = 1000)]
public Button InitializeButton()
{
return new Button();
}
}
}
Loading…
Cancel
Save