From 3640d4dc1bb300162fa3b86bbcb797fb889ced3e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 6 Sep 2016 12:14:38 -0500 Subject: [PATCH 1/2] Implemented optimization to only construct a dictionary for priority levels if there is more than one in use. This drops memory consumption in most applications by a reasonable amount since most don't use multiple bindings on single properties (like animation and a local binding). --- src/Avalonia.Base/Avalonia.Base.csproj | 1 + src/Avalonia.Base/PriorityValue.cs | 2 +- .../Utilities/SingleOrDictionary.cs | 147 ++++++++++++++++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Base/Utilities/SingleOrDictionary.cs diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 887d3ff9bd..46e24aa15a 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -118,6 +118,7 @@ + diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs index a7eb4465b3..3f4b405de9 100644 --- a/src/Avalonia.Base/PriorityValue.cs +++ b/src/Avalonia.Base/PriorityValue.cs @@ -28,7 +28,7 @@ namespace Avalonia { private readonly IPriorityValueOwner _owner; private readonly Type _valueType; - private readonly Dictionary _levels = new Dictionary(); + private readonly SingleOrDictionary _levels = new SingleOrDictionary(); private object _value; private readonly Func _validate; diff --git a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs new file mode 100644 index 0000000000..97d6b9ac95 --- /dev/null +++ b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Utilities +{ + /// + /// Stores either a single key value pair or constructs a dictionary when more than one value is stored. + /// + /// The type of the key. + /// The type of the value. + public class SingleOrDictionary : IEnumerable> + { + private KeyValuePair? singleValue; + private Dictionary dictionary; + + private bool useDictionary = false; + + public void Add(TKey key, TValue value) + { + if (singleValue != null) + { + dictionary = new Dictionary(); + ((ICollection>)dictionary).Add(singleValue.Value); + useDictionary = true; + singleValue = null; + } + + if (useDictionary) + { + dictionary.Add(key, value); + } + else + { + singleValue = new KeyValuePair(key, value); + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + if (!useDictionary) + { + if (!singleValue.HasValue || !singleValue.Value.Key.Equals(key)) + { + value = default(TValue); + return false; + } + else + { + value = singleValue.Value.Value; + return true; + } + } + else + { + return dictionary.TryGetValue(key, out value); + } + } + + public IEnumerator> GetEnumerator() + { + if (!useDictionary) + { + if (singleValue.HasValue) + { + return new SingleEnumerator>(singleValue.Value); + } + } + else + { + return dictionary.GetEnumerator(); + } + return Enumerable.Empty>().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerable Values + { + get + { + if(!useDictionary) + { + if (singleValue.HasValue) + { + return new[] { singleValue.Value.Value }; + } + } + else + { + return dictionary.Values; + } + return Enumerable.Empty(); + } + } + + private class SingleEnumerator : IEnumerator + { + private T value; + private int index = -1; + + public SingleEnumerator(T value) + { + this.value = value; + } + + public T Current + { + get + { + if (index == 0) + { + return value; + } + else + { + throw new InvalidOperationException(); + } + } + } + + object IEnumerator.Current => Current; + + public void Dispose() + { + } + + public bool MoveNext() + { + index++; + return index < 1; + } + + public void Reset() + { + index = -1; + } + } + + } +} From c90cc4bf2716b4ac62fd8972642c9a37278aca46 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 7 Sep 2016 16:03:49 -0500 Subject: [PATCH 2/2] Implemented feedback. --- .../Utilities/SingleOrDictionary.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs index 97d6b9ac95..fd984d758a 100644 --- a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs +++ b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs @@ -14,43 +14,40 @@ namespace Avalonia.Utilities /// The type of the value. public class SingleOrDictionary : IEnumerable> { - private KeyValuePair? singleValue; + private KeyValuePair? _singleValue; private Dictionary dictionary; - private bool useDictionary = false; - public void Add(TKey key, TValue value) { - if (singleValue != null) + if (_singleValue != null) { dictionary = new Dictionary(); - ((ICollection>)dictionary).Add(singleValue.Value); - useDictionary = true; - singleValue = null; + ((ICollection>)dictionary).Add(_singleValue.Value); + _singleValue = null; } - if (useDictionary) + if (dictionary != null) { dictionary.Add(key, value); } else { - singleValue = new KeyValuePair(key, value); + _singleValue = new KeyValuePair(key, value); } } public bool TryGetValue(TKey key, out TValue value) { - if (!useDictionary) + if (dictionary == null) { - if (!singleValue.HasValue || !singleValue.Value.Key.Equals(key)) + if (!_singleValue.HasValue || !_singleValue.Value.Key.Equals(key)) { value = default(TValue); return false; } else { - value = singleValue.Value.Value; + value = _singleValue.Value.Value; return true; } } @@ -62,11 +59,11 @@ namespace Avalonia.Utilities public IEnumerator> GetEnumerator() { - if (!useDictionary) + if (dictionary == null) { - if (singleValue.HasValue) + if (_singleValue.HasValue) { - return new SingleEnumerator>(singleValue.Value); + return new SingleEnumerator>(_singleValue.Value); } } else @@ -85,11 +82,11 @@ namespace Avalonia.Utilities { get { - if(!useDictionary) + if(dictionary == null) { - if (singleValue.HasValue) + if (_singleValue.HasValue) { - return new[] { singleValue.Value.Value }; + return new[] { _singleValue.Value.Value }; } } else