Skip to content

Instantly share code, notes, and snippets.

@bbowyersmyth
Created March 27, 2016 07:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbowyersmyth/da8a3b6d6a65cbb47902 to your computer and use it in GitHub Desktop.
Save bbowyersmyth/da8a3b6d6a65cbb47902 to your computer and use it in GitHub Desktop.
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