diff --git a/src/libraries/System.Linq/src/System/Linq/Range.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Range.SpeedOpt.cs index 6704eef5dedc29..2907e277229a44 100644 --- a/src/libraries/System.Linq/src/System/Linq/Range.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Range.SpeedOpt.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Linq { @@ -17,13 +20,7 @@ public override IEnumerable Select(Func selector public int[] ToArray() { int[] array = new int[_end - _start]; - int cur = _start; - for (int i = 0; i < array.Length; ++i) - { - array[i] = cur; - ++cur; - } - + InitializeSpan(array); return array; } @@ -84,6 +81,61 @@ public int TryGetLast(out bool found) found = true; return _end - 1; } + + // Destination *must* be non-empty and match the range length + private void InitializeSpan(Span destination) + { + if (destination.Length < Vector.Count * 2) + { + int cur = _start; + for (int i = 0; i < destination.Length; i++) + { + destination[i] = cur; + cur++; + } + } + else + { + InitializeSpanCore(destination); + } + } + + private void InitializeSpanCore(Span destination) + { + int width = Vector.Count; + int stride = Vector.Count * 2; + int remainder = destination.Length % stride; + + // Up to 16 elements which corresponds to AVX512 + Vector initMask = Unsafe.ReadUnaligned>( + ref Unsafe.As(ref MemoryMarshal.GetReference( + (ReadOnlySpan)new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }))); + + Vector mask = new Vector(stride); + Vector value = new Vector(_start) + initMask; + Vector value2 = value + new Vector(width); + + ref int pos = ref MemoryMarshal.GetReference(destination); + ref int limit = ref Unsafe.Add(ref pos, destination.Length - remainder); + while (!Unsafe.AreSame(ref pos, ref limit)) + { + Unsafe.WriteUnaligned(ref Unsafe.As(ref pos), value); + Unsafe.WriteUnaligned(ref Unsafe.As(ref Unsafe.Add(ref pos, width)), value2); + + value += mask; + value2 += mask; + pos = ref Unsafe.Add(ref pos, stride); + } + + int cur = _start + (destination.Length - remainder); + int end = _end; + while (cur < end) + { + pos = cur; + pos = ref Unsafe.Add(ref pos, 1); + cur++; + } + } } } }