From 508c67ca4e79a2aba30a0cd215cce43c04d257c2 Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Wed, 11 Oct 2023 23:51:38 +0200 Subject: [PATCH 1/9] Vector512 Support for Enumerable.Min/Max --- .../System.Linq/src/System/Linq/MaxMin.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs index 5def04f6cedba..50b48d007ed7a 100644 --- a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs +++ b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs @@ -16,6 +16,9 @@ private interface IMinMaxCalc where T : struct, IBinaryInteger public static abstract bool Compare(T left, T right); public static abstract Vector128 Compare(Vector128 left, Vector128 right); public static abstract Vector256 Compare(Vector256 left, Vector256 right); +#if NET8_0_OR_GREATER + public static abstract Vector512 Compare(Vector512 left, Vector512 right); +#endif } private static T MinMaxInteger(this IEnumerable source) @@ -66,7 +69,11 @@ private static T MinMaxInteger(this IEnumerable source) } } } +#if NET8_0_OR_GREATER + else if(!Vector512.IsHardwareAccelerated || span.Length < Vector512.Count) +#else else +#endif { ref T current = ref MemoryMarshal.GetReference(span); ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector256.Count); @@ -90,6 +97,32 @@ private static T MinMaxInteger(this IEnumerable source) } } } +#if NET8_0_OR_GREATER + else + { + ref T current = ref MemoryMarshal.GetReference(span); + ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector512.Count); + + Vector512 best = Vector512.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector512.Count); + + while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart)) + { + best = TMinMax.Compare(best, Vector512.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector512.Count); + } + best = TMinMax.Compare(best, Vector512.LoadUnsafe(ref lastVectorStart)); + + value = best[0]; + for (int i = 1; i < Vector512.Count; i++) + { + if (TMinMax.Compare(best[i], value)) + { + value = best[i]; + } + } + } +#endif } else { From 6f330dd7389a0e48bd2dd763455a0119c248cace Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Thu, 12 Oct 2023 00:06:49 +0200 Subject: [PATCH 2/9] implement comparer in Min/Max --- src/libraries/System.Linq/src/System/Linq/Max.cs | 3 +++ src/libraries/System.Linq/src/System/Linq/Min.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/libraries/System.Linq/src/System/Linq/Max.cs b/src/libraries/System.Linq/src/System/Linq/Max.cs index 543d300389904..5f5e15d4d741d 100644 --- a/src/libraries/System.Linq/src/System/Linq/Max.cs +++ b/src/libraries/System.Linq/src/System/Linq/Max.cs @@ -18,6 +18,9 @@ public static partial class Enumerable public static bool Compare(T left, T right) => left > right; public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Max(left, right); public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Max(left, right); +#if NET8_0_OR_GREATER + public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Max(left, right); +#endif } public static int? Max(this IEnumerable source) => MaxInteger(source); diff --git a/src/libraries/System.Linq/src/System/Linq/Min.cs b/src/libraries/System.Linq/src/System/Linq/Min.cs index a553f5ddcb00e..be93b54b76f0b 100644 --- a/src/libraries/System.Linq/src/System/Linq/Min.cs +++ b/src/libraries/System.Linq/src/System/Linq/Min.cs @@ -18,6 +18,9 @@ public static partial class Enumerable public static bool Compare(T left, T right) => left < right; public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Min(left, right); public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Min(left, right); +#if NET8_0_OR_GREATER + public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Min(left, right); +#endif } public static int? Min(this IEnumerable source) => MinInteger(source); From 8c5609060176693b8149ba0d278c71adf2048563 Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Thu, 12 Oct 2023 00:12:20 +0200 Subject: [PATCH 3/9] remove trailing whitespace --- src/libraries/System.Linq/src/System/Linq/MaxMin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs index 50b48d007ed7a..38b0cae58f364 100644 --- a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs +++ b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs @@ -122,7 +122,7 @@ private static T MinMaxInteger(this IEnumerable source) } } } -#endif +#endif } else { From 39b164b394a7a3287eef0e66343bc912e7b05437 Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Thu, 12 Oct 2023 01:48:01 +0200 Subject: [PATCH 4/9] remove conditions to only target NET 8 as Linq Packages is versioned anyway --- src/libraries/System.Linq/src/System/Linq/Max.cs | 2 -- src/libraries/System.Linq/src/System/Linq/MaxMin.cs | 8 -------- src/libraries/System.Linq/src/System/Linq/Min.cs | 2 -- 3 files changed, 12 deletions(-) diff --git a/src/libraries/System.Linq/src/System/Linq/Max.cs b/src/libraries/System.Linq/src/System/Linq/Max.cs index 5f5e15d4d741d..bd08684270f6c 100644 --- a/src/libraries/System.Linq/src/System/Linq/Max.cs +++ b/src/libraries/System.Linq/src/System/Linq/Max.cs @@ -18,9 +18,7 @@ public static partial class Enumerable public static bool Compare(T left, T right) => left > right; public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Max(left, right); public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Max(left, right); -#if NET8_0_OR_GREATER public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Max(left, right); -#endif } public static int? Max(this IEnumerable source) => MaxInteger(source); diff --git a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs index 38b0cae58f364..0120e8edb7c87 100644 --- a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs +++ b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs @@ -16,9 +16,7 @@ private interface IMinMaxCalc where T : struct, IBinaryInteger public static abstract bool Compare(T left, T right); public static abstract Vector128 Compare(Vector128 left, Vector128 right); public static abstract Vector256 Compare(Vector256 left, Vector256 right); -#if NET8_0_OR_GREATER public static abstract Vector512 Compare(Vector512 left, Vector512 right); -#endif } private static T MinMaxInteger(this IEnumerable source) @@ -69,11 +67,7 @@ private static T MinMaxInteger(this IEnumerable source) } } } -#if NET8_0_OR_GREATER else if(!Vector512.IsHardwareAccelerated || span.Length < Vector512.Count) -#else - else -#endif { ref T current = ref MemoryMarshal.GetReference(span); ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector256.Count); @@ -97,7 +91,6 @@ private static T MinMaxInteger(this IEnumerable source) } } } -#if NET8_0_OR_GREATER else { ref T current = ref MemoryMarshal.GetReference(span); @@ -122,7 +115,6 @@ private static T MinMaxInteger(this IEnumerable source) } } } -#endif } else { diff --git a/src/libraries/System.Linq/src/System/Linq/Min.cs b/src/libraries/System.Linq/src/System/Linq/Min.cs index be93b54b76f0b..6c81274f3013c 100644 --- a/src/libraries/System.Linq/src/System/Linq/Min.cs +++ b/src/libraries/System.Linq/src/System/Linq/Min.cs @@ -18,9 +18,7 @@ public static partial class Enumerable public static bool Compare(T left, T right) => left < right; public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Min(left, right); public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Min(left, right); -#if NET8_0_OR_GREATER public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Min(left, right); -#endif } public static int? Min(this IEnumerable source) => MinInteger(source); From 58be825a3ce99641082dbcb69c120c0bba791fa4 Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Thu, 12 Oct 2023 01:50:42 +0200 Subject: [PATCH 5/9] minor codestyle fix --- src/libraries/System.Linq/src/System/Linq/MaxMin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs index 0120e8edb7c87..47974c48a1c6f 100644 --- a/src/libraries/System.Linq/src/System/Linq/MaxMin.cs +++ b/src/libraries/System.Linq/src/System/Linq/MaxMin.cs @@ -67,7 +67,7 @@ private static T MinMaxInteger(this IEnumerable source) } } } - else if(!Vector512.IsHardwareAccelerated || span.Length < Vector512.Count) + else if (!Vector512.IsHardwareAccelerated || span.Length < Vector512.Count) { ref T current = ref MemoryMarshal.GetReference(span); ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector256.Count); From 2caa62e1e4142731239432a0465daa3661360787 Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Sat, 14 Oct 2023 13:26:58 +0200 Subject: [PATCH 6/9] increate Max_AllTypes_TestData to be at least one elemend wider than Vector512 --- src/libraries/System.Linq/tests/MaxTests.cs | 2 +- src/libraries/System.Linq/tests/MinTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Linq/tests/MaxTests.cs b/src/libraries/System.Linq/tests/MaxTests.cs index 42727d8362085..d33fe41a7ef78 100644 --- a/src/libraries/System.Linq/tests/MaxTests.cs +++ b/src/libraries/System.Linq/tests/MaxTests.cs @@ -11,7 +11,7 @@ public class MaxTests : EnumerableTests { public static IEnumerable Max_AllTypes_TestData() { - for (int length = 2; length < 33; length++) + for (int length = 2; length < 65; length++) { yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i)), (byte)(length + length - 1) }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i).ToArray()), (byte)(length + length - 1) }; diff --git a/src/libraries/System.Linq/tests/MinTests.cs b/src/libraries/System.Linq/tests/MinTests.cs index e877dd5911a40..021b259108323 100644 --- a/src/libraries/System.Linq/tests/MinTests.cs +++ b/src/libraries/System.Linq/tests/MinTests.cs @@ -11,7 +11,7 @@ public class MinTests : EnumerableTests { public static IEnumerable Min_AllTypes_TestData() { - for (int length = 2; length < 33; length++) + for (int length = 2; length < 65; length++) { yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i)), (byte)length }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i).ToArray()), (byte)length }; From 23026eedfbcdf6dabe4c429ea7bad33b1b2f44df Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Sat, 14 Oct 2023 14:21:33 +0200 Subject: [PATCH 7/9] avoid generating testdata for sbyte which are outside the representable range --- src/libraries/System.Linq/tests/MinTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Linq/tests/MinTests.cs b/src/libraries/System.Linq/tests/MinTests.cs index 021b259108323..a441c93d748cb 100644 --- a/src/libraries/System.Linq/tests/MinTests.cs +++ b/src/libraries/System.Linq/tests/MinTests.cs @@ -16,8 +16,9 @@ public static IEnumerable Min_AllTypes_TestData() yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i)), (byte)length }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i).ToArray()), (byte)length }; - yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i)), (sbyte)length }; - yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i).ToArray()), (sbyte)length }; + // dont generate testdata outside of the datatype bounds + yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Where(i => i <= sbyte.MaxValue).Select(i => (sbyte)i)), (sbyte)length }; + yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Where(i => i <= sbyte.MaxValue).Select(i => (sbyte)i).ToArray()), (sbyte)length }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (ushort)i)), (ushort)length }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (ushort)i).ToArray()), (ushort)length }; From ea81905dd7966da83fe955af98b683ba5fbed8f9 Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Sat, 14 Oct 2023 14:42:48 +0200 Subject: [PATCH 8/9] dont overflow the datatype inside the unittest --- src/libraries/System.Linq/tests/MaxTests.cs | 3 ++- src/libraries/System.Linq/tests/MinTests.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Linq/tests/MaxTests.cs b/src/libraries/System.Linq/tests/MaxTests.cs index d33fe41a7ef78..5d5d786a9e9d4 100644 --- a/src/libraries/System.Linq/tests/MaxTests.cs +++ b/src/libraries/System.Linq/tests/MaxTests.cs @@ -67,7 +67,8 @@ public void Max_AllTypes(IEnumerable source, T expected) where T : INumber T first = source.First(); Assert.Equal(first, source.Max(Comparer.Create((x, y) => x == first ? 1 : -1))); - Assert.Equal(expected + T.One, source.Max(x => x + T.One)); + if(expected != T.MaxValue) + Assert.Equal(expected + T.One, source.Max(x => x + T.One)); } [Fact] diff --git a/src/libraries/System.Linq/tests/MinTests.cs b/src/libraries/System.Linq/tests/MinTests.cs index a441c93d748cb..efd05cb9f6fdf 100644 --- a/src/libraries/System.Linq/tests/MinTests.cs +++ b/src/libraries/System.Linq/tests/MinTests.cs @@ -68,7 +68,8 @@ public void Min_AllTypes(IEnumerable source, T expected) where T : INumber T first = source.First(); Assert.Equal(first, source.Min(Comparer.Create((x, y) => x == first ? -1 : 1))); - Assert.Equal(expected + T.One, source.Min(x => x + T.One)); + if(expected != T.MaxValue) + Assert.Equal(expected + T.One, source.Min(x => x + T.One)); } [Fact] From 0da421635aefa8dbddcafe91c7c75f55fc48853b Mon Sep 17 00:00:00 2001 From: Timo Witte Date: Sat, 14 Oct 2023 17:07:52 +0200 Subject: [PATCH 9/9] dont generate testdata which leads to overflows in the number type during test --- src/libraries/System.Linq/tests/MaxTests.cs | 10 ++++++---- src/libraries/System.Linq/tests/MinTests.cs | 11 ++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Linq/tests/MaxTests.cs b/src/libraries/System.Linq/tests/MaxTests.cs index 5d5d786a9e9d4..d9b020e7d4796 100644 --- a/src/libraries/System.Linq/tests/MaxTests.cs +++ b/src/libraries/System.Linq/tests/MaxTests.cs @@ -16,8 +16,11 @@ public static IEnumerable Max_AllTypes_TestData() yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i)), (byte)(length + length - 1) }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i).ToArray()), (byte)(length + length - 1) }; - yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i)), (sbyte)(length + length - 1) }; - yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i).ToArray()), (sbyte)(length + length - 1) }; + // Unit Tests does +T.One so we should generate data up to one value below sbyte.MaxValue + if ((length + length) < sbyte.MaxValue) { + yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i)), (sbyte)(length + length - 1) }; + yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i).ToArray()), (sbyte)(length + length - 1) }; + } yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (ushort)i)), (ushort)(length + length - 1) }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (ushort)i).ToArray()), (ushort)(length + length - 1) }; @@ -67,8 +70,7 @@ public void Max_AllTypes(IEnumerable source, T expected) where T : INumber T first = source.First(); Assert.Equal(first, source.Max(Comparer.Create((x, y) => x == first ? 1 : -1))); - if(expected != T.MaxValue) - Assert.Equal(expected + T.One, source.Max(x => x + T.One)); + Assert.Equal(expected + T.One, source.Max(x => x + T.One)); } [Fact] diff --git a/src/libraries/System.Linq/tests/MinTests.cs b/src/libraries/System.Linq/tests/MinTests.cs index efd05cb9f6fdf..feca6994d066d 100644 --- a/src/libraries/System.Linq/tests/MinTests.cs +++ b/src/libraries/System.Linq/tests/MinTests.cs @@ -16,9 +16,11 @@ public static IEnumerable Min_AllTypes_TestData() yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i)), (byte)length }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (byte)i).ToArray()), (byte)length }; - // dont generate testdata outside of the datatype bounds - yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Where(i => i <= sbyte.MaxValue).Select(i => (sbyte)i)), (sbyte)length }; - yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Where(i => i <= sbyte.MaxValue).Select(i => (sbyte)i).ToArray()), (sbyte)length }; + // Unit Tests does +T.One so we should generate data up to one value below sbyte.MaxValue, otherwise the type overflows + if ((length + length) < sbyte.MaxValue) { + yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i)), (sbyte)length }; + yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (sbyte)i).ToArray()), (sbyte)length }; + } yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (ushort)i)), (ushort)length }; yield return new object[] { Shuffler.Shuffle(Enumerable.Range(length, length).Select(i => (ushort)i).ToArray()), (ushort)length }; @@ -68,8 +70,7 @@ public void Min_AllTypes(IEnumerable source, T expected) where T : INumber T first = source.First(); Assert.Equal(first, source.Min(Comparer.Create((x, y) => x == first ? -1 : 1))); - if(expected != T.MaxValue) - Assert.Equal(expected + T.One, source.Min(x => x + T.One)); + Assert.Equal(expected + T.One, source.Min(x => x + T.One)); } [Fact]