summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kotas <jkotas@microsoft.com>2018-03-27 00:27:21 -0700
committerGitHub <noreply@github.com>2018-03-27 00:27:21 -0700
commit9e078ffab41899650419a5928832a4302b6881e0 (patch)
treed93c373a4739e5d60c28e3e238bca72febee87ca
parent6b3d2dd677f5637e89153e8277184eca93152338 (diff)
downloadcoreclr-9e078ffab41899650419a5928832a4302b6881e0.tar.gz
coreclr-9e078ffab41899650419a5928832a4302b6881e0.tar.bz2
coreclr-9e078ffab41899650419a5928832a4302b6881e0.zip
Vectorized SequenceCompareTo for Span<char> (#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.
-rw-r--r--src/classlibnative/bcltype/stringnative.cpp38
-rw-r--r--src/mscorlib/shared/System.Private.CoreLib.Shared.projitems1
-rw-r--r--src/mscorlib/shared/System/Globalization/CompareInfo.cs28
-rw-r--r--src/mscorlib/shared/System/MemoryExtensions.cs14
-rw-r--r--src/mscorlib/shared/System/SpanHelpers.Char.cs201
-rw-r--r--src/mscorlib/shared/System/SpanHelpers.T.cs119
-rw-r--r--src/mscorlib/shared/System/String.Manipulation.cs4
-rw-r--r--src/mscorlib/src/System/String.Comparison.cs90
-rw-r--r--src/vm/ecalllist.h1
-rw-r--r--src/vm/object.cpp162
-rw-r--r--src/vm/object.h2
11 files changed, 253 insertions, 407 deletions
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 @@
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.BinarySearch.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Byte.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Char.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.Manipulation.cs" />
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<char> span1 = string1.AsSpan(offset1, length1);
+ ReadOnlySpan<char> 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<T, byte>(ref MemoryMarshal.GetReference(other)),
other.Length);
+ if (typeof(T) == typeof(char))
+ return SpanHelpers.SequenceCompareTo(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, char>(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<T, byte>(ref MemoryMarshal.GetReference(other)),
other.Length);
+ if (typeof(T) == typeof(char))
+ return SpanHelpers.SequenceCompareTo(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, char>(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<ushort>.Count)
+ {
+ IntPtr nLength = minLength - Vector<ushort>.Count;
+ do
+ {
+ if (Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) !=
+ Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref second, i))))
+ {
+ break;
+ }
+ i += Vector<ushort>.Count;
+ }
+ while ((byte*)nLength >= (byte*)i);
+ }
+#endif
+
+ while ((byte*)minLength >= (byte*)(i + sizeof(UIntPtr) / sizeof(char)))
+ {
+ if (Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) !=
+ Unsafe.ReadUnaligned<UIntPtr>(ref Unsafe.As<char, byte>(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<int>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) ==
+ Unsafe.ReadUnaligned<int>(ref Unsafe.As<char, byte>(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<ushort>.Count * 2)
+ {
+ const int elementsPerByte = sizeof(ushort) / sizeof(byte);
+ int unaligned = ((int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1)) / elementsPerByte;
+ nLength = (IntPtr)((Vector<ushort>.Count - unaligned) & (Vector<ushort>.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<ushort>.Count - 1));
+
+ // Get comparison Vector
+ Vector<ushort> vComparison = new Vector<ushort>(value);
+
+ while ((byte*)nLength > (byte*)index)
+ {
+ var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, index))));
+ if (Vector<ushort>.Zero.Equals(vMatches))
+ {
+ index += Vector<ushort>.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<ushort> 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<ulong>.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<ushort>.Count * 2)
- {
- const int elementsPerByte = sizeof(ushort) / sizeof(byte);
- int unaligned = ((int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1)) / elementsPerByte;
- nLength = (IntPtr)((Vector<ushort>.Count - unaligned) & (Vector<ushort>.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<ushort>.Count - 1));
-
- // Get comparison Vector
- Vector<ushort> vComparison = new Vector<ushort>(value);
-
- while ((byte*)nLength > (byte*)index)
- {
- var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<ushort>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref searchSpace, index))));
- if (Vector<ushort>.Zero.Equals(vMatches))
- {
- index += Vector<ushort>.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<T>(ref T searchSpace, T value0, T value1, int length)
where T : IEquatable<T>
{
@@ -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<ushort> 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<ulong>.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<char> strA, ReadOnlySpan<char> 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<nuint>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref first, i))) !=
- Unsafe.ReadUnaligned<nuint>(ref Unsafe.As<char, byte>(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;
};