-
-
Save aelij/b62d149e1a85c3edfad7598e9c2f12cb to your computer and use it in GitHub Desktop.
This file contains 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
// 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