From 9e078ffab41899650419a5928832a4302b6881e0 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 27 Mar 2018 00:27:21 -0700 Subject: Vectorized SequenceCompareTo for Span (#17237) - This change makes the compare for very short Span strings a bit slower and for longer Span strings many times faster. - Switch several places where it was a clear benefit to use it. `String.CompareOrdinal(string,string)` is notable exception that I have left intact for now. It is fine tuned for current string layout, and replacing with a call of vectorized SequenceCompareTo gives mixed results. --- src/classlibnative/bcltype/stringnative.cpp | 38 ---- .../shared/System.Private.CoreLib.Shared.projitems | 1 + .../shared/System/Globalization/CompareInfo.cs | 28 +-- src/mscorlib/shared/System/MemoryExtensions.cs | 14 ++ src/mscorlib/shared/System/SpanHelpers.Char.cs | 201 +++++++++++++++++++++ src/mscorlib/shared/System/SpanHelpers.T.cs | 119 ------------ src/mscorlib/shared/System/String.Manipulation.cs | 4 +- src/mscorlib/src/System/String.Comparison.cs | 90 +++------ src/vm/ecalllist.h | 1 - src/vm/object.cpp | 162 ----------------- src/vm/object.h | 2 - 11 files changed, 253 insertions(+), 407 deletions(-) create mode 100644 src/mscorlib/shared/System/SpanHelpers.Char.cs diff --git a/src/classlibnative/bcltype/stringnative.cpp b/src/classlibnative/bcltype/stringnative.cpp index 8b040dc038..6bf6deaf7e 100644 --- a/src/classlibnative/bcltype/stringnative.cpp +++ b/src/classlibnative/bcltype/stringnative.cpp @@ -109,44 +109,6 @@ FCIMPL2(INT32, COMString::FCCompareOrdinalIgnoreCaseWC, StringObject* strA, __in } FCIMPLEND -/*================================CompareOrdinalEx=============================== -**Args: typedef struct {STRINGREF thisRef; INT32 options; INT32 length; INT32 valueOffset;\ - STRINGREF value; INT32 thisOffset;} _compareOrdinalArgsEx; -==============================================================================*/ - -FCIMPL6(INT32, COMString::CompareOrdinalEx, StringObject* strA, INT32 indexA, INT32 countA, StringObject* strB, INT32 indexB, INT32 countB) -{ - FCALL_CONTRACT; - - VALIDATEOBJECT(strA); - VALIDATEOBJECT(strB); - DWORD *strAChars, *strBChars; - int strALength, strBLength; - - // These runtime tests are handled in the managed wrapper. - _ASSERTE(strA != NULL && strB != NULL); - _ASSERTE(indexA >= 0 && indexB >= 0); - _ASSERTE(countA >= 0 && countB >= 0); - - strA->RefInterpretGetStringValuesDangerousForGC((WCHAR **) &strAChars, &strALength); - strB->RefInterpretGetStringValuesDangerousForGC((WCHAR **) &strBChars, &strBLength); - - _ASSERTE(countA <= strALength - indexA); - _ASSERTE(countB <= strBLength - indexB); - - // Set up the loop variables. - strAChars = (DWORD *) ((WCHAR *) strAChars + indexA); - strBChars = (DWORD *) ((WCHAR *) strBChars + indexB); - - INT32 result = StringObject::FastCompareStringHelper(strAChars, countA, strBChars, countB); - - FC_GC_POLL_RET(); - return result; - -} -FCIMPLEND - - /*==================================GETCHARAT=================================== **Returns the character at position index. Thows IndexOutOfRangeException as **appropriate. diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems index f4f8dd32a2..4f7e0614e3 100644 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems @@ -492,6 +492,7 @@ + diff --git a/src/mscorlib/shared/System/Globalization/CompareInfo.cs b/src/mscorlib/shared/System/Globalization/CompareInfo.cs index 4ccd739bcd..079b8afbc0 100644 --- a/src/mscorlib/shared/System/Globalization/CompareInfo.cs +++ b/src/mscorlib/shared/System/Globalization/CompareInfo.cs @@ -341,7 +341,7 @@ namespace System.Globalization if (_invariantMode) { if ((options & CompareOptions.IgnoreCase) != 0) - return CompareOrdinalIgnoreCase(string1, 0, string1.Length, string2, 0, string2.Length); + return CompareOrdinalIgnoreCase(string1, string2); return String.CompareOrdinal(string1, string2); } @@ -501,35 +501,23 @@ namespace System.Globalization return (1); } + ReadOnlySpan span1 = string1.AsSpan(offset1, length1); + ReadOnlySpan span2 = string2.AsSpan(offset2, length2); + if (options == CompareOptions.Ordinal) { - return CompareOrdinal(string1, offset1, length1, - string2, offset2, length2); + return string.CompareOrdinal(span1, span2); } if (_invariantMode) { if ((options & CompareOptions.IgnoreCase) != 0) - return CompareOrdinalIgnoreCase(string1, offset1, length1, string2, offset2, length2); + return CompareOrdinalIgnoreCase(span1, span2); - return CompareOrdinal(string1, offset1, length1, string2, offset2, length2); + return string.CompareOrdinal(span1, span2); } - return CompareString( - string1.AsSpan(offset1, length1), - string2.AsSpan(offset2, length2), - options); - } - - private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2) - { - int result = String.CompareOrdinal(string1, offset1, string2, offset2, - (length1 < length2 ? length1 : length2)); - if ((length1 != length2) && result == 0) - { - return (length1 > length2 ? 1 : -1); - } - return (result); + return CompareString(span1, span2, options); } // diff --git a/src/mscorlib/shared/System/MemoryExtensions.cs b/src/mscorlib/shared/System/MemoryExtensions.cs index 0a06f3584e..1a3b33acc7 100644 --- a/src/mscorlib/shared/System/MemoryExtensions.cs +++ b/src/mscorlib/shared/System/MemoryExtensions.cs @@ -301,6 +301,13 @@ namespace System ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); + if (typeof(T) == typeof(char)) + return SpanHelpers.SequenceCompareTo( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(other)), + other.Length); + return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } @@ -659,6 +666,13 @@ namespace System ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); + if (typeof(T) == typeof(char)) + return SpanHelpers.SequenceCompareTo( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(other)), + other.Length); + return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } diff --git a/src/mscorlib/shared/System/SpanHelpers.Char.cs b/src/mscorlib/shared/System/SpanHelpers.Char.cs new file mode 100644 index 0000000000..2033d8b906 --- /dev/null +++ b/src/mscorlib/shared/System/SpanHelpers.Char.cs @@ -0,0 +1,201 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +#if !netstandard +using Internal.Runtime.CompilerServices; +#endif + +#if !netstandard11 +using System.Numerics; +#endif + +namespace System +{ + internal static partial class SpanHelpers + { + public static unsafe int SequenceCompareTo(ref char first, int firstLength, ref char second, int secondLength) + { + Debug.Assert(firstLength >= 0); + Debug.Assert(secondLength >= 0); + + int lengthDelta = firstLength - secondLength; + + if (Unsafe.AreSame(ref first, ref second)) + goto Equal; + + IntPtr minLength = (IntPtr)((firstLength < secondLength) ? firstLength : secondLength); + IntPtr i = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations + + if ((byte*)minLength >= (byte*)(sizeof(UIntPtr) / sizeof(char))) + { +#if !netstandard11 + if (Vector.IsHardwareAccelerated && (byte*)minLength >= (byte*)Vector.Count) + { + IntPtr nLength = minLength - Vector.Count; + do + { + if (Unsafe.ReadUnaligned>(ref Unsafe.As(ref Unsafe.Add(ref first, i))) != + Unsafe.ReadUnaligned>(ref Unsafe.As(ref Unsafe.Add(ref second, i)))) + { + break; + } + i += Vector.Count; + } + while ((byte*)nLength >= (byte*)i); + } +#endif + + while ((byte*)minLength >= (byte*)(i + sizeof(UIntPtr) / sizeof(char))) + { + if (Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref first, i))) != + Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref second, i)))) + { + break; + } + i += sizeof(UIntPtr) / sizeof(char); + } + } + + if (sizeof(UIntPtr) > sizeof(int) && (byte*)minLength >= (byte*)(i + sizeof(int) / sizeof(char))) + { + if (Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref first, i))) == + Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref second, i)))) + { + i += sizeof(int) / sizeof(char); + } + } + + while ((byte*)i < (byte*)minLength) + { + int result = Unsafe.Add(ref first, i).CompareTo(Unsafe.Add(ref second, i)); + if (result != 0) return result; + i += 1; + } + + Equal: + return lengthDelta; + } + + public static unsafe int IndexOf(ref char searchSpace, char value, int length) + { + Debug.Assert(length >= 0); + + uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions + IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations + IntPtr nLength = (IntPtr)length; +#if !netstandard11 + if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) + { + const int elementsPerByte = sizeof(ushort) / sizeof(byte); + int unaligned = ((int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1)) / elementsPerByte; + nLength = (IntPtr)((Vector.Count - unaligned) & (Vector.Count - 1)); + } + SequentialScan: +#endif + while ((byte*)nLength >= (byte*)4) + { + nLength -= 4; + + if (uValue == Unsafe.Add(ref searchSpace, index)) + goto Found; + if (uValue == Unsafe.Add(ref searchSpace, index + 1)) + goto Found1; + if (uValue == Unsafe.Add(ref searchSpace, index + 2)) + goto Found2; + if (uValue == Unsafe.Add(ref searchSpace, index + 3)) + goto Found3; + + index += 4; + } + + while ((byte*)nLength > (byte*)0) + { + nLength -= 1; + + if (uValue == Unsafe.Add(ref searchSpace, index)) + goto Found; + + index += 1; + } +#if !netstandard11 + if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) + { + nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector.Count - 1)); + + // Get comparison Vector + Vector vComparison = new Vector(value); + + while ((byte*)nLength > (byte*)index) + { + var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned>(ref Unsafe.As(ref Unsafe.Add(ref searchSpace, index)))); + if (Vector.Zero.Equals(vMatches)) + { + index += Vector.Count; + continue; + } + // Find offset of first match + return (int)(byte*)index + LocateFirstFoundChar(vMatches); + } + + if ((int)(byte*)index < length) + { + nLength = (IntPtr)(length - (int)(byte*)index); + goto SequentialScan; + } + } +#endif + return -1; + Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 + return (int)(byte*)index; + Found1: + return (int)(byte*)(index + 1); + Found2: + return (int)(byte*)(index + 2); + Found3: + return (int)(byte*)(index + 3); + } + +#if !netstandard11 + // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int LocateFirstFoundChar(Vector match) + { + var vector64 = Vector.AsVectorUInt64(match); + ulong candidate = 0; + int i = 0; + // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001 + for (; i < Vector.Count; i++) + { + candidate = vector64[i]; + if (candidate != 0) + { + break; + } + } + + // Single LEA instruction with jitted const (using function result) + return i * 4 + LocateFirstFoundChar(candidate); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int LocateFirstFoundChar(ulong match) + { + unchecked + { + // Flag least significant power of two bit + var powerOfTwoFlag = match ^ (match - 1); + // Shift all powers of two into the high byte and extract + return (int)((powerOfTwoFlag * XorPowerOfTwoToHighChar) >> 49); + } + } + + private const ulong XorPowerOfTwoToHighChar = (0x03ul | + 0x02ul << 16 | + 0x01ul << 32) + 1; +#endif + } +} diff --git a/src/mscorlib/shared/System/SpanHelpers.T.cs b/src/mscorlib/shared/System/SpanHelpers.T.cs index 818216bffa..4567deddb9 100644 --- a/src/mscorlib/shared/System/SpanHelpers.T.cs +++ b/src/mscorlib/shared/System/SpanHelpers.T.cs @@ -127,85 +127,6 @@ namespace System return (int)(byte*)(index + 7); } - public static unsafe int IndexOf(ref char searchSpace, char value, int length) - { - Debug.Assert(length >= 0); - - uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions - IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations - IntPtr nLength = (IntPtr)length; -#if !netstandard11 - if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) - { - const int elementsPerByte = sizeof(ushort) / sizeof(byte); - int unaligned = ((int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1)) / elementsPerByte; - nLength = (IntPtr)((Vector.Count - unaligned) & (Vector.Count - 1)); - } - SequentialScan: -#endif - while ((byte*)nLength >= (byte*)4) - { - nLength -= 4; - - if (uValue == Unsafe.Add(ref searchSpace, index)) - goto Found; - if (uValue == Unsafe.Add(ref searchSpace, index + 1)) - goto Found1; - if (uValue == Unsafe.Add(ref searchSpace, index + 2)) - goto Found2; - if (uValue == Unsafe.Add(ref searchSpace, index + 3)) - goto Found3; - - index += 4; - } - - while ((byte*)nLength > (byte*)0) - { - nLength -= 1; - - if (uValue == Unsafe.Add(ref searchSpace, index)) - goto Found; - - index += 1; - } -#if !netstandard11 - if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length)) - { - nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector.Count - 1)); - - // Get comparison Vector - Vector vComparison = new Vector(value); - - while ((byte*)nLength > (byte*)index) - { - var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned>(ref Unsafe.As(ref Unsafe.Add(ref searchSpace, index)))); - if (Vector.Zero.Equals(vMatches)) - { - index += Vector.Count; - continue; - } - // Find offset of first match - return (int)(byte*)index + LocateFirstFoundChar(vMatches); - } - - if ((int)(byte*)index < length) - { - nLength = (IntPtr)(length - (int)(byte*)index); - goto SequentialScan; - } - } -#endif - return -1; - Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549 - return (int)(byte*)index; - Found1: - return (int)(byte*)(index + 1); - Found2: - return (int)(byte*)(index + 2); - Found3: - return (int)(byte*)(index + 3); - } - public static int IndexOfAny(ref T searchSpace, T value0, T value1, int length) where T : IEquatable { @@ -761,45 +682,5 @@ namespace System } return firstLength.CompareTo(secondLength); } - -#if !netstandard11 - // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int LocateFirstFoundChar(Vector match) - { - var vector64 = Vector.AsVectorUInt64(match); - ulong candidate = 0; - int i = 0; - // Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001 - for (; i < Vector.Count; i++) - { - candidate = vector64[i]; - if (candidate != 0) - { - break; - } - } - - // Single LEA instruction with jitted const (using function result) - return i * 4 + LocateFirstFoundChar(candidate); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int LocateFirstFoundChar(ulong match) - { - unchecked - { - // Flag least significant power of two bit - var powerOfTwoFlag = match ^ (match - 1); - // Shift all powers of two into the high byte and extract - return (int)((powerOfTwoFlag * XorPowerOfTwoToHighChar) >> 49); - } - } - - private const ulong XorPowerOfTwoToHighChar = (0x03ul | - 0x02ul << 16 | - 0x01ul << 32) + 1; -#endif - } } diff --git a/src/mscorlib/shared/System/String.Manipulation.cs b/src/mscorlib/shared/System/String.Manipulation.cs index d2aac0d431..69609aacfb 100644 --- a/src/mscorlib/shared/System/String.Manipulation.cs +++ b/src/mscorlib/shared/System/String.Manipulation.cs @@ -1541,7 +1541,7 @@ namespace System if (this[i] == separator[0] && currentSepLength <= Length - i) { if (currentSepLength == 1 - || CompareOrdinal(this, i, separator, 0, currentSepLength) == 0) + || this.AsSpan(i, currentSepLength).SequenceEqual(separator)) { sepListBuilder.Append(i); i += currentSepLength - 1; @@ -1575,7 +1575,7 @@ namespace System if (this[i] == separator[0] && currentSepLength <= Length - i) { if (currentSepLength == 1 - || CompareOrdinal(this, i, separator, 0, currentSepLength) == 0) + || this.AsSpan(i, currentSepLength).SequenceEqual(separator)) { sepListBuilder.Append(i); lengthListBuilder.Append(currentSepLength); diff --git a/src/mscorlib/src/System/String.Comparison.cs b/src/mscorlib/src/System/String.Comparison.cs index 14aa4b74b8..34c20ba7dd 100644 --- a/src/mscorlib/src/System/String.Comparison.cs +++ b/src/mscorlib/src/System/String.Comparison.cs @@ -21,10 +21,6 @@ namespace System { public partial class String { - // - //Native Static Methods - // - private static unsafe int CompareOrdinalIgnoreCaseHelper(String strA, String strB) { Debug.Assert(strA != null); @@ -61,16 +57,6 @@ namespace System } } - // native call to COMString::CompareOrdinalEx - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern int CompareOrdinalHelper(String strA, int indexA, int countA, String strB, int indexB, int countB); - - // - // - // NATIVE INSTANCE METHODS - // - // - // // Search/Query methods // @@ -88,6 +74,18 @@ namespace System ((nuint)strA.Length) * 2); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int CompareOrdinalHelper(String strA, int indexA, int countA, String strB, int indexB, int countB) + { + Debug.Assert(strA != null); + Debug.Assert(strB != null); + Debug.Assert(indexA >= 0 && indexB >= 0); + Debug.Assert(countA >= 0 && countB >= 0); + Debug.Assert(indexA + countA <= strA.Length && indexB + countB <= strB.Length); + + return SpanHelpers.SequenceCompareTo(ref Unsafe.Add(ref strA.GetRawStringData(), indexA), countA, ref Unsafe.Add(ref strB.GetRawStringData(), indexB), countB); + } + private static unsafe bool EqualsIgnoreCaseAsciiHelper(String strA, String strB) { Debug.Assert(strA != null); @@ -315,13 +313,14 @@ namespace System return CompareOrdinalHelper(strA, strB); case StringComparison.OrdinalIgnoreCase: +#if CORECLR // If both strings are ASCII strings, we can take the fast path. if (strA.IsAscii() && strB.IsAscii()) { - return (CompareOrdinalIgnoreCaseHelper(strA, strB)); + return CompareOrdinalIgnoreCaseHelper(strA, strB); } - - return CompareInfo.CompareOrdinalIgnoreCase(strA, 0, strA.Length, strB, 0, strB.Length); +#endif + return CompareInfo.CompareOrdinalIgnoreCase(strA, strB); default: throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); @@ -503,7 +502,7 @@ namespace System return CompareOrdinalHelper(strA, indexA, lengthA, strB, indexB, lengthB); case StringComparison.OrdinalIgnoreCase: - return (CompareInfo.CompareOrdinalIgnoreCase(strA, indexA, lengthA, strB, indexB, lengthB)); + return CompareInfo.CompareOrdinalIgnoreCase(strA, indexA, lengthA, strB, indexB, lengthB); default: throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); @@ -539,40 +538,9 @@ namespace System return CompareOrdinalHelper(strA, strB); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int CompareOrdinal(ReadOnlySpan strA, ReadOnlySpan strB) - { - // TODO: Add a vectorized code path, similar to SequenceEqual - // https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/SpanHelpers.byte.cs#L900 - - int minLength = Math.Min(strA.Length, strB.Length); - ref char first = ref MemoryMarshal.GetReference(strA); - ref char second = ref MemoryMarshal.GetReference(strB); - - int i = 0; - if (minLength >= sizeof(nuint) / sizeof(char)) - { - while (i < minLength - sizeof(nuint) / sizeof(char)) - { - if (Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref first, i))) != - Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref second, i)))) - { - break; - } - i += sizeof(nuint) / sizeof(char); - } - } - while (i < minLength) - { - char a = Unsafe.Add(ref first, i); - char b = Unsafe.Add(ref second, i); - if (a != b) - { - return a - b; - } - i++; - } - return strA.Length - strB.Length; - } + => SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(strA), strA.Length, ref MemoryMarshal.GetReference(strB), strB.Length); // Compares strA and strB using an ordinal (code-point) comparison. // @@ -795,13 +763,13 @@ namespace System case StringComparison.OrdinalIgnoreCase: if (this.Length != value.Length) return false; - +#if CORECLR // If both strings are ASCII strings, we can take the fast path. if (this.IsAscii() && value.IsAscii()) { return EqualsIgnoreCaseAsciiHelper(this, value); } - +#endif return (CompareInfo.CompareOrdinalIgnoreCase(this, 0, this.Length, value, 0, value.Length) == 0); default: @@ -857,23 +825,19 @@ namespace System case StringComparison.Ordinal: if (a.Length != b.Length) return false; - return EqualsHelper(a, b); case StringComparison.OrdinalIgnoreCase: if (a.Length != b.Length) return false; - else +#if CORECLR + // If both strings are ASCII strings, we can take the fast path. + if (a.IsAscii() && b.IsAscii()) { - // If both strings are ASCII strings, we can take the fast path. - if (a.IsAscii() && b.IsAscii()) - { - return EqualsIgnoreCaseAsciiHelper(a, b); - } - // Take the slow path. - - return (CompareInfo.CompareOrdinalIgnoreCase(a, 0, a.Length, b, 0, b.Length) == 0); + return EqualsIgnoreCaseAsciiHelper(a, b); } +#endif + return (CompareInfo.CompareOrdinalIgnoreCase(a, 0, a.Length, b, 0, b.Length) == 0); default: throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType)); diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 7e96bc7798..92d75e3f90 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -110,7 +110,6 @@ FCFuncStart(gStringFuncs) FCIntrinsic("get_Length", COMString::Length, CORINFO_INTRINSIC_StringLength) FCIntrinsic("get_Chars", COMString::GetCharAt, CORINFO_INTRINSIC_StringGetChar) FCFuncElement("IsAscii", COMString::IsAscii) - FCFuncElement("CompareOrdinalHelper", COMString::CompareOrdinalEx) #ifdef FEATURE_COMINTEROP FCFuncElement("SetTrailByte", COMString::FCSetTrailByte) FCFuncElement("TryGetTrailByte", COMString::FCTryGetTrailByte) diff --git a/src/vm/object.cpp b/src/vm/object.cpp index 0282b6a6a4..83afcd2fe5 100644 --- a/src/vm/object.cpp +++ b/src/vm/object.cpp @@ -1148,168 +1148,6 @@ BOOL StringObject::CaseInsensitiveCompHelper(__in_ecount(aLength) WCHAR *strACha } -INT32 StringObject::FastCompareStringHelper(DWORD* strAChars, INT32 countA, DWORD* strBChars, INT32 countB) -{ - STATIC_CONTRACT_SO_TOLERANT; - - INT32 count = (countA < countB) ? countA : countB; - - PREFIX_ASSUME(count >= 0); - - ptrdiff_t diff = (char *)strAChars - (char *)strBChars; - -#if defined(_WIN64) || defined(ALIGN_ACCESS) - int alignmentA = ((SIZE_T)strAChars) & (sizeof(SIZE_T) - 1); - int alignmentB = ((SIZE_T)strBChars) & (sizeof(SIZE_T) - 1); -#endif // _WIN64 || ALIGN_ACCESS - -#if defined(_WIN64) - if (alignmentA == alignmentB) - { - if ((alignmentA == 2 || alignmentA == 6) && (count >= 1)) - { - LPWSTR ptr2 = (WCHAR *)strBChars; - - if (( *((WCHAR*)((char *)ptr2 + diff)) - *ptr2) != 0) - { - return ((int)*((WCHAR*)((char *)ptr2 + diff)) - (int)*ptr2); - } - strBChars = (DWORD*)(++ptr2); - count -= 1; - alignmentA = (alignmentA == 2 ? 4 : 0); - } - - if ((alignmentA == 4) && (count >= 2)) - { - DWORD* ptr2 = (DWORD*)strBChars; - - if (( *((DWORD*)((char *)ptr2 + diff)) - *ptr2) != 0) - { - LPWSTR chkptr1 = (WCHAR*)((char *)strBChars + diff); - LPWSTR chkptr2 = (WCHAR*)strBChars; - - if (*chkptr1 != *chkptr2) - { - return ((int)*chkptr1 - (int)*chkptr2); - } - return ((int)*(chkptr1+1) - (int)*(chkptr2+1)); - } - strBChars = ++ptr2; - count -= 2; - alignmentA = 0; - } - - if (alignmentA == 0) - { - while (count >= 4) - { - SIZE_T* ptr2 = (SIZE_T*)strBChars; - - if (( *((SIZE_T*)((char *)ptr2 + diff)) - *ptr2) != 0) - { - if (( *((DWORD*)((char *)ptr2 + diff)) - *(DWORD*)ptr2) != 0) - { - LPWSTR chkptr1 = (WCHAR*)((char *)strBChars + diff); - LPWSTR chkptr2 = (WCHAR*)strBChars; - - if (*chkptr1 != *chkptr2) - { - return ((int)*chkptr1 - (int)*chkptr2); - } - return ((int)*(chkptr1+1) - (int)*(chkptr2+1)); - } - else - { - LPWSTR chkptr1 = (WCHAR*)((DWORD*)((char *)strBChars + diff) + 1); - LPWSTR chkptr2 = (WCHAR*)((DWORD*)strBChars + 1); - - if (*chkptr1 != *chkptr2) - { - return ((int)*chkptr1 - (int)*chkptr2); - } - return ((int)*(chkptr1+1) - (int)*(chkptr2+1)); - } - } - strBChars = (DWORD*)(++ptr2); - count -= 4; - } - } - - LPWSTR ptr2 = (WCHAR*)strBChars; - while ((count -= 1) >= 0) - { - if (( *((WCHAR*)((char *)ptr2 + diff)) - *ptr2) != 0) - { - return ((int)*((WCHAR*)((char *)ptr2 + diff)) - (int)*ptr2); - } - ++ptr2; - } - } - else -#endif // _WIN64 -#if defined(ALIGN_ACCESS) - if ( ( !IS_ALIGNED((size_t)strAChars, sizeof(DWORD)) || - !IS_ALIGNED((size_t)strBChars, sizeof(DWORD)) ) && - (abs(alignmentA - alignmentB) != 4) ) - { - _ASSERTE(IS_ALIGNED((size_t)strAChars, sizeof(WCHAR))); - _ASSERTE(IS_ALIGNED((size_t)strBChars, sizeof(WCHAR))); - LPWSTR ptr2 = (WCHAR *)strBChars; - - while ((count -= 1) >= 0) - { - if (( *((WCHAR*)((char *)ptr2 + diff)) - *ptr2) != 0) - { - return ((int)*((WCHAR*)((char *)ptr2 + diff)) - (int)*ptr2); - } - ++ptr2; - } - } - else -#endif // ALIGN_ACCESS - { -#if defined(_WIN64) || defined(ALIGN_ACCESS) - if (abs(alignmentA - alignmentB) == 4) - { - if ((alignmentA == 2) || (alignmentB == 2)) - { - LPWSTR ptr2 = (WCHAR *)strBChars; - - if (( *((WCHAR*)((char *)ptr2 + diff)) - *ptr2) != 0) - { - return ((int)*((WCHAR*)((char *)ptr2 + diff)) - (int)*ptr2); - } - strBChars = (DWORD*)(++ptr2); - count -= 1; - } - } -#endif // WIN64 || ALIGN_ACCESS - - // Loop comparing a DWORD at a time. - while ((count -= 2) >= 0) - { - if ((*((DWORD* )((char *)strBChars + diff)) - *strBChars) != 0) - { - LPWSTR ptr1 = (WCHAR*)((char *)strBChars + diff); - LPWSTR ptr2 = (WCHAR*)strBChars; - if (*ptr1 != *ptr2) { - return ((int)*ptr1 - (int)*ptr2); - } - return ((int)*(ptr1+1) - (int)*(ptr2+1)); - } - ++strBChars; - } - - int c; - if (count == -1) - if ((c = *((WCHAR *) ((char *)strBChars + diff)) - *((WCHAR *) strBChars)) != 0) - return c; - } - - return countA - countB; -} - - /*=============================InternalHasHighChars============================= **Action: Checks if the string can be sorted quickly. The requirements are that ** the string contain no character greater than 0x80 and that the string not diff --git a/src/vm/object.h b/src/vm/object.h index b0125fa50e..4324fcac2e 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -1060,8 +1060,6 @@ class StringObject : public Object private: - static INT32 FastCompareStringHelper(DWORD* strAChars, INT32 countA, DWORD* strBChars, INT32 countB); - static STRINGREF* EmptyStringRefPtr; }; -- cgit v1.2.3