RecyclableList.Enumerator optimized. Tests fixed.

This commit is contained in:
mlemanczyk committed 2023-06-21 02:15:09 +02:00
1 parent 85aac1dad6
commit 20fe0cc491
3 files changed
+193 -202

No files matched your search

@@ -1,86 +1,184 @@
using System.Collections; using System.Collections;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices;
namespace Recyclable.Collections namespace Recyclable.Collections
{ {
public partial class RecyclableList<T> public partial class RecyclableList<T>
{ {
[StructLayout(LayoutKind.Auto)]
public struct Enumerator : IEnumerator<T>, IEnumerator public struct Enumerator : IEnumerator<T>
{ {
#nullable disable
private readonly RecyclableList<T> _list;
private int _currentIndex; private int _currentIndex;
private T _current; private readonly ulong _enumeratorVersion;
private readonly RecyclableList<T> _list;
//[Pure] // & WAS SLOWER
//private readonly int _listCount; // private bool _isInValidState = false;
//private readonly int _listCountMinus1;
//private readonly T[] _memoryBlock;
// private bool _endOfCollectionReached; // private bool _endOfCollectionReached;
// private T _current;
//private RecyclableCollectionVersion _listVersion; #nullable disable
//private ulong _enumeratorVersion;
//private bool _isVersionChanged;
//private void VersionChanged() => _isVersionChanged = true;
//internal RecyclableListEnumeratorPool? _pool;
//internal bool _returned;
//public RecyclableListEnumerator()
//{
//}
// [Pure] // [Pure]
internal Enumerator(RecyclableList<T> list) public readonly T Current
{ {
_current = default; get
_currentIndex = 0; {
_list = list; if (_enumeratorVersion != _list._version)
//_memoryBlock = list._memoryBlock; {
//_listCount = list._count; throw new InvalidOperationException();
//_listVersion = list._version!; // & WAS SLOWER
//_listVersion.VersionChanged += VersionChanged; // ThrowHelper.ThrowListModifiedException();
//_enumeratorVersion = _listVersion.Version;
} }
//{ if (_currentIndex == 0 || _currentIndex > _list._count)
// //[MethodImpl(MethodImplOptions.AggressiveInlining)] {
// get => throw new IndexOutOfRangeException();
// //if (_currentIndex < 0) // & WAS SLOWER
// //{ // ThrowHelper.ThrowCollectionNotInitialized();// new IndexOutOfRangeException();// _current;
// // ThrowHelper.ThrowInvalidOperationException("The enumerator wasn't initialized. You need to call MoveNext() before getting the current iterator value"); }
// //}
// //else Contract.EndContractBlock();
// //if (_endOfCollectionReached)
// //{ return _list._memoryBlock[_currentIndex - 1];// _current;
// // throw new InvalidOperationException("The enumerator reached the end of the collection. You need to call Reset(), if you want to enumerate the collection again"); }
// //} }
// //else
// //{
// //if (_currentIndex < 0)
// //{
// // ThrowHelper.ThrowInvalidOperationException("The enumerator wasn't initialized. You need to call MoveNext() before getting the current iterator value");
// //}
// //else
// _memoryBlock[_currentIndex];//}
//}
// [Pure] // [Pure]
public readonly void Dispose() object IEnumerator.Current
{ {
// if (_listVersion != null) get
{
if (_enumeratorVersion != _list._version)
{
throw new InvalidOperationException();
// & WAS SLOWER
// ThrowHelper.ThrowListModifiedException();
}
if (_currentIndex == 0 || _currentIndex > _list._count)
{
throw new IndexOutOfRangeException();
// & WAS SLOWER
// ThrowHelper.ThrowCollectionNotInitialized();
}
Contract.EndContractBlock();
return _list._memoryBlock[_currentIndex - 1];
// & WAS SLOWER
// return _current;
}
}
// & WAS SLOWER
// private readonly int _listCount;
// private RecyclableCollectionVersion _listVersion;
// private readonly T[] _memoryBlock;
// private bool _isVersionChanged;
// private void VersionChanged() => _isVersionChanged = true;
// internal RecyclableListEnumeratorPool? _pool;
// internal bool _returned;
// private readonly int _listCountMinus1;
// & WAS SLOWER
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// [MethodImpl(MethodImplOptions.NoInlining)]
public bool MoveNext()
{
// & WAS SLOWER
// var list = _list;
if (_enumeratorVersion != _list._version)
{
throw new InvalidOperationException();
// & WAS SLOWER
// ThrowHelper.ThrowListModifiedException();
}
Contract.EndContractBlock();
// & WAS SLOWER
// if (_currentIndex == 0)
// { // {
// _listVersion.VersionChanged -= VersionChanged; // _isInValidState = true;
// //_listVersion.RemoveEnumerator(this); // _currentIndex++;
//#nullable disable // return true;
// _listVersion = null; // }
//#nullable restore // else
if (_currentIndex < _list._count)
{
// & WAS SLOWER
// _current = _list._memoryBlock[_currentIndex];
_currentIndex++;
return true;
}
// & WAS SLOWER
// else if (_currentIndex == _list._count)
// {
// & WAS SLOWER
// This isn't needed in most use-cases, because the enumerator will get disposed, releasing the reference, if any.
// _current = _defaultValue;
// _currentIndex++;
// return false;
// }
// else if (!_endOfCollectionReached)
// {
// // & WAS SLOWER
// // This isn't needed in most use-cases, because the enumerator will get disposed, releasing the reference, if any.
// // _current = _defaultValue;
// // _currentIndex = int.MinValue;
// _endOfCollectionReached = true;
// return false;
// } // }
// & WAS SLOWER
// else if (!_endOfCollectionReached)
// {
// _current = default;
// _endOfCollectionReached = true;
// return false;
// }
// & WAS SLOWER
// _current = default;
// _currentIndex = _list._count + 1;
return false;
}
public Enumerator(RecyclableList<T> list)
{
_currentIndex = 0;
_list = list;
_enumeratorVersion = list._version;
// & WAS SLOWER
// _current = default;
// _endOfCollectionReached = false;
// _listCount = list._count;
// _memoryBlock = list._memoryBlock;
// _isVersionChanged = false;
// _listVersion = list.Version;
// _listVersion.AddEnumerator();
// _enumeratorVersion = list._version.Version;
// list._version.RegisterHandler(VersionChanged);
// _enumeratorVersion = _listVersion.Version;
// _endOfCollectionReached = false;
// _listCountMinus1 = list._count - 1;
}
public readonly void Dispose()
{
// & WAS SLOWER
//if (_listVersion != null)
//{
//_list._version.RemoveEnumerator();
//_listVersion.RemoveEnumerator(this);
//}
// & WAS SLOWER
// if (_pool != null) // if (_pool != null)
//{ //{
// if (!_returned) // if (!_returned)
@@ -96,70 +194,15 @@ namespace Recyclable.Collections
//} //}
} }
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var list = _list;
//if (_isVersionChanged)
//{
// ThrowHelper.ThrowListModifiedException();
// return false;
//}
//else
if (_currentIndex < list._count)
{
_current = list._memoryBlock[_currentIndex];
_currentIndex++;
return true;
}
//else if (!_endOfCollectionReached)
//{
// _endOfCollectionReached = true;
// return false;
//}
_current = default;
return false;
}
#nullable restore
public T Current => _current;
object IEnumerator.Current => _current;
#nullable disable
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
void IEnumerator.Reset() void IEnumerator.Reset()
{ {
_currentIndex = 0; _currentIndex = 0;
_current = default;
}
// & WAS SLOWER
// _endOfCollectionReached = false; // _endOfCollectionReached = false;
//if (_isVersionChanged) // _isInValidState = false;
//{ // _current = default;
// }
//_enumeratorVersion = _listVersion!.Version;
// _isVersionChanged = false;
//}
//internal RecyclableListEnumerator Reset(RecyclableListEnumeratorPool pool, RecyclableList<T> list)
//{
// _returned = false;
// if (_pool != pool)
// {
// _pool = pool;
// }
// _currentIndex = RecyclableDefaults.ItemNotFoundIndex;
// _listVersion = list._version!;
// _enumeratorVersion = _listVersion!.Version;
// list._version!.AddEnumerator();
// return this;
//}
//[Pure]
//public object Clone() => MemberwiseClone();
} }
} }
#nullable restore #nullable restore
+26 -82
View File
@@ -15,9 +15,12 @@ namespace Recyclable.Collections
public static explicit operator ReadOnlySpan<T>(RecyclableList<T> source) => new(source._memoryBlock, 0, source.Count); public static explicit operator ReadOnlySpan<T>(RecyclableList<T> source) => new(source._memoryBlock, 0, source.Count);
protected T[] _memoryBlock; protected T[] _memoryBlock;
internal RecyclableCollectionVersion? _version; // & WAS SLOWER
// internal RecyclableCollectionVersion _version;
// public RecyclableCollectionVersion Version => _version;
public RecyclableCollectionVersion? Version => _version; protected ulong _version;
public ulong Version => _version;
protected IEnumerator<T> AddRangeEnumerated(IEnumerable<T> source, int growByCount) protected IEnumerator<T> AddRangeEnumerated(IEnumerable<T> source, int growByCount)
{ {
@@ -196,11 +199,7 @@ namespace Recyclable.Collections
set set
{ {
_memoryBlock[index] = value; _memoryBlock[index] = value;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
} }
@@ -214,11 +213,7 @@ namespace Recyclable.Collections
{ {
_memoryBlock = Resize(_memoryBlock, value); _memoryBlock = Resize(_memoryBlock, value);
_capacity = _memoryBlock.Length; _capacity = _memoryBlock.Length;
} _version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
} }
} }
} }
@@ -254,11 +249,7 @@ namespace Recyclable.Collections
} }
_memoryBlock[_count++] = item; _memoryBlock[_count++] = item;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -281,10 +272,7 @@ namespace Recyclable.Collections
itemsSpan.CopyTo(targetSpan); itemsSpan.CopyTo(targetSpan);
_count = targetCapacity; _count = targetCapacity;
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -306,10 +294,7 @@ namespace Recyclable.Collections
itemsSpan.CopyTo(targetSpan); itemsSpan.CopyTo(targetSpan);
_count = targetCapacity; _count = targetCapacity;
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -330,10 +315,7 @@ namespace Recyclable.Collections
items.CopyTo(_memoryBlock, _count); items.CopyTo(_memoryBlock, _count);
_count = targetCapacity; _count = targetCapacity;
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -354,10 +336,7 @@ namespace Recyclable.Collections
items.CopyTo(_memoryBlock, _count); items.CopyTo(_memoryBlock, _count);
_count = targetCapacity; _count = targetCapacity;
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -380,10 +359,7 @@ namespace Recyclable.Collections
itemsSpan.CopyTo(targetSpan); itemsSpan.CopyTo(targetSpan);
_count = targetCapacity; _count = targetCapacity;
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -438,10 +414,7 @@ namespace Recyclable.Collections
_count = targetCapacity; _count = targetCapacity;
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
@@ -467,19 +440,13 @@ namespace Recyclable.Collections
{ {
AddRangeWithKnownCount(source, _count, requiredAdditionalCapacity); AddRangeWithKnownCount(source, _count, requiredAdditionalCapacity);
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
else else
{ {
AddRangeEnumerated(source, growByCount); AddRangeEnumerated(source, growByCount);
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
} }
@@ -494,11 +461,7 @@ namespace Recyclable.Collections
} }
_count = 0; _count = 0;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -529,11 +492,7 @@ namespace Recyclable.Collections
_memoryBlock[index] = item; _memoryBlock[index] = item;
_count++; _count++;
_version++;
if (_version?.IsVersioned ?? false)
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -555,10 +514,7 @@ namespace Recyclable.Collections
#pragma warning restore CS8601 #pragma warning restore CS8601
} }
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
return true; return true;
} }
@@ -587,22 +543,17 @@ namespace Recyclable.Collections
#pragma warning restore CS8601 #pragma warning restore CS8601
} }
if (_version?.IsVersioned ?? false) _version++;
{
_version.Inc();
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() => new Enumerator(this); public Enumerator GetEnumerator() => new Enumerator(this);
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
IEnumerator IEnumerable.GetEnumerator() =>
//_version ??= new(); // RecyclableCollectionVersionPool.Shared.Get();
new Enumerator(this);//return RecyclableListEnumeratorPool.Shared.Get(this);
//[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
IEnumerator<T> IEnumerable<T>.GetEnumerator() => IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
//_version ??= new(); // RecyclableCollectionVersionPool.Shared.Get();
new Enumerator(this);//return RecyclableListEnumeratorPool.Shared.Get(this); [MethodImpl(MethodImplOptions.AggressiveInlining)]
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
public void Dispose() public void Dispose()
{ {
@@ -615,13 +566,6 @@ namespace Recyclable.Collections
_arrayPool.Return(_memoryBlock, NeedsClearing); _arrayPool.Return(_memoryBlock, NeedsClearing);
} }
if (_version != null)
{
//var toReturn = _version;
_version = null;
//RecyclableCollectionVersionPool.Shared.Return(toReturn);
}
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
} }
@@ -349,7 +349,11 @@ namespace Recyclable.CollectionsTests
// Act // Act
var yieldedItems = new List<long>((int)itemsCount); var yieldedItems = new List<long>((int)itemsCount);
yieldedItems.AddRange(list); using var enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
yieldedItems.Add(enumerator.Current);
}
// Validate // Validate
_ = yieldedItems.Count.Should().Be((int)itemsCount); _ = yieldedItems.Count.Should().Be((int)itemsCount);