Skip to content

Instantly share code, notes, and snippets.

@itn3000
Last active August 29, 2017 09:10
Show Gist options
  • Select an option

  • Save itn3000/be15fc7aed1e53c76da4a519cbc1a0fd to your computer and use it in GitHub Desktop.

Select an option

Save itn3000/be15fc7aed1e53c76da4a519cbc1a0fd to your computer and use it in GitHub Desktop.
byte array comparer(inspired by Messagepack-CSharp and System.Span<T>)
using System;
static class ByteComparer
{
public static bool AreSameSpan(byte[] b1, byte[] b2)
{
var span = new Span<byte>(b1);
var span2 = new Span<byte>(b2);
return span.SequenceEqual(span2);
}
// similar to MessagePack-CSharp's implementation
public static unsafe bool AreSameBytePtr(byte[] xs, int xsOffset, int xsCount, byte[] ys, int ysOffset, int ysCount)
{
if (xs == null || ys == null || xsCount != ysCount)
{
return false;
}
fixed (byte* p1 = xs)
fixed (byte* p2 = ys)
{
var x1 = p1 + xsOffset;
var x2 = p2 + ysOffset;
var length = xsCount;
var loooCount = length / 8;
for (var i = 0; i < loooCount; i++, x1 += 8, x2 += 8)
{
if (*(long*)x1 != *(long*)x2)
{
return false;
}
}
if ((length & 4) != 0)
{
if (*(int*)x1 != *(int*)x2)
{
return false;
}
x1 += 4;
x2 += 4;
}
if ((length & 2) != 0)
{
if (*(short*)x1 != *(short*)x2)
{
return false;
}
x1 += 2;
x2 += 2;
}
if ((length & 1) != 0)
{
if (*x1 != *x2)
{
return false;
}
}
return true;
}
}
// similar to System.Span<byte>'s implementation
public static unsafe bool AreSameByteWithOffset(byte[] xs, int xsOffset, int xsCount, byte[] ys, int ysOffset, int ysCount)
{
if (xs == null || ys == null || xsCount != ysCount)
{
return false;
}
fixed (byte* p1 = xs)
fixed (byte* p2 = ys)
{
// when length >= System.Numerics.Vector<byte>.Count, comparison will be faster if using System.Numerics.Vector<byte>
var length = xsCount;
// It is most efficient
if (length >= 8)
{
var x1 = p1 + xsOffset;
var x2 = p2 + ysOffset;
// calculate the end loop offset.
var endxptr = x1 + xsCount - 8;
var endyptr = x2 + xsCount - 8;
// compare bytes per sizeof(long)
while (x1 < endxptr)
{
if (*(long*)x1 != *(long*)x2)
{
return false;
}
x1 += 8;
x2 += 8;
}
// compare last sizeof(long) bytes.
// the most loss of duplicated comparison when xsCount % sizeof(long) == 1
return *(long*)endxptr == *(long*)endyptr;
}
else if (length >= 4)
{
var x1 = p1 + xsOffset;
var x2 = p2 + ysOffset;
// calculate the end loop offset.
var endxptr = x1 + xsCount - 4;
var endyptr = x2 + xsCount - 4;
// compare bytes per sizeof(int)
while (x1 < endxptr)
{
if (*(int*)x1 != *(int*)x2)
{
return false;
}
x1 += 4;
x2 += 4;
}
// compare last sizeof(UIntPtr) bytes.
// the most loss of duplicated comparison when xsCount % sizeof(int) == sizeof(int) - 1
return *(int*)endxptr == *(int*)endyptr;
}
else
{
var x1 = p1 + xsOffset;
var x2 = p2 + ysOffset;
var endptr = x1 + xsCount;
for (; x1 < endptr; x1++, x2++)
{
if (*x1 != *x2)
{
return false;
}
}
return true;
}
}
}
}
using System;
namespace comparebytetest
{
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Running;
[ShortRunJob]
public class CompareByteBench
{
[Params(1000)]
public int LoopNum { get; set; }
[Params(1, 5, 10, 20)]
public int DataLength { get; set; }
[Benchmark]
public void CompareVector()
{
var data = new byte[DataLength];
var data2 = new byte[DataLength];
for (int i = 0; i < LoopNum; i++)
{
if (!ByteComparer.AreSameByteWithOffset(data, 0, data.Length, data2, 0, data2.Length))
{
throw new InvalidOperationException();
}
}
}
[Benchmark]
public void CompareAsPtr()
{
var data = new byte[DataLength];
var data2 = new byte[DataLength];
for (int i = 0; i < LoopNum; i++)
{
if (!ByteComparer.AreSameBytePtr(data, 0, data.Length, data2, 0, data2.Length))
{
throw new InvalidOperationException();
}
}
}
}
class Program
{
static void Main(string[] args)
{
var reporter = BenchmarkRunner.Run<CompareByteBench>();
}
}
}
BenchmarkDotNet=v0.10.9, OS=Windows 8.1 (6.3.9600)
Processor=Intel Core i7-4770 CPU 3.40GHz (Haswell), ProcessorCount=8
Frequency=3312628 Hz, Resolution=301.8751 ns, Timer=TSC
.NET Core SDK=2.0.0
  [Host]   : .NET Core 2.0.0 (Framework 4.6.00001.0), 64bit RyuJIT
  ShortRun : .NET Core 2.0.0 (Framework 4.6.00001.0), 64bit RyuJIT

Job=ShortRun  LaunchCount=1  TargetCount=3  
WarmupCount=3  
Method LoopNum DataLength Mean Error StdDev
CompareVector 1000 1 5.107 us 0.6074 us 0.0343 us
CompareAsPtr 1000 1 5.347 us 0.7132 us 0.0403 us
CompareVector 1000 5 5.403 us 0.7034 us 0.0397 us
CompareAsPtr 1000 5 5.746 us 1.3781 us 0.0779 us
CompareVector 1000 10 5.235 us 1.3322 us 0.0753 us
CompareAsPtr 1000 10 5.704 us 0.9331 us 0.0527 us
CompareVector 1000 20 6.602 us 0.7716 us 0.0436 us
CompareAsPtr 1000 20 7.443 us 2.2654 us 0.1280 us
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment