From: Michal Strehovský Date: Fri, 7 Jul 2023 07:38:43 +0000 (+0900) Subject: Bring back old array enumerator code (#88371) X-Git-Tag: accepted/tizen/unified/riscv/20231226.055536~1189 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4285e438dfe1fe808ebc896650fbe8123b20c117;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Bring back old array enumerator code (#88371) This is a 0.3% size saving for Stage1. We not only allow array enumerators to be preinitialized again, but also avoid introducing many array `MethodTables` (looks like the new enumerators force array MethodTables for cases where we could have avoided them). Fixes #82993. --- diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 8a32e8f..699f99c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -400,7 +400,8 @@ namespace System // ! Warning: "this" is an array, not an SZArrayHelper. See comments above // ! or you may introduce a security hole! T[] @this = Unsafe.As(this); - return @this.Length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this); + int length = @this.Length; + return length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this, length); } private void CopyTo(T[] array, int index) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs index f2d1aac..1cb5604 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs @@ -1099,7 +1099,10 @@ namespace System public new IEnumerator GetEnumerator() { T[] @this = Unsafe.As(this); - return @this.Length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this); + // get length so we don't have to call the Length property again in ArrayEnumerator constructor + // and avoid more checking there too. + int length = @this.Length; + return length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(@this, length); } public int Count diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs b/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs index 90542e9..60aa839 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs @@ -69,72 +69,60 @@ namespace System internal abstract class SZGenericArrayEnumeratorBase : IDisposable { - protected readonly Array _array; protected int _index; + protected readonly int _endIndex; - protected SZGenericArrayEnumeratorBase(Array array) + protected SZGenericArrayEnumeratorBase(int endIndex) { - Debug.Assert(array != null); - - _array = array; _index = -1; + _endIndex = endIndex; } public bool MoveNext() { int index = _index + 1; - uint length = (uint)_array.NativeLength; - if ((uint)index >= length) + if ((uint)index < (uint)_endIndex) { - _index = (int)length; - return false; + _index = index; + return true; } - _index = index; - return true; + _index = _endIndex; + return false; } public void Reset() => _index = -1; -#pragma warning disable CA1822 // https://github.com/dotnet/roslyn-analyzers/issues/5911 public void Dispose() { } -#pragma warning restore CA1822 } internal sealed class SZGenericArrayEnumerator : SZGenericArrayEnumeratorBase, IEnumerator { + private readonly T[]? _array; + /// Provides an empty enumerator singleton. /// /// If the consumer is using SZGenericArrayEnumerator elsewhere or is otherwise likely /// to be using T[] elsewhere, this singleton should be used. Otherwise, GenericEmptyEnumerator's /// singleton should be used instead, as it doesn't reference T[] in order to reduce footprint. /// -#pragma warning disable CA1825 - internal static readonly SZGenericArrayEnumerator Empty = - // Array.Empty is intentionally omitted here, since we don't want to pay for generic instantiations - // that wouldn't have otherwise been used. - new SZGenericArrayEnumerator(new T[0]); -#pragma warning restore CA1825 - - public SZGenericArrayEnumerator(T[] array) - : base(array) + internal static readonly SZGenericArrayEnumerator Empty = new SZGenericArrayEnumerator(null, 0); + + internal SZGenericArrayEnumerator(T[]? array, int endIndex) + : base(endIndex) { + Debug.Assert(array == null || endIndex == array.Length); + _array = array; } public T Current { get { - int index = _index; - T[] array = Unsafe.As(_array); - - if ((uint)index >= (uint)array.Length) - { - ThrowHelper.ThrowInvalidOperationException_EnumCurrent(index); - } - - return array[index]; + if ((uint)_index >= (uint)_endIndex) + ThrowHelper.ThrowInvalidOperationException_EnumCurrent(_index); + return _array![_index]; } } diff --git a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs index af3e7f0..f6782a4 100644 --- a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs @@ -468,7 +468,8 @@ namespace System internal IEnumerator InternalArray__IEnumerable_GetEnumerator() { - return Length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(Unsafe.As(this)); + int length = Length; + return length == 0 ? SZGenericArrayEnumerator.Empty : new SZGenericArrayEnumerator(Unsafe.As(this), length); } internal void InternalArray__ICollection_Clear()