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

9
src/Avalonia.Base/StyledElement.cs

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

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

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

6
src/Avalonia.Base/ValueStore.cs

@ -33,12 +33,6 @@ namespace Avalonia
_values = new AvaloniaPropertyValueStore<IValue>();
}
public bool IsInitializing
{
get => _values.IsInitializing;
set => _values.IsInitializing = value;
}
public void BeginBatchUpdate()
{
_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;
[Benchmark]
[Arguments(false)]
[Arguments(true)]
public void Add(bool isInitializing)
public void Add()
{
var store = new AvaloniaPropertyValueStore<object> { IsInitializing = isInitializing };
var store = new AvaloniaPropertyValueStore<object>();
for (int i = 0; i < PropertyCount; i++)
{
@ -327,11 +325,9 @@ public class ValueStore_AddRemoveBenchmarks
public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties;
[Benchmark]
[Arguments(false)]
[Arguments(true)]
public void AddAndRemoveValue(bool isInitializing)
public void AddAndRemoveValue()
{
var store = new AvaloniaPropertyValueStore<object> { IsInitializing = isInitializing };
var store = new AvaloniaPropertyValueStore<object>();
for (int i = 0; i < PropertyCount; i++)
{
@ -389,11 +385,9 @@ public class ValueStore_AddRemoveInterleavedBenchmarks
public AvaloniaProperty[] Properties => UseShuffledProperties ? MockProperties.ShuffledProperties : MockProperties.LinearProperties;
[Benchmark]
[Arguments(false)]
[Arguments(true)]
public void AddAndRemoveValueInterleaved(bool isInitializing)
public void AddAndRemoveValueInterleaved()
{
var store = new AvaloniaPropertyValueStore<object> { IsInitializing = isInitializing };
var store = new AvaloniaPropertyValueStore<object>();
for (int i = 0; i < PropertyCount; i++)
{

Loading…
Cancel
Save