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
+191 -233

No files matched your search

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