Skip to content

Commit

Permalink
perf: Improve DependencyProperty propagation
Browse files Browse the repository at this point in the history
- Remove linq usage in GetChildrenDependencyObjects
- Use HashTableEx in dependency property inheritance propagation to avoid JIT costs and improve lookup performance.
  • Loading branch information
jeromelaban committed Sep 7, 2021
1 parent 5f50f1f commit ea6381e
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 14 deletions.
54 changes: 54 additions & 0 deletions src/Uno.UI/UI/Xaml/DependencyObjectStore.AncestorsDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#nullable enable

using System;
using Uno.UI.DataBinding;
using System.Collections.Generic;
using Uno.Extensions;
using Uno.Logging;
using Uno.Diagnostics.Eventing;
using Uno.Disposables;
using System.Linq;
using System.Threading;
using Uno.Collections;
using System.Runtime.CompilerServices;
using System.Diagnostics;
using Windows.UI.Xaml.Data;
using Uno.UI;
using System.Collections;

#if XAMARIN_ANDROID
using View = Android.Views.View;
#elif XAMARIN_IOS_UNIFIED
using View = UIKit.UIView;
#elif XAMARIN_IOS
using View = MonoTouch.UIKit.UIView;
#endif

namespace Windows.UI.Xaml
{
public partial class DependencyObjectStore : IDisposable
{
private class AncestorsDictionary
{
private readonly HashtableEx _entries = new HashtableEx();

internal bool TryGetValue(object key, out bool isAncestor)
{
if (_entries.TryGetValue(key, out var value))
{
isAncestor = (bool)value!;
return true;
}

isAncestor = false;
return false;
}

internal void Set(object key, bool isAncestor)
=> _entries[key] = isAncestor;

internal void Clear()
=> _entries.Clear();
}
}
}
20 changes: 13 additions & 7 deletions src/Uno.UI/UI/Xaml/DependencyObjectStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1183,11 +1183,17 @@ private void UpdateChildResourceBindings(bool isThemeChangedUpdate)
/// </remarks>
private IEnumerable<DependencyObject> GetChildrenDependencyObjects()
{
var propertyValues = _properties.GetAllDetails()
.Except(_properties.DataContextPropertyDetails, _properties.TemplatedParentPropertyDetails)
.Select(d => GetValue(d));
foreach (var propertyValue in propertyValues)
foreach (var propertyDetail in _properties.GetAllDetails())
{
if(propertyDetail == null
|| propertyDetail == _properties.DataContextPropertyDetails
|| propertyDetail == _properties.TemplatedParentPropertyDetails)
{
continue;
}

var propertyValue = GetValue(propertyDetail);

if (propertyValue is IEnumerable<DependencyObject> dependencyObjectCollection &&
// Try to avoid enumerating collections that shouldn't be enumerated, since we may be encountering user-defined values. This may need to be refined to somehow only consider values coming from the framework itself.
(propertyValue is ICollection || propertyValue is DependencyObjectCollectionBase)
Expand Down Expand Up @@ -1326,7 +1332,7 @@ private void PropagateInheritedNonLocalProperties(DependencyObjectStore? childSt
// properties.

// Ancestors is a local cache to avoid walking up the tree multiple times.
var ancestors = new Dictionary<object, bool>();
var ancestors = new AncestorsDictionary();

// This alias is used to avoid the resolution of the underlying WeakReference during the
// call to IsAncestor.
Expand Down Expand Up @@ -1374,7 +1380,7 @@ void Propagate(DependencyObjectStore store)
}
}

private static bool IsAncestor(DependencyObject? instance, Dictionary<object, bool> map, object ancestor)
private static bool IsAncestor(DependencyObject? instance, AncestorsDictionary map, object ancestor)
{
#if DEBUG
var hashSet = new HashSet<DependencyObject>(Uno.ReferenceEqualityComparer<DependencyObject>.Default);
Expand Down Expand Up @@ -1417,7 +1423,7 @@ private static bool IsAncestor(DependencyObject? instance, Dictionary<object, bo

}

map[ancestor] = isAncestor;
map.Set(ancestor, isAncestor);
}

return isAncestor;
Expand Down
14 changes: 7 additions & 7 deletions src/Uno.UI/UI/Xaml/DependencyPropertyDetailsCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace Windows.UI.Xaml
/// </remarks>
partial class DependencyPropertyDetailsCollection : IDisposable
{
private static readonly DependencyPropertyDetails[] Empty = new DependencyPropertyDetails[0];
private static readonly DependencyPropertyDetails?[] Empty = new DependencyPropertyDetails?[0];

private readonly Type _ownerType;
private readonly ManagedWeakReference _ownerReference;
Expand All @@ -33,9 +33,9 @@ partial class DependencyPropertyDetailsCollection : IDisposable
private DependencyPropertyDetails? _dataContextPropertyDetails;
private DependencyPropertyDetails? _templatedParentPropertyDetails;

private readonly static ArrayPool<DependencyPropertyDetails> _pool = ArrayPool<DependencyPropertyDetails>.Create(500, 100);
private readonly static ArrayPool<DependencyPropertyDetails?> _pool = ArrayPool<DependencyPropertyDetails?>.Create(500, 100);

private DependencyPropertyDetails[]? _entries;
private DependencyPropertyDetails?[]? _entries;
private int _entriesLength;
private int _minId;
private int _maxId;
Expand All @@ -56,7 +56,7 @@ public DependencyPropertyDetailsCollection(Type ownerType, ManagedWeakReference
_templatedParentProperty = templatedParentProperty;
}

private DependencyPropertyDetails[] Entries
private DependencyPropertyDetails?[] Entries
{
get
{
Expand Down Expand Up @@ -154,7 +154,7 @@ public DependencyPropertyDetails FindPropertyDetails(DependencyProperty property
if (forceCreate)
{
int newEntriesSize;
DependencyPropertyDetails[] newEntries;
DependencyPropertyDetails?[] newEntries;

if (entryIndex < 0)
{
Expand Down Expand Up @@ -210,7 +210,7 @@ private bool TryResolveDefaultValueFromProviders(DependencyProperty property, ou
return false;
}

private void AssignEntries(DependencyPropertyDetails[] newEntries, int newSize)
private void AssignEntries(DependencyPropertyDetails?[] newEntries, int newSize)
{
ReturnEntriesToPool();

Expand All @@ -230,7 +230,7 @@ private void ReturnEntriesToPool()
}
}

internal IEnumerable<DependencyPropertyDetails> GetAllDetails() => Entries.Trim();
internal DependencyPropertyDetails?[] GetAllDetails() => Entries;

/// <summary>
/// Adds a default value provider.
Expand Down

0 comments on commit ea6381e

Please sign in to comment.