Created
March 27, 2016 07:46
-
-
Save bbowyersmyth/da8a3b6d6a65cbb47902 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using BenchmarkDotNet.Attributes; | |
using System; | |
using System.Diagnostics; | |
namespace ConsoleApplication2 | |
{ | |
[Config("jobs=RyuJitX64")] | |
public unsafe class Equals | |
{ | |
// Equal | |
[Params(5, 12, 26, 100, 1000)] | |
public int length = 0; | |
// Diff at index n | |
//[Params(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)] | |
//public int offset = 0; | |
//public int length = 15; | |
string test1; | |
string test2; | |
[Setup] | |
public void Setup() | |
{ | |
// Equal | |
test1 = new string('a', length); | |
test2 = new string('a', length); | |
// Diff at index n | |
//test1 = new string('a', length); | |
//var chars = test1.ToCharArray(); | |
//chars[offset] = 'X'; | |
//test2 = new string(chars); | |
} | |
[Benchmark] | |
public bool EqualsRT() | |
{ | |
return EqualsRT(test1, test2); | |
} | |
[Benchmark] | |
public bool EqualsNew() | |
{ | |
return EqualsNew(test1, test2); | |
} | |
public static bool EqualsRT(String a, String b) | |
{ | |
if ((Object)a == (Object)b) | |
{ | |
return true; | |
} | |
if ((Object)a == null || (Object)b == null) | |
{ | |
return false; | |
} | |
if (a.Length != b.Length) | |
return false; | |
return OrdinalCompareEqualLengthStrings(a, b); | |
} | |
public bool EqualsNew(String a, String b) | |
{ | |
if ((Object)a == (Object)b) | |
{ | |
return true; | |
} | |
if ((Object)a == null || (Object)b == null || a.Length != b.Length) | |
{ | |
return false; | |
} | |
return OrdinalCompareEqualLengthStringsNew(a, b); | |
} | |
private unsafe static bool OrdinalCompareEqualLengthStrings(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) fixed (char* bp = strB) | |
{ | |
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 64-bit platforms 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) break; | |
length -= 2; a += 2; b += 2; | |
} | |
return (length <= 0); | |
} | |
} | |
private unsafe static bool OrdinalCompareEqualLengthStringsNew(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) fixed (char* bp = strB) | |
{ | |
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 64-bit platforms 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) goto ReturnFalse; | |
if (*(long*)(a + 4) != *(long*)(b + 4)) goto ReturnFalse; | |
if (*(long*)(a + 8) != *(long*)(b + 8)) goto ReturnFalse; | |
length -= 12; a += 12; b += 12; | |
} | |
#else | |
while (length >= 10) | |
{ | |
if (*(int*)a != *(int*)b) goto ReturnFalse; | |
if (*(int*)(a + 2) != *(int*)(b + 2)) goto ReturnFalse; | |
if (*(int*)(a + 4) != *(int*)(b + 4)) goto ReturnFalse; | |
if (*(int*)(a + 6) != *(int*)(b + 6)) goto ReturnFalse; | |
if (*(int*)(a + 8) != *(int*)(b + 8)) goto ReturnFalse; | |
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) goto ReturnFalse; | |
length -= 2; a += 2; b += 2; | |
} | |
return true; | |
ReturnFalse: | |
return false; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment