Browse Source

WIP: Removed initializing, use try pattern.

pull/8571/head
Steven Kirk 4 years ago
parent
commit
cda288ff03
  1. 17
      src/Avalonia.Base/AvaloniaObject.cs
  2. 9
      src/Avalonia.Base/StyledElement.cs
  3. 87
      src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
  4. 6
      src/Avalonia.Base/ValueStore.cs
  5. 18
      tests/Avalonia.Benchmarks/Base/ValueStoreAddRemoveBenchmarks.cs

17
src/Avalonia.Base/AvaloniaObject.cs

@ -25,7 +25,6 @@ namespace Avalonia
private List<AvaloniaObject>? _inheritanceChildren; private List<AvaloniaObject>? _inheritanceChildren;
private ValueStore? _values; private ValueStore? _values;
private bool _batchUpdate; private bool _batchUpdate;
private bool _isInitializing;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaObject"/> class. /// Initializes a new instance of the <see cref="AvaloniaObject"/> class.
@ -126,10 +125,6 @@ namespace Avalonia
if (_values is null) if (_values is null)
{ {
_values = new ValueStore(this); _values = new ValueStore(this);
_values.IsInitializing = _isInitializing;
_isInitializing = false;
if (_batchUpdate) if (_batchUpdate)
_values.BeginBatchUpdate(); _values.BeginBatchUpdate();
} }
@ -497,18 +492,6 @@ namespace Avalonia
_batchUpdate = false; _batchUpdate = false;
_values?.EndBatchUpdate(); _values?.EndBatchUpdate();
} }
internal void SetValueStoreIsInitializing(bool isInitializing)
{
if (_values is null)
{
_isInitializing = isInitializing;
return;
}
_values.IsInitializing = isInitializing;
}
/// <inheritdoc/> /// <inheritdoc/>
internal void AddInheritanceChild(AvaloniaObject child) internal void AddInheritanceChild(AvaloniaObject child)

9
src/Avalonia.Base/StyledElement.cs

@ -306,11 +306,6 @@ namespace Avalonia
public virtual void BeginInit() public virtual void BeginInit()
{ {
++_initCount; ++_initCount;
if (_initCount == 1)
{
SetValueStoreIsInitializing(true);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -328,8 +323,6 @@ namespace Avalonia
ApplyStyling(); ApplyStyling();
InitializeIfNeeded(); InitializeIfNeeded();
} }
SetValueStoreIsInitializing(false);
} }
} }
@ -347,13 +340,11 @@ namespace Avalonia
try try
{ {
BeginBatchUpdate(); BeginBatchUpdate();
SetValueStoreIsInitializing(true);
AvaloniaLocator.Current.GetService<IStyler>()?.ApplyStyles(this); AvaloniaLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
} }
finally finally
{ {
EndBatchUpdate(); EndBatchUpdate();
SetValueStoreIsInitializing(false);
} }
_styled = true; _styled = true;

87
src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs

