From 7c5545f1088347973606f3283f9a9dfd218d3229 Mon Sep 17 00:00:00 2001 From: donandren Date: Mon, 6 Jun 2016 00:00:00 +0300 Subject: [PATCH] performance optimization of - AvaloniaObject.GetValue - AvaloniaPropertyRegistry.IsRegistered - AvaloniaProperty.GetMetadata --- src/Avalonia.Base/AvaloniaObject.cs | 45 ++++++++++------ src/Avalonia.Base/AvaloniaProperty.cs | 54 +++++++++---------- src/Avalonia.Base/AvaloniaPropertyRegistry.cs | 44 +++++++++++---- 3 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index b0db89f7ea..fd4f495edb 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -261,25 +261,12 @@ namespace Avalonia } else { - object result = AvaloniaProperty.UnsetValue; - PriorityValue value; - if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property)) { ThrowNotRegistered(property); } - if (_values.TryGetValue(property, out value)) - { - result = value.Value; - } - - if (result == AvaloniaProperty.UnsetValue) - { - result = GetDefaultValue(property); - } - - return result; + return GetValueInternal(property); } } @@ -767,7 +754,7 @@ namespace Avalonia { if (property.Inherits && _inheritanceParent != null) { - return _inheritanceParent.GetValue(property); + return (_inheritanceParent as AvaloniaObject).GetValueInternal(property); } else { @@ -775,6 +762,32 @@ namespace Avalonia } } + /// + /// Gets a value + /// without check for registered as this can slow getting the value + /// this method is intended for internal usage in AvaloniaObject only + /// it's called only after check the property is registered + /// + /// The property. + /// The value. + private object GetValueInternal(AvaloniaProperty property) + { + object result = AvaloniaProperty.UnsetValue; + PriorityValue value; + + if (_values.TryGetValue(property, out value)) + { + result = value.Value; + } + + if (result == AvaloniaProperty.UnsetValue) + { + result = GetDefaultValue(property); + } + + return result; + } + /// /// Given a returns a registered avalonia property that is /// equal or throws if not found. @@ -849,4 +862,4 @@ namespace Avalonia throw new ArgumentException($"Property '{p.Name} not registered on '{this.GetType()}"); } } -} +} \ No newline at end of file diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index b815c90295..0f5500b116 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -26,6 +26,7 @@ namespace Avalonia private readonly Subject _changed; private readonly PropertyMetadata _defaultMetadata; private readonly Dictionary _metadata; + private readonly Dictionary _metadataCache = new Dictionary(); /// /// Initializes a new instance of the class. @@ -73,8 +74,8 @@ namespace Avalonia /// The new owner type. /// Optional overridden metadata. protected AvaloniaProperty( - AvaloniaProperty source, - Type ownerType, + AvaloniaProperty source, + Type ownerType, PropertyMetadata metadata) { Contract.Requires(source != null); @@ -163,11 +164,11 @@ namespace Avalonia /// object. /// /// - /// When a property changes, change notifications are sent to all property subscribers; - /// for example via the observable and and the + /// When a property changes, change notifications are sent to all property subscribers; + /// for example via the observable and and the /// event. If this callback is set for a property, /// then it will be called before and after these notifications take place. The bool argument - /// will be true before the property change notifications are sent and false afterwards. This + /// will be true before the property change notifications are sent and false afterwards. This /// callback is intended to support Control.IsDataContextChanging. /// public Action Notifying { get; } @@ -273,8 +274,8 @@ namespace Avalonia defaultBindingMode: defaultBindingMode); var result = new StyledProperty( - name, - typeof(TOwner), + name, + typeof(TOwner), metadata, inherits, notifying); @@ -423,24 +424,9 @@ namespace Avalonia /// public PropertyMetadata GetMetadata() where T : IAvaloniaObject { - var type = typeof(T); - - while (type != null) - { - PropertyMetadata result; - - if (_metadata.TryGetValue(type, out result)) - { - return result; - } - - type = type.GetTypeInfo().BaseType; - } - - return _defaultMetadata; + return GetMetadata(typeof(T)); } - /// /// Gets the property metadata for the specified type. /// @@ -448,22 +434,33 @@ namespace Avalonia /// /// The property metadata. /// + /// public PropertyMetadata GetMetadata(Type type) { Contract.Requires(type != null); - while (type != null) + PropertyMetadata result; + Type currentType = type; + + if (_metadataCache.TryGetValue(type, out result)) { - PropertyMetadata result; + return result; + } - if (_metadata.TryGetValue(type, out result)) + while (currentType != null) + { + if (_metadata.TryGetValue(currentType, out result)) { + _metadataCache[type] = result; + return result; } - type = type.GetTypeInfo().BaseType; + currentType = currentType.GetTypeInfo().BaseType; } + _metadataCache[type] = _defaultMetadata; + return _defaultMetadata; } @@ -523,6 +520,7 @@ namespace Avalonia var baseMetadata = GetMetadata(type); metadata.Merge(baseMetadata, this); _metadata.Add(type, metadata); + _metadataCache.Clear(); } [DebuggerHidden] @@ -551,4 +549,4 @@ namespace Avalonia public override string ToString() => "(unset)"; } } -} +} \ No newline at end of file diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs index eaff31fbac..a1d6db64df 100644 --- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs +++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs @@ -20,6 +20,12 @@ namespace Avalonia private readonly Dictionary> _registered = new Dictionary>(); + /// + /// The registered properties by type cached values to increase performance. + /// + private readonly Dictionary> _registeredCache = + new Dictionary>(); + /// /// The registered attached properties by owner type. /// @@ -103,21 +109,38 @@ namespace Avalonia /// public AvaloniaProperty FindRegistered(Type type, AvaloniaProperty property) { - while (type != null) + Type currentType = type; + Dictionary cache; + AvaloniaProperty result; + + if (_registeredCache.TryGetValue(type, out cache)) + { + if (cache.TryGetValue(property.Id, out result)) + { + return result; + } + } + + while (currentType != null) { Dictionary inner; - if (_registered.TryGetValue(type, out inner)) + if (_registered.TryGetValue(currentType, out inner)) { - AvaloniaProperty result; - if (inner.TryGetValue(property.Id, out result)) { + if (cache == null) + { + _registeredCache[type] = cache = new Dictionary(); + } + + cache[property.Id] = result; + return result; } } - type = type.GetTypeInfo().BaseType; + currentType = currentType.GetTypeInfo().BaseType; } return null; @@ -130,7 +153,7 @@ namespace Avalonia /// The property. /// The registered property or null if not found. /// - /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a + /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a /// different object but is equal according to . /// public AvaloniaProperty FindRegistered(object o, AvaloniaProperty property) @@ -143,7 +166,7 @@ namespace Avalonia /// /// The type. /// - /// The property name. If an attached property it should be in the form + /// The property name. If an attached property it should be in the form /// "OwnerType.PropertyName". /// /// @@ -183,13 +206,12 @@ namespace Avalonia return results.FirstOrDefault(x => x.Name == propertyName); } - /// /// Finds a registered property on an object by name. /// /// The object. /// - /// The property name. If an attached property it should be in the form + /// The property name. If an attached property it should be in the form /// "OwnerType.PropertyName". /// /// @@ -276,6 +298,8 @@ namespace Avalonia inner.Add(property.Id, property); } } + + _registeredCache.Clear(); } } -} +} \ No newline at end of file