Not sure if these were missed or intentionally left out. It refers to e.g.
|
public static unsafe int IndexOfAnyExcept<T>(this ReadOnlySpan<T> span, T value0, T value1) where T : IEquatable<T>? |
|
{ |
|
if (RuntimeHelpers.IsBitwiseEquatable<T>()) |
|
{ |
|
if (sizeof(T) == sizeof(byte)) |
|
{ |
|
return SpanHelpers.IndexOfAnyExceptValueType( |
|
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), |
|
Unsafe.BitCast<T, byte>(value0), |
|
Unsafe.BitCast<T, byte>(value1), |
|
span.Length); |
|
} |
|
else if (sizeof(T) == sizeof(short)) |
|
{ |
|
return SpanHelpers.IndexOfAnyExceptValueType( |
|
ref Unsafe.As<T, short>(ref MemoryMarshal.GetReference(span)), |
|
Unsafe.BitCast<T, short>(value0), |
|
Unsafe.BitCast<T, short>(value1), |
|
span.Length); |
|
} |
|
} |
|
|
|
return SpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); |
|
} |
Description
Several multi-value search methods in MemoryExtensions dispatch to vectorized SpanHelpers helpers for byte and short-sized types, but silently fall through to the slow scalar IEquatable<T>.Equals-based path for int and long-sized types.
The SpanHelpers helpers already support all four sizes generically (T : struct, INumber<T>) — the fix is purely in MemoryExtensions.cs.
The affected methods and their current coverage:
| Method |
byte |
short |
int |
long |
IndexOfAny(span, v0, v1) |
✅ |
✅ |
❌ |
❌ |
IndexOfAny(span, v0, v1, comparer) |
✅ |
✅ |
❌ |
❌ |
IndexOfAny(span, v0, v1, v2) |
✅ |
✅ |
❌ |
❌ |
IndexOfAny(span, v0, v1, v2, comparer) |
✅ |
✅ |
❌ |
❌ |
IndexOfAnyExcept(span, v0, v1) |
✅ |
✅ |
❌ |
❌ |
IndexOfAnyExcept(span, v0, v1, comparer) |
✅ |
✅ |
❌ |
❌ |
IndexOfAnyExcept(span, v0, v1, v2) |
✅ |
✅ |
❌ |
❌ |
IndexOfAnyExcept(span, v0, v1, v2, comparer) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAny(span, v0, v1) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAny(span, v0, v1, comparer) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAny(span, v0, v1, v2) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAny(span, v0, v1, v2, comparer) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAnyExcept(span, v0, v1) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAnyExcept(span, v0, v1, comparer) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAnyExcept(span, v0, v1, v2) |
✅ |
✅ |
❌ |
❌ |
LastIndexOfAnyExcept(span, v0, v1, v2, comparer) |
✅ |
✅ |
❌ |
❌ |
Analysis
The SpanHelpers helpers (IndexOfAnyValueType, IndexOfAnyExceptValueType, LastIndexOfAnyValueType, LastIndexOfAnyExceptValueType) are all generic over T : struct, INumber<T> and their implementations are fully vectorized for all four sizes (the Debug.Assert(value0 is byte or short or int or long, ...) inside confirms this). No changes to SpanHelpers are required.
The fix is to add sizeof(int) and sizeof(long) branches to each affected method in MemoryExtensions.cs, e.g. for IndexOfAny(span, v0, v1):
else if (sizeof(T) == sizeof(int))
{
return SpanHelpers.IndexOfAnyValueType(
ref Unsafe.As<T, int>(ref MemoryMarshal.GetReference(span)),
Unsafe.BitCast<T, int>(value0),
Unsafe.BitCast<T, int>(value1),
span.Length);
}
else if (sizeof(T) == sizeof(long))
{
return SpanHelpers.IndexOfAnyValueType(
ref Unsafe.As<T, long>(ref MemoryMarshal.GetReference(span)),
Unsafe.BitCast<T, long>(value0),
Unsafe.BitCast<T, long>(value1),
span.Length);
}
Not sure if these were missed or intentionally left out. It refers to e.g.
runtime/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs
Lines 938 to 961 in 8f65ed7
Description
Several multi-value search methods in
MemoryExtensionsdispatch to vectorizedSpanHelpershelpers forbyteandshort-sized types, but silently fall through to the slow scalarIEquatable<T>.Equals-based path forintandlong-sized types.The
SpanHelpershelpers already support all four sizes generically (T : struct, INumber<T>) — the fix is purely inMemoryExtensions.cs.The affected methods and their current coverage:
byteshortintlongIndexOfAny(span, v0, v1)IndexOfAny(span, v0, v1, comparer)IndexOfAny(span, v0, v1, v2)IndexOfAny(span, v0, v1, v2, comparer)IndexOfAnyExcept(span, v0, v1)IndexOfAnyExcept(span, v0, v1, comparer)IndexOfAnyExcept(span, v0, v1, v2)IndexOfAnyExcept(span, v0, v1, v2, comparer)LastIndexOfAny(span, v0, v1)LastIndexOfAny(span, v0, v1, comparer)LastIndexOfAny(span, v0, v1, v2)LastIndexOfAny(span, v0, v1, v2, comparer)LastIndexOfAnyExcept(span, v0, v1)LastIndexOfAnyExcept(span, v0, v1, comparer)LastIndexOfAnyExcept(span, v0, v1, v2)LastIndexOfAnyExcept(span, v0, v1, v2, comparer)Analysis
The
SpanHelpershelpers (IndexOfAnyValueType,IndexOfAnyExceptValueType,LastIndexOfAnyValueType,LastIndexOfAnyExceptValueType) are all generic overT : struct, INumber<T>and their implementations are fully vectorized for all four sizes (theDebug.Assert(value0 is byte or short or int or long, ...)inside confirms this). No changes toSpanHelpersare required.The fix is to add
sizeof(int)andsizeof(long)branches to each affected method inMemoryExtensions.cs, e.g. forIndexOfAny(span, v0, v1):