RecyclableLongList.Enumerator performance improvements. Versioning removing from default enumerator. Will be implemented as .GetVersionedEnumerator() due to high impact on performance.

This commit is contained in:
mlemanczyk committed 2023-06-24 19:48:39 +02:00
1 parent 0961dc4f6b
commit 175fb617f8
2 files changed
+180 -222

No files matched your search

@@ -1,149 +1,198 @@
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
public sealed class RecyclableLongListEnumerator<T> : IEnumerator<T> public partial class RecyclableLongList<T>
{ {
public static void ThrowListModifiedException() => throw new InvalidOperationException("Can't move to the next item, because the collection was modified. You must restart the enumeration by calling Reset(), if you want to enumerate the collection again"); // & WAS SLOWER
// [StructLayout(LayoutKind.Sequential, Pack = 1)]
private int _blockSize; // [StructLayout(LayoutKind.Sequential)]
private int _blockSizeMinus1; public struct Enumerator : IEnumerator<T>
private int _currentBlockIndex = 0;
private int _currentIndex = RecyclableDefaults.ItemNotFoundIndex;
private int _lastBlockWithData;
private RecyclableLongList<T>? _list;
private RecyclableCollectionVersion? _listVersion;
private T[][] _memoryBlocks;
private int _nextItemBlockIndex;
private int _nextItemIndex;
//private ulong _version;
private bool _isVersionChanged;
private void VersionChanged() => _isVersionChanged = true;
private T? _current;
public RecyclableLongListEnumerator(RecyclableLongList<T> list)
{ {
_listVersion = list._version; // & WAS SLOWER WITHOUT
list._version!.VersionChanged += VersionChanged; #nullable disable
//_listVersion!.AddEnumerator(); private static readonly T _default = default;
_blockSize = list._blockSize; #nullable restore
_blockSizeMinus1 = list._blockSizeMinus1;
_lastBlockWithData = list._lastBlockWithData;
_list = list;
//_version = _listVersion!.Version;
_memoryBlocks = list._memoryBlocks;
_nextItemBlockIndex = list._nextItemBlockIndex;
_nextItemIndex = _lastBlockWithData == _nextItemBlockIndex ? _list._nextItemIndex : _blockSize;
}
public T Current => _current ?? throw new InvalidOperationException("The enumerator wasn't initialized. You need to call MoveNext() before getting the current iterator value"); private static readonly bool _isReferenceType = !typeof(T).IsValueType;
object? IEnumerator.Current => _current;
public void Dispose() private int _currentBlockIndex;
{ private int _currentItemIndex;
if (_listVersion != null) private readonly RecyclableLongList<T> _list;
{ // & WAS SLOWER WITHOUT
_listVersion.VersionChanged -= VersionChanged; #nullable disable
//_listVersion.RemoveEnumerator(); private T _current;
_listVersion = null; #nullable restore
} // & WAS SLOWER
// private readonly ulong _enumeratorVersion;
// public T Current => _enumeratorVersion != _list._version ? throw new InvalidOperationException() : _current;
if (_list != null) public readonly T Current => _current;
{ // & WAS SLOWER
_list = null; // get => _currentItemIndex > 0
} // ? _list._memoryBlocks[_currentBlockIndex][_currentItemIndex - 1]
// : _list._memoryBlocks[_currentBlockIndex - 1][_list._blockSize - 1];
// & WAS SLOWER
// get => _currentItemIndex != RecyclableDefaults.BlockIndexLimit
// ? _currentItemIndex > 0
// ? _list._memoryBlocks[_currentBlockIndex][_currentItemIndex - 1]
// & WAS SLOWER
// : _list._memoryBlocks[_currentBlockIndex - 1][_list._blockSize - 1]
// ? _current
// : throw new IndexOutOfRangeException();
// & WAS SLOWER
// {
// get
// {
// if (_enumeratorVersion != _list._version)
// {
// throw new InvalidOperationException();
// }
GC.SuppressFinalize(this); // if (_list._longCount == 0 || _currentItemIndex == RecyclableDefaults.BlockIndexLimit)
} // {
// throw new IndexOutOfRangeException();
// }
// Contract.EndContractBlock();
// return _currentItemIndex > 0
// ? _list._memoryBlocks[_currentBlockIndex][_currentItemIndex - 1]
// : _list._memoryBlocks[_currentBlockIndex - 1][_list._blockSize - 1];
// }
// }
// & WAS SLOWER
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// [MethodImpl(MethodImplOptions.NoInlining)]
// [MethodImpl(MethodImplOptions.AggressiveOptimization)]
// [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public bool MoveNext() public bool MoveNext()
{ {
if (_list != null) // & WAS SLOWER
// if (_enumeratorVersion != _list._version)
// {
// throw new InvalidOperationException();
// }
//
// Contract.EndContractBlock();
// & WAS SLOWER WITHOUT
var list = _list;
// & WAS SLOWER
// ref var currentBlockIndex = ref _currentBlockIndex;
// & WAS SLOWER
// ref var currentItemIndex = ref _currentItemIndex;
if (_currentBlockIndex < list._lastBlockWithData)
{ {
if (_isVersionChanged) // & WAS SLOWER WITHOUT
_current = list._memoryBlocks[_currentBlockIndex][_currentItemIndex];
if (_currentItemIndex + 1 < list._blockSize)
{ {
_current = default; _currentItemIndex++;
ThrowListModifiedException();
return false;
}
else if (_currentBlockIndex < _lastBlockWithData)
{
if (_currentIndex < _blockSizeMinus1)
{
_currentIndex++;
} }
else else
{ {
_currentIndex = 0;
_currentBlockIndex++; _currentBlockIndex++;
if (_currentBlockIndex == _lastBlockWithData && _currentIndex >= _nextItemIndex) _currentItemIndex = 0;
{
_current = default;
return false;
}
} }
_current = _memoryBlocks[_currentBlockIndex][_currentIndex];
return true; return true;
} }
else if (_currentBlockIndex == _lastBlockWithData && !(_currentIndex >= _nextItemIndex - 1)) // & WAS SLOWER
// else if (list._longCount == 0)
// {
// return false;
// }
// Move to the next item if we're within block boundaries, otherwise report end of collection.
else if (list._longCount > 0 && _currentItemIndex < (list._nextItemIndex > 0 ? list._nextItemIndex : list._blockSize))
{ {
_currentIndex++; // & WAS SLOWER WITHOUT
_current = _memoryBlocks[_currentBlockIndex][_currentIndex]; _current = list._memoryBlocks[_currentBlockIndex][_currentItemIndex];
// & WAS SLOWER WITHOUT
_currentItemIndex++;
return true; return true;
} }
else // & WAS SLOWER
{ // else if (_currentItemIndex != RecyclableDefaults.BlockIndexLimit)
_current = default; // {
// _currentItemIndex = RecyclableDefaults.BlockIndexLimit;
// _current = _default;
// }
return false; return false;
} }
}
else #nullable disable
readonly object IEnumerator.Current => _current;
// & WAS SLOWER
// get => _currentItemIndex > 0
// ? _list._memoryBlocks[_currentBlockIndex][_currentItemIndex - 1]
// : (object)_list._memoryBlocks[_currentBlockIndex - 1][_list._blockSize - 1];
// & WAS SLOWER
// get => _currentItemIndex != RecyclableDefaults.BlockIndexLimit
// // ? _currentItemIndex > 0
// // ? _list._memoryBlocks[_currentBlockIndex][_currentItemIndex - 1]
// // : _list._memoryBlocks[_currentBlockIndex - 1][_list._blockSize - 1]
// // & WAS SLOWER
// ? _current
// : throw new IndexOutOfRangeException();
#nullable restore
// & WAS SLOWER
// readonly object? IEnumerator.Current => _enumeratorVersion != _list._version ? throw new InvalidOperationException() : _current;
// {
// get
// {
// // if (_enumeratorVersion != _list._version)
// // {
// // throw new InvalidOperationException();
// // }
// // if (_list._longCount == 0 || _currentItemIndex == RecyclableDefaults.BlockIndexLimit)
// // {
// // throw new IndexOutOfRangeException();
// // }
// // Contract.EndContractBlock();
// return _currentItemIndex > 0
// ? _list._memoryBlocks[_currentBlockIndex][_currentItemIndex - 1]
// : _list._memoryBlocks[_currentBlockIndex - 1][_list._blockSize - 1];
// }
// }
public Enumerator(RecyclableLongList<T> list)
{ {
_current = default; // & WAS SLOWER WITHOUT
return false; _current = _default;
} _currentBlockIndex = 0;
// & WAS SLOWER
//_currentBlockIndex = -1;
_currentItemIndex = 0;
_list = list;
// & WAS SLOWER
// _enumeratorVersion = list._version;
} }
public void Reset() public void Reset()
{ {
// & WAS SLOWER WITHOUT
_current = _default;
_currentBlockIndex = 0; _currentBlockIndex = 0;
_currentIndex = RecyclableDefaults.ItemNotFoundIndex; // & WAS SLOWER
_current = default; //_currentBlockIndex = -1;
if (_isVersionChanged) _currentItemIndex = 0;
{
//_blockSize = _list!._blockSize;
//_blockSizeMinus1 = _list._blockSizeMinus1;
_isVersionChanged = false;
//_lastBlockWithData = _list!._lastBlockWithData;
//_listVersion = _list._version;
//_memoryBlocks = _list._memoryBlocks;
//_nextItemBlockIndex = _list._nextItemBlockIndex;
//_nextItemIndex = _lastBlockWithData == _nextItemBlockIndex ? _list._nextItemIndex : _blockSize;
//_version = _listVersion!.Version;
}
} }
internal RecyclableLongListEnumerator<T> Init() public void Dispose()
{ {
_currentBlockIndex = 0; if (_isReferenceType)
_currentIndex = RecyclableDefaults.ItemNotFoundIndex;
_current = default;
if (_list != null)
{ {
_blockSize = _list._blockSize; _current = _default;
_blockSizeMinus1 = _list._blockSizeMinus1;
_isVersionChanged = false;
_lastBlockWithData = _list._lastBlockWithData;
_listVersion = _list._version;
_memoryBlocks = _list._memoryBlocks;
_nextItemBlockIndex = _list._nextItemBlockIndex;
_nextItemIndex = _lastBlockWithData == _nextItemBlockIndex ? _list._nextItemIndex : _blockSize;
//_version = _listVersion!.Version;
} }
}
return this;
} }
} }
} }
+24 -115
View File
@@ -39,11 +39,7 @@ namespace Recyclable.Collections
protected set protected set
{ {
_capacity = value; _capacity = value;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
} }
@@ -54,11 +50,7 @@ namespace Recyclable.Collections
set set
{ {
_longCount = value; _longCount = value;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
} }
@@ -74,11 +66,7 @@ namespace Recyclable.Collections
set set
{ {
_longCount = value; _longCount = value;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
} }
@@ -89,8 +77,8 @@ namespace Recyclable.Collections
public int NextItemBlockIndex => _nextItemBlockIndex; public int NextItemBlockIndex => _nextItemBlockIndex;
public int NextItemIndex => _nextItemIndex; public int NextItemIndex => _nextItemIndex;
internal RecyclableCollectionVersion? _version; private ulong _version;
public RecyclableCollectionVersion? Version => _version; public ulong Version => _version;
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static void CopyFollowingItems(RecyclableLongList<T> list, long destinationItemIndex) private static void CopyFollowingItems(RecyclableLongList<T> list, long destinationItemIndex)
@@ -667,11 +655,7 @@ namespace Recyclable.Collections
set set
{ {
new Span<T>(_memoryBlocks[checked((int)(index >> _blockSizePow2BitShift))])[checked((int)(index & _blockSizeMinus1))] = value; new Span<T>(_memoryBlocks[checked((int)(index >> _blockSizePow2BitShift))])[checked((int)(index & _blockSizeMinus1))] = value;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
} }
@@ -681,11 +665,7 @@ namespace Recyclable.Collections
set set
{ {
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = value; new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = value;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
} }
@@ -716,11 +696,7 @@ namespace Recyclable.Collections
} }
_longCount++; _longCount++;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
public void AddRange(in T[] items) public void AddRange(in T[] items)
@@ -769,11 +745,7 @@ namespace Recyclable.Collections
_nextItemIndex = itemsSpan.Length; _nextItemIndex = itemsSpan.Length;
_longCount = targetCapacity; _longCount = targetCapacity;
_lastBlockWithData = targetBlockIndex; _lastBlockWithData = targetBlockIndex;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
public void AddRange(RecyclableList<T> items) public void AddRange(RecyclableList<T> items)
@@ -814,11 +786,7 @@ namespace Recyclable.Collections
_longCount = targetCapacity; _longCount = targetCapacity;
_nextItemBlockIndex = targetBlockIndex; _nextItemBlockIndex = targetBlockIndex;
_lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1); _lastBlockWithData = targetBlockIndex - (itemsSpan.Length > 0 ? 0 : 1);
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
public void AddRange(ReadOnlySpan<T> itemsSpan) public void AddRange(ReadOnlySpan<T> itemsSpan)
@@ -866,11 +834,7 @@ namespace Recyclable.Collections
_nextItemIndex = itemsSpan.Length; _nextItemIndex = itemsSpan.Length;
_longCount = targetCapacity; _longCount = targetCapacity;
_lastBlockWithData = targetBlockIndex; _lastBlockWithData = targetBlockIndex;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
public void AddRange(RecyclableLongList<T> items) public void AddRange(RecyclableLongList<T> items)
@@ -943,11 +907,7 @@ namespace Recyclable.Collections
} }
_longCount = targetCapacity; _longCount = targetCapacity;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
public void AddRange(List<T> items) public void AddRange(List<T> items)
@@ -995,11 +955,7 @@ namespace Recyclable.Collections
_nextItemBlockIndex = targetBlockIndex; _nextItemBlockIndex = targetBlockIndex;
_longCount = targetCapacity; _longCount = targetCapacity;
_lastBlockWithData = targetBlockIndex - (_nextItemIndex > 0 ? 0 : 1); _lastBlockWithData = targetBlockIndex - (_nextItemIndex > 0 ? 0 : 1);
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
public void AddRange(IList<T> items) public void AddRange(IList<T> items)
@@ -1055,10 +1011,7 @@ namespace Recyclable.Collections
_defaultBlockArrayPool.Return(itemsBuffer, NeedsClearing); _defaultBlockArrayPool.Return(itemsBuffer, NeedsClearing);
} }
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
} }
@@ -1087,20 +1040,12 @@ namespace Recyclable.Collections
else if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity)) else if (source.TryGetNonEnumeratedCount(out var requiredAdditionalCapacity))
{ {
AddRangeWithKnownCount(source, requiredAdditionalCapacity); AddRangeWithKnownCount(source, requiredAdditionalCapacity);
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
else else
{ {
AddRangeEnumerated(source, growByCount); AddRangeEnumerated(source, growByCount);
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
} }
@@ -1130,11 +1075,7 @@ namespace Recyclable.Collections
_nextItemIndex = 0; _nextItemIndex = 0;
_lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex; _lastBlockWithData = RecyclableDefaults.ItemNotFoundIndex;
_longCount = 0; _longCount = 0;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1165,17 +1106,9 @@ namespace Recyclable.Collections
public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex); public void CopyTo(T[] array, int arrayIndex) => RecyclableLongList<T>.RecyclableLongListHelpers.CopyTo(_memoryBlocks, 0, _blockSize, _longCount, array, arrayIndex);
public IEnumerator<T> GetEnumerator() public Enumerator GetEnumerator() => new(this);
{ IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
_version ??= new(); IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
return new RecyclableLongListEnumerator<T>(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
_version ??= new();
return new RecyclableLongListEnumerator<T>(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public int IndexOf(T item) public int IndexOf(T item)
@@ -1288,11 +1221,7 @@ namespace Recyclable.Collections
#pragma warning restore CS8601 #pragma warning restore CS8601
} }
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
return true; return true;
} }
@@ -1322,11 +1251,7 @@ namespace Recyclable.Collections
} }
_lastBlockWithData--; _lastBlockWithData--;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -1344,7 +1269,6 @@ namespace Recyclable.Collections
CopyFollowingItems(this, index); CopyFollowingItems(this, index);
} }
if (_nextItemIndex > 0) if (_nextItemIndex > 0)
{ {
_nextItemIndex--; _nextItemIndex--;
@@ -1367,10 +1291,7 @@ namespace Recyclable.Collections
#pragma warning restore CS8601 #pragma warning restore CS8601
} }
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -1410,21 +1331,16 @@ namespace Recyclable.Collections
#pragma warning restore CS8601 #pragma warning restore CS8601
} }
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
~RecyclableLongList() ~RecyclableLongList()
{ {
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(); Dispose();
} }
public void Dispose() public void Dispose()
{ {
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
if (_capacity > 0) if (_capacity > 0)
{ {
Clear(); Clear();
@@ -1432,16 +1348,9 @@ namespace Recyclable.Collections
{ {
_memoryBlocksPool.Return(_memoryBlocks, NeedsClearing); _memoryBlocksPool.Return(_memoryBlocks, NeedsClearing);
} }
if (_version != null)
{
//var toReturn = _version;
_version = null;
//RecyclableCollectionVersionPool.Shared.Return(toReturn);
} }
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
} }
} }
}