From 8deb43681911c67b5e7f56635a1d2eb195dfef6b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 30 May 2025 10:24:05 -0400 Subject: [PATCH 1/2] Try to improve List enumerator perf --- .../src/System/Collections/Generic/List.cs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs index 1df1ea89696c5f..39923e50a3fc8c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs @@ -1201,27 +1201,26 @@ public void Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { List localList = _list; - if (_version == localList._version && ((uint)_index < (uint)localList._size)) + if (_version != localList._version) { - _current = localList._items[_index]; - _index++; - return true; + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - return MoveNextRare(); - } - private bool MoveNextRare() - { - if (_version != _list._version) + T[] items = localList._items; + int index = _index; + if ((uint)index < (uint)items.Length && + (uint)index < (uint)localList._size) { - ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + _current = items[index]; + _index++; + return true; } - _index = _list._size + 1; _current = default; return false; } From d3443a9d1215222dc8e5cd835c5e5e86e489e530 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 31 May 2025 07:56:54 -0400 Subject: [PATCH 2/2] Try having a dedicated class for lists as enumerables --- .../src/System/Collections/Generic/List.cs | 80 +++++++++++++++++-- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs index 39923e50a3fc8c..8eb0e5e9b7a2d2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs @@ -666,7 +666,7 @@ public void ForEach(Action action) IEnumerator IEnumerable.GetEnumerator() => Count == 0 ? SZGenericArrayEnumerator.Empty : - GetEnumerator(); + new EnumeratorObject(this); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this).GetEnumerator(); @@ -1201,6 +1201,76 @@ public void Dispose() { } + public bool MoveNext() + { + List localList = _list; + + if (_version == localList._version && ((uint)_index < (uint)localList._size)) + { + _current = localList._items[_index]; + _index++; + return true; + } + return MoveNextRare(); + } + + private bool MoveNextRare() + { + if (_version != _list._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + _index = _list._size + 1; + _current = default; + return false; + } + + public T Current => _current!; + + object? IEnumerator.Current + { + get + { + if (_index == 0 || _index == _list._size + 1) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + return Current; + } + } + + void IEnumerator.Reset() + { + if (_version != _list._version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + _index = 0; + _current = default; + } + } + + private sealed class EnumeratorObject : IEnumerator, IEnumerator + { + private readonly List _list; + private int _index; + private readonly int _version; + private T? _current; + + internal EnumeratorObject(List list) + { + _list = list; + _index = 0; + _version = list._version; + _current = default; + } + + void IDisposable.Dispose() + { + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { @@ -1211,17 +1281,15 @@ public bool MoveNext() ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); } - T[] items = localList._items; - int index = _index; - if ((uint)index < (uint)items.Length && - (uint)index < (uint)localList._size) + if ((uint)_index < (uint)localList._size) { - _current = items[index]; + _current = localList._items[_index]; _index++; return true; } _current = default; + _index = localList._size + 1; return false; }