Browse Source

Merge pull request #2127 from AvaloniaUI/perf/cache-ao-initialization

[Perf] Cache AvaloniaObject initialization notifications.
Steven Kirk 6 years ago
parent
commit
fe74dfe538
2 changed files with 57 additions and 44 deletions
  1. 2 44
      src/Avalonia.Base/AvaloniaObject.cs
  2. 55 0
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs

+ 2 - 44
src/Avalonia.Base/AvaloniaObject.cs

@@ -22,27 +22,10 @@ namespace Avalonia
     /// </remarks>
     public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
     {
-        /// <summary>
-        /// The parent object that inherited values are inherited from.
-        /// </summary>
         private IAvaloniaObject _inheritanceParent;
-
-        /// <summary>
-        /// Maintains a list of direct property binding subscriptions so that the binding source
-        /// doesn't get collected.
-        /// </summary>
         private List<DirectBindingSubscription> _directBindings;
-
-        /// <summary>
-        /// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
-        /// </summary>
         private PropertyChangedEventHandler _inpcChanged;
-
-        /// <summary>
-        /// Event handler for <see cref="PropertyChanged"/> implementation.
-        /// </summary>
         private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;
-
         private ValueStore _values;
         private ValueStore Values => _values ?? (_values = new ValueStore(this));
 
@@ -52,32 +35,7 @@ namespace Avalonia
         public AvaloniaObject()
         {
             VerifyAccess();
-
-            void Notify(AvaloniaProperty property)
-            {
-                object value = property.IsDirect ?
-                    ((IDirectPropertyAccessor)property).GetValue(this) :
-                    ((IStyledPropertyAccessor)property).GetDefaultValue(GetType());
-
-                var e = new AvaloniaPropertyChangedEventArgs(
-                    this,
-                    property,
-                    AvaloniaProperty.UnsetValue,
-                    value,
-                    BindingPriority.Unset);
-
-                property.NotifyInitialized(e);
-            }
-
-            foreach (var property in AvaloniaPropertyRegistry.Instance.GetRegistered(this))
-            {
-                Notify(property);
-            }
-
-            foreach (var property in AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(this.GetType()))
-            {
-                Notify(property);
-            }
+            AvaloniaPropertyRegistry.Instance.NotifyInitialized(this);
         }
 
         /// <summary>
@@ -628,7 +586,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="property">The property.</param>
         /// <returns>The default value.</returns>
-        internal object GetDefaultValue(AvaloniaProperty property)
+        private object GetDefaultValue(AvaloniaProperty property)
         {
             if (property.Inherits && InheritanceParent is AvaloniaObject aobj)
                 return aobj.GetValue(property);

+ 55 - 0
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using Avalonia.Data;
 
 namespace Avalonia
 {
@@ -23,6 +24,8 @@ namespace Avalonia
             new Dictionary<Type, List<AvaloniaProperty>>();
         private readonly Dictionary<Type, List<AvaloniaProperty>> _attachedCache =
             new Dictionary<Type, List<AvaloniaProperty>>();
+        private readonly Dictionary<Type, List<KeyValuePair<AvaloniaProperty, object>>> _initializedCache =
+            new Dictionary<Type, List<KeyValuePair<AvaloniaProperty, object>>>();
 
         /// <summary>
         /// Gets the <see cref="AvaloniaPropertyRegistry"/> instance
@@ -226,6 +229,7 @@ namespace Avalonia
             }
             
             _registeredCache.Clear();
+            _initializedCache.Clear();
         }
 
         /// <summary>
@@ -261,6 +265,57 @@ namespace Avalonia
             }
             
             _attachedCache.Clear();
+            _initializedCache.Clear();
+        }
+
+        internal void NotifyInitialized(AvaloniaObject o)
+        {
+            Contract.Requires<ArgumentNullException>(o != null);
+
+            var type = o.GetType();
+
+            void Notify(AvaloniaProperty property, object value)
+            {
+                var e = new AvaloniaPropertyChangedEventArgs(
+                    o,
+                    property,
+                    AvaloniaProperty.UnsetValue,
+                    value,
+                    BindingPriority.Unset);
+
+                property.NotifyInitialized(e);
+            }
+
+            if (!_initializedCache.TryGetValue(type, out var items))
+            {
+                var build = new Dictionary<AvaloniaProperty, object>();
+
+                foreach (var property in GetRegistered(type))
+                {
+                    var value = !property.IsDirect ?
+                        ((IStyledPropertyAccessor)property).GetDefaultValue(type) :
+                        null;
+                    build.Add(property, value);
+                }
+
+                foreach (var property in GetRegisteredAttached(type))
+                {
+                    if (!build.ContainsKey(property))
+                    {
+                        var value = ((IStyledPropertyAccessor)property).GetDefaultValue(type);
+                        build.Add(property, value);
+                    }
+                }
+
+                items = build.ToList();
+                _initializedCache.Add(type, items);
+            }
+
+            foreach (var i in items)
+            {
+                var value = i.Key.IsDirect ? o.GetValue(i.Key) : i.Value;
+                Notify(i.Key, value);
+            }
         }
     }
 }