@ -9,6 +9,7 @@ namespace Avalonia.Utilities
/// <typeparam name="TValue">Stored value type.</typeparam> /// <typeparam name="TValue">Stored value type.</typeparam>
internal struct AvaloniaPropertyValueStore<TValue> internal struct AvaloniaPropertyValueStore<TValue>
{ {
private const int DefaultInitialCapacity = 4;
private Entry[]? _entries; private Entry[]? _entries;
private int _entryCount; private int _entryCount;
@ -16,19 +17,19 @@ namespace Avalonia.Utilities
{ {
_entries = null; _entries = null;
_entryCount = 0; _entryCount = 0;
IsInitializing = false; }
InitialSize = 4;
public AvaloniaPropertyValueStore(int capactity)
{
_entries = new Entry[capactity];
_entryCount = 0;
} }
public int Count => _entryCount; public int Count => _entryCount;
public bool IsInitializing { get; set; }
public int InitialSize { get; set; }
public TValue this[int index] => _entries![index].Value; public TValue this[int index] => _entries![index].Value;
private EntryIndex LookupEntry(int propertyId) private bool TryGetEntry(int propertyId, out int index)
{ {
int checkIndex; int checkIndex;
int iLo = 0; int iLo = 0;
@ -36,7 +37,8 @@ namespace Avalonia.Utilities
if (iHi <= 0) if (iHi <= 0)
{ {
return new EntryIndex(0, found: false); index = 0;
return false;
} }
// Do a binary search to find the value // Do a binary search to find the value
@ -47,7 +49,8 @@ namespace Avalonia.Utilities
if (propertyId == checkIndex) if (propertyId == checkIndex)
{ {
return new EntryIndex(iPv, found: true); index = iPv;
return true;
} }
if (propertyId <= checkIndex) if (propertyId <= checkIndex)
@ -67,7 +70,8 @@ namespace Avalonia.Utilities
if (checkIndex == propertyId) if (checkIndex == propertyId)
{ {
return new EntryIndex(iLo, found: true); index = iLo;
return true;
} }
if (checkIndex > propertyId) if (checkIndex > propertyId)
@ -79,22 +83,20 @@ namespace Avalonia.Utilities
iLo++; iLo++;
} while (iLo < iHi); } while (iLo < iHi);
return new EntryIndex(iLo, found: false); index = iLo;
return false;
} }
public bool TryGetValue(AvaloniaProperty property, [MaybeNullWhen(false)] out TValue value) public bool TryGetValue(AvaloniaProperty property, [MaybeNullWhen(false)] out TValue value)
{ {
var entryIndex = LookupEntry(property.Id); if (TryGetEntry(property.Id, out var index))
if (!entryIndex.Found)
{ {
value = default; value = _entries![index].Value;
return false; return true;
} }
value = _entries![entryIndex.Index].Value; value = default;
return false;
return true;
} }
private void InsertEntry(Entry entry, int entryIndex) private void InsertEntry(Entry entry, int entryIndex)
@ -103,9 +105,7 @@ namespace Avalonia.Utilities
{ {
if (_entryCount == _entries!.Length) if (_entryCount == _entries!.Length)
{ {
// We want to have more aggressive resizing when initializing. const double growthFactor = 1.2;
var growthFactor = IsInitializing ? 2.0 : 1.2;
var newSize = (int)(_entryCount * growthFactor); var newSize = (int)(_entryCount * growthFactor);
if (newSize == _entryCount) if (newSize == _entryCount)
@ -137,11 +137,7 @@ namespace Avalonia.Utilities
} }
else else
{ {
if (_entries is null) _entries ??= new Entry[DefaultInitialCapacity];
{
_entries = new Entry[InitialSize];
}
_entries[0] = entry; _entries[0] = entry;
} }
@ -151,41 +147,28 @@ namespace Avalonia.Utilities
public void AddValue(AvaloniaProperty property, TValue value) public void AddValue(AvaloniaProperty property, TValue value)
{ {
var propertyId = property.Id; var propertyId = property.Id;
var index = LookupEntry(propertyId); TryGetEntry(propertyId, out var index);
InsertEntry(new Entry(propertyId, value), index);
InsertEntry(new Entry(propertyId, value), index.Index);
} }
public void SetValue(AvaloniaProperty property, TValue value) public void SetValue(AvaloniaProperty property, TValue value)
{ {
var propertyId = property.Id; var propertyId = property.Id;
var entryIndex = LookupEntry(propertyId); TryGetEntry(propertyId, out var index);
_entries![index] = new Entry(propertyId, value);
_entries![entryIndex.Index] = new Entry(propertyId, value);
} }
public void Remove(AvaloniaProperty property) public bool Remove(AvaloniaProperty property)
{ {
var entry = LookupEntry(property.Id); if (TryGetEntry(property.Id, out var index))
if (!entry.Found) return;
Array.Copy(_entries!, entry.Index + 1, _entries!, entry.Index, _entryCount - entry.Index - 1);
_entryCount--;
_entries![_entryCount] = default;
}
private readonly struct EntryIndex
{
public readonly int Index;
public readonly bool Found;
public EntryIndex(int index, bool found)
{ {
Index = index; Array.Copy(_entries!, index + 1, _entries!, index, _entryCount - index - 1);
Found = found; _entryCount--;
_entries![_entryCount] = default;
return true;
} }
return false;
} }
private readonly struct Entry private readonly struct Entry

6
src/Avalonia.Base/ValueStore.cs

@ -33,12 +33,6 @@ namespace Avalonia
_values = new AvaloniaPropertyValueStore<IValue>(); _values = new AvaloniaPropertyValueStore<IValue>();
} }
public bool IsInitializing
{
get => _values.IsInitializing;
set => _values.IsInitializing = value;
}
public void BeginBatchUpdate() public void BeginBatchUpdate()
{ {
_batchUpdate ??= new BatchUpdate(this); _batchUpdate ??= new BatchUpdate(this);

18
tests/Avalonia.Benchmarks/Base/ValueStoreAddRemoveBenchmarks.cs

@ -279,11 +279,9 @@ public class ValueStore_AddBenchmarks
public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties; public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties;
[Benchmark] [Benchmark]
[Arguments(false)] public void Add()
[Arguments(true)]
public void Add(bool isInitializing)
{ {
var store = new AvaloniaPropertyValueStore<object> { IsInitializing = isInitializing }; var store = new AvaloniaPropertyValueStore<object>();
for (int i = 0; i < PropertyCount; i++) for (int i = 0; i < PropertyCount; i++)
{ {
@ -327,11 +325,9 @@ public class ValueStore_AddRemoveBenchmarks
public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties; public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties;
[Benchmark] [Benchmark]
[Arguments(false)] public void AddAndRemoveValue()
[Arguments(true)]
public void AddAndRemoveValue(bool isInitializing)
{ {
var store = new AvaloniaPropertyValueStore<object> { IsInitializing = isInitializing }; var store = new AvaloniaPropertyValueStore<object>();
for (int i = 0; i < PropertyCount; i++) for (int i = 0; i < PropertyCount; i++)
{ {
@ -389,11 +385,9 @@ public class ValueStore_AddRemoveInterleavedBenchmarks
public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties; public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties;
[Benchmark] [Benchmark]
[Arguments(false)] public void AddAndRemoveValueInterleaved()
[Arguments(true)]
public void AddAndRemoveValueInterleaved(bool isInitializing)
{ {
var store = new AvaloniaPropertyValueStore<object> { IsInitializing = isInitializing }; var store = new AvaloniaPropertyValueStore<object>();
for (int i = 0; i < PropertyCount; i++) for (int i = 0; i < PropertyCount; i++)
{ {

Loading…
Cancel
Save