-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Closed as not planned
Labels
api-suggestionEarly API idea and discussion, it is NOT ready for implementationEarly API idea and discussion, it is NOT ready for implementationarea-System.Numerics
Description
Background and motivation
Now that we have params ReadOnlySpan<T>
, is it worth revisiting #47456 for variadic versions of Math.Min
and Math.Max
?
If you want to get the max value from multiple variables, you usually end up with some unwieldy code, like
var maxValue = Math.Max(Math.Max(Math.Max(val1, val2), val3), val4);
Otherwise you'd have to allocate an array and use System.Linq.Enumerable.Max
, i.e.
var maxValue = new[] { val1, val2, val3, val4 }.Max();
The implementation would basically be the Span
-path from the Enumerable.Max
implementation:
runtime/src/libraries/System.Linq/src/System/Linq/MaxMin.cs
Lines 35 to 122 in 4420903
if (span.IsEmpty) | |
{ | |
ThrowHelper.ThrowNoElementsException(); | |
} | |
if (!Vector128.IsHardwareAccelerated || !Vector128<T>.IsSupported || span.Length < Vector128<T>.Count) | |
{ | |
value = span[0]; | |
for (int i = 1; i < span.Length; i++) | |
{ | |
if (TMinMax.Compare(span[i], value)) | |
{ | |
value = span[i]; | |
} | |
} | |
} | |
else if (!Vector256.IsHardwareAccelerated || !Vector256<T>.IsSupported || span.Length < Vector256<T>.Count) | |
{ | |
ref T current = ref MemoryMarshal.GetReference(span); | |
ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector128<T>.Count); | |
Vector128<T> best = Vector128.LoadUnsafe(ref current); | |
current = ref Unsafe.Add(ref current, Vector128<T>.Count); | |
while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart)) | |
{ | |
best = TMinMax.Compare(best, Vector128.LoadUnsafe(ref current)); | |
current = ref Unsafe.Add(ref current, Vector128<T>.Count); | |
} | |
best = TMinMax.Compare(best, Vector128.LoadUnsafe(ref lastVectorStart)); | |
value = best[0]; | |
for (int i = 1; i < Vector128<T>.Count; i++) | |
{ | |
if (TMinMax.Compare(best[i], value)) | |
{ | |
value = best[i]; | |
} | |
} | |
} | |
else if (!Vector512.IsHardwareAccelerated || !Vector512<T>.IsSupported || span.Length < Vector512<T>.Count) | |
{ | |
ref T current = ref MemoryMarshal.GetReference(span); | |
ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector256<T>.Count); | |
Vector256<T> best = Vector256.LoadUnsafe(ref current); | |
current = ref Unsafe.Add(ref current, Vector256<T>.Count); | |
while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart)) | |
{ | |
best = TMinMax.Compare(best, Vector256.LoadUnsafe(ref current)); | |
current = ref Unsafe.Add(ref current, Vector256<T>.Count); | |
} | |
best = TMinMax.Compare(best, Vector256.LoadUnsafe(ref lastVectorStart)); | |
value = best[0]; | |
for (int i = 1; i < Vector256<T>.Count; i++) | |
{ | |
if (TMinMax.Compare(best[i], value)) | |
{ | |
value = best[i]; | |
} | |
} | |
} | |
else | |
{ | |
ref T current = ref MemoryMarshal.GetReference(span); | |
ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector512<T>.Count); | |
Vector512<T> best = Vector512.LoadUnsafe(ref current); | |
current = ref Unsafe.Add(ref current, Vector512<T>.Count); | |
while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart)) | |
{ | |
best = TMinMax.Compare(best, Vector512.LoadUnsafe(ref current)); | |
current = ref Unsafe.Add(ref current, Vector512<T>.Count); | |
} | |
best = TMinMax.Compare(best, Vector512.LoadUnsafe(ref lastVectorStart)); | |
value = best[0]; | |
for (int i = 1; i < Vector512<T>.Count; i++) | |
{ | |
if (TMinMax.Compare(best[i], value)) | |
{ | |
value = best[i]; | |
} | |
} | |
} |
These methods could even call the new Math
overloads.
API Proposal
namespace System;
public static class Math
{
public static byte Max(params ReadOnlySpan<byte> values);
public static byte Max(params ReadOnlySpan<decimal> values);
public static byte Max(params ReadOnlySpan<double> values);
public static byte Max(params ReadOnlySpan<short> values);
public static byte Max(params ReadOnlySpan<int> values);
public static byte Max(params ReadOnlySpan<nint> values);
public static byte Max(params ReadOnlySpan<sbyte> values);
public static byte Max(params ReadOnlySpan<float> values);
public static byte Max(params ReadOnlySpan<ushort> values);
public static byte Max(params ReadOnlySpan<uint> values);
public static byte Max(params ReadOnlySpan<ulong> values);
public static byte Max(params ReadOnlySpan<nuint> values);
public static byte Min(params ReadOnlySpan<byte> values);
public static byte Min(params ReadOnlySpan<decimal> values);
public static byte Min(params ReadOnlySpan<double> values);
public static byte Min(params ReadOnlySpan<short> values);
public static byte Min(params ReadOnlySpan<int> values);
public static byte Min(params ReadOnlySpan<nint> values);
public static byte Min(params ReadOnlySpan<sbyte> values);
public static byte Min(params ReadOnlySpan<float> values);
public static byte Min(params ReadOnlySpan<ushort> values);
public static byte Min(params ReadOnlySpan<uint> values);
public static byte Min(params ReadOnlySpan<ulong> values);
public static byte Min(params ReadOnlySpan<nuint> values);
}
API Usage
var maxValue = Math.Max(val1, val2, val3, val4);
Alternative Designs
No response
Risks
No response
sfiruch, Peter-Juhasz, Xor-el and AslanAmcaAslanAmca
Metadata
Metadata
Assignees
Labels
api-suggestionEarly API idea and discussion, it is NOT ready for implementationEarly API idea and discussion, it is NOT ready for implementationarea-System.Numerics