diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/SingleStringSearchValuesThreeChars.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/SingleStringSearchValuesThreeChars.cs index c005173b67e143..e0e137cdaad337 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/SingleStringSearchValuesThreeChars.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/SingleStringSearchValuesThreeChars.cs @@ -88,55 +88,7 @@ private int IndexOf(ref char searchSpace, int searchSpaceLength) nuint ch2ByteOffset = _ch2ByteOffset; nuint ch3ByteOffset = _ch3ByteOffset; - if (Vector512.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector512.Count >= 0) - { - Vector512 ch1 = Vector512.Create(_ch1); - Vector512 ch2 = Vector512.Create(_ch2); - Vector512 ch3 = Vector512.Create(_ch3); - - ref char lastSearchSpace = ref Unsafe.Add(ref searchSpace, searchSpaceMinusValueTailLength - Vector512.Count); - - while (true) - { - ValidateReadPosition(ref searchSpaceStart, searchSpaceLength, ref searchSpace, Vector512.Count); - ValidateReadPosition(ref searchSpaceStart, searchSpaceLength, ref searchSpace, Vector512.Count + (int)(_ch2ByteOffset / 2)); - ValidateReadPosition(ref searchSpaceStart, searchSpaceLength, ref searchSpace, Vector512.Count + (int)(_ch3ByteOffset / 2)); - - // Find which starting positions likely contain a match (likely match all 3 anchor characters). - Vector512 result = GetComparisonResult(ref searchSpace, ch2ByteOffset, ch3ByteOffset, ch1, ch2, ch3); - - if (result != Vector512.Zero) - { - goto CandidateFound; - } - - LoopFooter: - // We haven't found a match. Update the input position and check if we've reached the end. - searchSpace = ref Unsafe.Add(ref searchSpace, Vector512.Count); - - if (Unsafe.IsAddressGreaterThan(ref searchSpace, ref lastSearchSpace)) - { - if (Unsafe.AreSame(ref searchSpace, ref Unsafe.Add(ref lastSearchSpace, Vector512.Count))) - { - return -1; - } - - // We have fewer than 32 characters remaining. Adjust the input position such that we will do one last loop iteration. - searchSpace = ref lastSearchSpace; - } - - continue; - - CandidateFound: - // We found potential matches, but they may be false-positives, so we must verify each one. - if (TryMatch(ref searchSpaceStart, searchSpaceLength, ref searchSpace, result.ExtractMostSignificantBits(), out int offset)) - { - return offset; - } - goto LoopFooter; - } - } - else if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector256.Count >= 0) + if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector256.Count >= 0) { Vector256 ch1 = Vector256.Create(_ch1); Vector256 ch2 = Vector256.Create(_ch2); @@ -300,29 +252,6 @@ private static Vector256 GetComparisonResult(ref char searchSpace, nuint c } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 GetComparisonResult(ref char searchSpace, nuint ch2ByteOffset, nuint ch3ByteOffset, Vector512 ch1, Vector512 ch2, Vector512 ch3) - { - // See comments in 'GetComparisonResult' for Vector128 above. - // This method is the same, but operates on 64 input characters at a time. - if (typeof(TCaseSensitivity) == typeof(CaseSensitive)) - { - Vector512 cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace)); - Vector512 cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref Unsafe.As(ref searchSpace), ch2ByteOffset).AsUInt16()); - Vector512 cmpCh3 = Vector512.Equals(ch3, Vector512.LoadUnsafe(ref Unsafe.As(ref searchSpace), ch3ByteOffset).AsUInt16()); - return (cmpCh1 & cmpCh2 & cmpCh3).AsByte(); - } - else - { - Vector512 caseConversion = Vector512.Create(CaseConversionMask); - - Vector512 cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace) & caseConversion); - Vector512 cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref Unsafe.As(ref searchSpace), ch2ByteOffset).AsUInt16() & caseConversion); - Vector512 cmpCh3 = Vector512.Equals(ch3, Vector512.LoadUnsafe(ref Unsafe.As(ref searchSpace), ch3ByteOffset).AsUInt16() & caseConversion); - return (cmpCh1 & cmpCh2 & cmpCh3).AsByte(); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryMatch(ref char searchSpaceStart, int searchSpaceLength, ref char searchSpace, uint mask, out int offsetFromStart) { @@ -351,35 +280,6 @@ private bool TryMatch(ref char searchSpaceStart, int searchSpaceLength, ref char return false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool TryMatch(ref char searchSpaceStart, int searchSpaceLength, ref char searchSpace, ulong mask, out int offsetFromStart) - { - // 'mask' encodes the input positions where at least 3 characters likely matched. - // Verify each one to see if we've found a match, otherwise return back to the vectorized loop. - do - { - int bitPos = BitOperations.TrailingZeroCount(mask); - Debug.Assert(bitPos % 2 == 0); - - ref char matchRef = ref Unsafe.AddByteOffset(ref searchSpace, bitPos); - - ValidateReadPosition(ref searchSpaceStart, searchSpaceLength, ref matchRef, _valueState.Value.Length); - - if (CanSkipAnchorMatchVerification || TCaseSensitivity.Equals(ref matchRef, in _valueState)) - { - offsetFromStart = (int)((nuint)Unsafe.ByteOffset(ref searchSpaceStart, ref matchRef) / 2); - return true; - } - - mask = BitOperations.ResetLowestSetBit(BitOperations.ResetLowestSetBit(mask)); - } - while (mask != 0); - - offsetFromStart = 0; - return false; - } - - internal override bool ContainsCore(string value) => HasUniqueValues ? base.ContainsCore(value) : _valueState.Value.Equals(value, IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs index 9cbeccb2f88de1..57174709cd3772 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs @@ -68,74 +68,7 @@ ref Unsafe.As(ref Unsafe.Add(ref searchSpace, offset + 1)), // Based on http://0x80.pl/articles/simd-strfind.html#algorithm-1-generic-simd "Algorithm 1: Generic SIMD" by Wojciech Mula // Some details about the implementation can also be found in https://github.com/dotnet/runtime/pull/63285 SEARCH_TWO_CHARS: - if (Vector512.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector512.Count >= 0) - { - // Find the last unique (which is not equal to ch1) character - // the algorithm is fine if both are equal, just a little bit less efficient - ushort ch2Val = Unsafe.Add(ref value, valueTailLength); - nint ch1ch2Distance = (nint)(uint)valueTailLength; - while (ch2Val == valueHead && ch1ch2Distance > 1) - ch2Val = Unsafe.Add(ref value, --ch1ch2Distance); - - Vector512 ch1 = Vector512.Create((ushort)valueHead); - Vector512 ch2 = Vector512.Create(ch2Val); - - nint searchSpaceMinusValueTailLengthAndVector = - searchSpaceMinusValueTailLength - (nint)Vector512.Count; - - do - { - // Make sure we don't go out of bounds - Debug.Assert(offset + ch1ch2Distance + Vector512.Count <= searchSpaceLength); - - Vector512 cmpCh2 = Vector512.Equals(ch2, Vector512.LoadUnsafe(ref searchSpace, (nuint)(offset + ch1ch2Distance))); - Vector512 cmpCh1 = Vector512.Equals(ch1, Vector512.LoadUnsafe(ref searchSpace, (nuint)offset)); - Vector512 cmpAnd = (cmpCh1 & cmpCh2).AsByte(); - - // Early out: cmpAnd is all zeros - if (cmpAnd != Vector512.Zero) - { - goto CANDIDATE_FOUND; - } - - LOOP_FOOTER: - offset += Vector512.Count; - - if (offset == searchSpaceMinusValueTailLength) - return -1; - - // Overlap with the current chunk for trailing elements - if (offset > searchSpaceMinusValueTailLengthAndVector) - offset = searchSpaceMinusValueTailLengthAndVector; - - continue; - - CANDIDATE_FOUND: - ulong mask = cmpAnd.ExtractMostSignificantBits(); - do - { - int bitPos = BitOperations.TrailingZeroCount(mask); - // div by 2 (shr) because we work with 2-byte chars - nint charPos = (nint)((uint)bitPos / 2); - if (valueLength == 2 || // we already matched two chars - SequenceEqual( - ref Unsafe.As(ref Unsafe.Add(ref searchSpace, offset + charPos)), - ref Unsafe.As(ref value), (nuint)(uint)valueLength * 2)) - { - return (int)(offset + charPos); - } - - // Clear two the lowest set bits - if (Bmi1.X64.IsSupported) - mask = Bmi1.X64.ResetLowestSetBit(Bmi1.X64.ResetLowestSetBit(mask)); - else - mask &= ~(ulong)((ulong)0b11 << bitPos); - } while (mask != 0); - goto LOOP_FOOTER; - - } while (true); - } - else if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector256.Count >= 0) + if (Vector256.IsHardwareAccelerated && searchSpaceMinusValueTailLength - Vector256.Count >= 0) { // Find the last unique (which is not equal to ch1) character // the algorithm is fine if both are equal, just a little bit less efficient