Use vectorized SpanHelpers.SequenceEqual for string equality (#16994)
authorJan Kotas <jkotas@microsoft.com>
Sat, 17 Mar 2018 14:16:35 +0000 (07:16 -0700)
committerGitHub <noreply@github.com>
Sat, 17 Mar 2018 14:16:35 +0000 (07:16 -0700)
src/mscorlib/src/System/String.Comparison.cs

index 6dfea0b..6f12a65 100644 (file)
@@ -80,61 +80,17 @@ namespace System
         // Search/Query methods
         //
 
-        private static unsafe bool EqualsHelper(String strA, String strB)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static bool EqualsHelper(String strA, String strB)
         {
             Debug.Assert(strA != null);
             Debug.Assert(strB != null);
             Debug.Assert(strA.Length == strB.Length);
 
-            int length = strA.Length;
-
-            fixed (char* ap = &strA._firstChar) fixed (char* bp = &strB._firstChar)
-            {
-                char* a = ap;
-                char* b = bp;
-
-#if BIT64
-                // Single int read aligns pointers for the following long reads
-                // PERF: No length check needed as there is always an int32 worth of string allocated
-                //       This read can also include the null terminator which both strings will have
-                if (*(int*)a != *(int*)b) return false;
-                length -= 2; a += 2; b += 2;
-
-                // for AMD64 bit platform we unroll by 12 and
-                // check 3 qword at a time. This is less code
-                // than the 32 bit case and is a shorter path length.
-
-                while (length >= 12)
-                {
-                    if (*(long*)a != *(long*)b) return false;
-                    if (*(long*)(a + 4) != *(long*)(b + 4)) return false;
-                    if (*(long*)(a + 8) != *(long*)(b + 8)) return false;
-                    length -= 12; a += 12; b += 12;
-                }
-#else
-                while (length >= 10)
-                {
-                    if (*(int*)a != *(int*)b) return false;
-                    if (*(int*)(a + 2) != *(int*)(b + 2)) return false;
-                    if (*(int*)(a + 4) != *(int*)(b + 4)) return false;
-                    if (*(int*)(a + 6) != *(int*)(b + 6)) return false;
-                    if (*(int*)(a + 8) != *(int*)(b + 8)) return false;
-                    length -= 10; a += 10; b += 10;
-                }
-#endif
-
-                // This depends on the fact that the String objects are
-                // always zero terminated and that the terminating zero is not included
-                // in the length. For odd string sizes, the last compare will include
-                // the zero terminator.
-                while (length > 0)
-                {
-                    if (*(int*)a != *(int*)b) return false;
-                    length -= 2; a += 2; b += 2;
-                }
-
-                return true;
-            }
+            return SpanHelpers.SequenceEqual(
+                    ref Unsafe.As<char, byte>(ref strA.GetRawStringData()),
+                    ref Unsafe.As<char, byte>(ref strB.GetRawStringData()),
+                    ((nuint)strA.Length) * 2);
         }
 
         private static unsafe bool EqualsIgnoreCaseAsciiHelper(String strA, String strB)
@@ -175,70 +131,6 @@ namespace System
             }
         }
 
-        private static unsafe bool StartsWithOrdinalHelper(String str, String startsWith)
-        {
-            Debug.Assert(str != null);
-            Debug.Assert(startsWith != null);
-            Debug.Assert(str.Length >= startsWith.Length);
-
-            int length = startsWith.Length;
-
-            fixed (char* ap = &str._firstChar)
-            fixed (char* bp = &startsWith._firstChar)
-            {
-                char* a = ap;
-                char* b = bp;
-
-#if BIT64
-                // Single int read aligns pointers for the following long reads
-                // No length check needed as this method is called when length >= 2
-                Debug.Assert(length >= 2);
-                if (*(int*)a != *(int*)b)
-                    return false;
-                length -= 2;
-                a += 2;
-                b += 2;
-
-                while (length >= 12)
-                {
-                    if (*(long*)a != *(long*)b)
-                        return false;
-                    if (*(long*)(a + 4) != *(long*)(b + 4))
-                        return false;
-                    if (*(long*)(a + 8) != *(long*)(b + 8))
-                        return false;
-                    length -= 12;
-                    a += 12;
-                    b += 12;
-                }
-#else
-                while (length >= 10)
-                {
-                    if (*(int*)a != *(int*)b) return false;
-                    if (*(int*)(a+2) != *(int*)(b+2)) return false;
-                    if (*(int*)(a+4) != *(int*)(b+4)) return false;
-                    if (*(int*)(a+6) != *(int*)(b+6)) return false;
-                    if (*(int*)(a+8) != *(int*)(b+8)) return false;
-                    length -= 10; a += 10; b += 10;
-                }
-#endif
-
-                while (length >= 2)
-                {
-                    if (*(int*)a != *(int*)b)
-                        return false;
-                    length -= 2;
-                    a += 2;
-                    b += 2;
-                }
-
-                // PERF: This depends on the fact that the String objects are always zero terminated 
-                // and that the terminating zero is not included in the length. For even string sizes
-                // this compare can include the zero terminator. Bitwise OR avoids a branch.
-                return length == 0 | *a == *b;
-            }
-        }
-
         private static unsafe int CompareOrdinalHelper(String strA, String strB)
         {
             Debug.Assert(strA != null);
@@ -1126,7 +1018,10 @@ namespace System
                     }
                     return (value.Length == 1) ?
                             true :                 // First char is the same and thats all there is to compare
-                            StartsWithOrdinalHelper(this, value);
+                            SpanHelpers.SequenceEqual(
+                                ref Unsafe.As<char, byte>(ref this.GetRawStringData()),
+                                ref Unsafe.As<char, byte>(ref value.GetRawStringData()),
+                                ((nuint)value.Length) * 2);
 
                 case StringComparison.OrdinalIgnoreCase:
                     if (this.Length < value.Length)