Skip to content

Instantly share code, notes, and snippets.

@aelij
Created April 3, 2017 17:50
Show Gist options
  • Save aelij/b62d149e1a85c3edfad7598e9c2f12cb to your computer and use it in GitHub Desktop.
Save aelij/b62d149e1a85c3edfad7598e9c2f12cb to your computer and use it in GitHub Desktop.
// NuGet: BenchmarkDotNet
// NuGet: System.Runtime.CompilerServices.Unsafe
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace ArrayComparePerf
{
public class ArrayComparePerfProgram
{
private const int Size = 1026;
private static readonly byte[] a1;
private static readonly byte[] a2;
static ArrayComparePerfProgram()
{
a1 = new byte[Size];
a2 = new byte[Size];
a2[Size - 1] = 1;
}
private static void Main()
{
BenchmarkRunner.Run<ArrayComparePerfProgram>();
}
[Benchmark]
public bool UnsafeLibrary()
{
if (a1.Length != a2.Length) return false;
var longSize = (int)Math.Floor(a1.Length / 8.0);
var long1 = Unsafe.As<long[]>(a1);
var long2 = Unsafe.As<long[]>(a2);
for (var i = 0; i < longSize; i++)
{
if (long1[i] != long2[i]) return false;
}
for (var i = longSize * 8; i < a1.Length; i++)
{
if (a1[i] != a2[i]) return false;
}
return true;
}
// Copyright (c) 2008-2013 Hafthor Stefansson
// Distributed under the MIT/X11 software license
// Ref: http://www.opensource.org/licenses/mit-license.php.
[Benchmark]
public unsafe bool UnsafeCompare()
{
if (a1 == null || a2 == null || a1.Length != a2.Length)
return false;
fixed (byte* p1 = a1, p2 = a2)
{
byte* x1 = p1, x2 = p2;
int l = a1.Length;
for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8)
if (*((long*)x1) != *((long*)x2)) return false;
if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; }
if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; }
if ((l & 1) != 0) if (*x1 != *x2) return false;
return true;
}
}
[Benchmark]
public bool JSharpEquals()
{
if (a1 == a2)
{
return true;
}
if (a1 != null && a2 != null)
{
if (a1.Length != a2.Length)
{
return false;
}
for (int i = 0; i < a1.Length; i++)
{
if (a1[i] != a2[i])
{
return false;
}
}
return true;
}
return false;
}
[Benchmark]
public unsafe bool EqualBytesLongUnrolled()
{
if (a1 == a2)
return true;
if (a1.Length != a2.Length)
return false;
fixed (byte* bytes1 = a1, bytes2 = a2)
{
int len = a1.Length;
int rem = len % (sizeof(long) * 16);
long* b1 = (long*)bytes1;
long* b2 = (long*)bytes2;
long* e1 = (long*)(bytes1 + len - rem);
while (b1 < e1)
{
if (*(b1) != *(b2) || *(b1 + 1) != *(b2 + 1) ||
*(b1 + 2) != *(b2 + 2) || *(b1 + 3) != *(b2 + 3) ||
*(b1 + 4) != *(b2 + 4) || *(b1 + 5) != *(b2 + 5) ||
*(b1 + 6) != *(b2 + 6) || *(b1 + 7) != *(b2 + 7) ||
*(b1 + 8) != *(b2 + 8) || *(b1 + 9) != *(b2 + 9) ||
*(b1 + 10) != *(b2 + 10) || *(b1 + 11) != *(b2 + 11) ||
*(b1 + 12) != *(b2 + 12) || *(b1 + 13) != *(b2 + 13) ||
*(b1 + 14) != *(b2 + 14) || *(b1 + 15) != *(b2 + 15))
return false;
b1 += 16;
b2 += 16;
}
for (int i = 0; i < rem; i++)
if (a1[len - 1 - i] != a2[len - 1 - i])
return false;
return true;
}
}
private static unsafe bool NewMemCmp(byte* b0, byte* b1, int length)
{
byte* lastAddr = b0 + length;
byte* lastAddrMinus32 = lastAddr - 32;
while (b0 < lastAddrMinus32) // unroll the loop so that we are comparing 32 bytes at a time.
{
if (*(ulong*)b0 != *(ulong*)b1) return false;
if (*(ulong*)(b0 + 8) != *(ulong*)(b1 + 8)) return false;
if (*(ulong*)(b0 + 16) != *(ulong*)(b1 + 16)) return false;
if (*(ulong*)(b0 + 24) != *(ulong*)(b1 + 24)) return false;
b0 += 32;
b1 += 32;
}
while (b0 < lastAddr)
{
if (*b0 != *b1) return false;
b0++;
b1++;
}
return true;
}
[Benchmark]
public unsafe bool NewMemCmp()
{
fixed (byte* b0 = a1) fixed (byte* b1 = a2)
{
return NewMemCmp(b0, b1, Size);
}
}
[Benchmark]
public bool ArraysEqual()
{
if (a1.Length != a2.Length) return false;
var length = a1.Length;
var tailIdx = length - length % sizeof(Int64);
//check in 8 byte chunks
for (var i = 0; i < tailIdx; i += sizeof(Int64))
{
if (BitConverter.ToInt64(a1, i) != BitConverter.ToInt64(a2, i)) return false;
}
//check the remainder of the array, always shorter than 8 bytes
for (var i = tailIdx; i < length; i++)
{
if (a1[i] != a2[i]) return false;
}
return true;
}
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(byte[] b1, byte[] b2, long count);
[Benchmark]
public bool PInvokeMemcmp()
{
// Validate buffers are the same length.
// This also ensures that the count does not exceed the length of either buffer.
return a1.Length == a2.Length && memcmp(a1, a2, a1.Length) == 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment