diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs index 73de2d970c626d..e9e4f985d03065 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs @@ -244,13 +244,19 @@ public static SearchValues Create(ReadOnlySpan values, StringCom private static bool TryGetSingleRange(ReadOnlySpan values, out T minInclusive, out T maxInclusive) where T : struct, INumber, IMinMaxValue { - T min = T.MaxValue; - T max = T.MinValue; + T min; + T max; - foreach (T value in values) + if (typeof(T) == typeof(char)) + { + // char is not a valid Vector128 element type; treat as ushort instead. + GetMinMax(MemoryMarshal.Cast(values), out ushort minUshort, out ushort maxUshort); + min = Unsafe.BitCast(minUshort); + max = Unsafe.BitCast(maxUshort); + } + else { - min = T.Min(min, value); - max = T.Max(max, value); + GetMinMax(values, out min, out max); } minInclusive = min; @@ -280,6 +286,58 @@ private static bool TryGetSingleRange(ReadOnlySpan values, out T minInclus return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void GetMinMax(ReadOnlySpan values, out T min, out T max) + where T : struct, INumber, IMinMaxValue + { + Debug.Assert(values.Length >= 1); + + if (Vector128.IsHardwareAccelerated && Vector128.IsSupported && values.Length >= Vector128.Count) + { + ref T current = ref MemoryMarshal.GetReference(values); + ref T lastVectorStart = ref Unsafe.Add(ref current, values.Length - Vector128.Count); + + Vector128 vMin = Vector128.Create(T.MaxValue); + Vector128 vMax = Vector128.Create(T.MinValue); + + do + { + Vector128 v = Vector128.LoadUnsafe(ref current); + vMin = Vector128.Min(vMin, v); + vMax = Vector128.Max(vMax, v); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart)); + + // Process the last (possibly overlapping) vector. + Vector128 last = Vector128.LoadUnsafe(ref lastVectorStart); + vMin = Vector128.Min(vMin, last); + vMax = Vector128.Max(vMax, last); + + // Horizontal reduction. + min = vMin[0]; + max = vMax[0]; + for (int i = 1; i < Vector128.Count; i++) + { + min = T.Min(vMin[i], min); + max = T.Max(vMax[i], max); + } + return; + } + + // Scalar fallback. + T fallbackMin = T.MaxValue; + T fallbackMax = T.MinValue; + foreach (T value in values) + { + fallbackMin = T.Min(fallbackMin, value); + fallbackMax = T.Max(fallbackMax, value); + } + + min = fallbackMin; + max = fallbackMax; + } + internal interface IRuntimeConst { static abstract bool Value { get; }