Skip to content

Instantly share code, notes, and snippets.

@EgorBo
Created December 3, 2018 11:55
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 EgorBo/75348d5f194cbe91342d1b77d73b6c04 to your computer and use it in GitHub Desktop.
Save EgorBo/75348d5f194cbe91342d1b77d73b6c04 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
/*// TEST:
var b = new Benchmarks();
foreach (var o in b.TestData())
{
b.Equals_((MyGuid)o[1], (MyGuid)o[1], "");
b.Equals_2((MyGuid)o[1], (MyGuid)o[1], "");
b.Equals_3((MyGuid)o[1], (MyGuid)o[1], "");
b.Equals_4((MyGuid)o[1], (MyGuid)o[1], "");
}*/
BenchmarkRunner.Run<Benchmarks>();
}
}
//[DisassemblyDiagnoser]
public class Benchmarks
{
[Benchmark(Baseline = true)]
[ArgumentsSource(nameof(TestData))]
// reference impl
public bool Equals_(MyGuid id1, MyGuid id2, string caseName) => id1.Equals(id2);
[Benchmark]
[ArgumentsSource(nameof(TestData))]
// compare two ulong
public bool Equals_2(MyGuid id1, MyGuid id2, string caseName) => id1.Equals2(id2);
[Benchmark]
[ArgumentsSource(nameof(TestData))]
// compare two int and one ulong
public bool Equals_3(MyGuid id1, MyGuid id2, string caseName) => id1.Equals3(id2);
[Benchmark]
[ArgumentsSource(nameof(TestData))]
// compare two ulong via value tuple
public bool Equals_4(MyGuid id1, MyGuid id2, string caseName) => id1.Equals4(id2);
public IEnumerable<object[]> TestData()
{
// case 1 - two guids are equal :
yield return new object[] { MyGuid.Parse("{28019FE4-BB5D-4309-8381-7132C895D5D3}"), MyGuid.Parse("{28019FE4-BB5D-4309-8381-7132C895D5D3}"), "equal1" };
yield return new object[] { MyGuid.Parse("{9512FC5A-942F-4A94-A713-D8CE13B22390}"), MyGuid.Parse("{9512FC5A-942F-4A94-A713-D8CE13B22390}"), "equal2" };
// case 2 - two guids are completely different:
yield return new object[] { MyGuid.Parse("{84D3BA92-A27A-4B7F-8332-D7C5E16DE435}"), MyGuid.Parse("{E11E8F1E-41C0-4067-97AE-65682DDF1ECB}"), "not equal1" };
yield return new object[] { MyGuid.Parse("{FC79C93D-07BE-412F-9486-25871E3F6A1E}"), MyGuid.Parse("{13259553-450D-4757-8700-E4B0D39EA4AE}"), "not equal2" };
// case 3 - SQL Server sequential guids (almost the same, 8EE.. vs 8FE..)
yield return new object[] { MyGuid.Parse("{8EED61E8-D3B9-E711-99CF-F0D5BFF9F348}"), MyGuid.Parse("{8FED61E8-D3B9-E711-99CF-F0D5BFF9F348}"), "sql seq guids" };
}
}
[StructLayout(LayoutKind.Sequential)]
[Serializable]
// simplified copy of Guid
public struct MyGuid : IEquatable<MyGuid>
{
private int _a;
private short _b;
private short _c;
private byte _d;
private byte _e;
private byte _f;
private byte _g;
private byte _h;
private byte _i;
private byte _j;
private byte _k;
public MyGuid(ReadOnlySpan<byte> b)
{
_a = b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0];
_b = (short)(b[5] << 8 | b[4]);
_c = (short)(b[7] << 8 | b[6]);
_d = b[8];
_e = b[9];
_f = b[10];
_g = b[11];
_h = b[12];
_i = b[13];
_j = b[14];
_k = b[15];
}
public static MyGuid Parse(string str) => new MyGuid(Guid.Parse(str).ToByteArray());
// reference implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(MyGuid g)
{
return g._a == _a &&
Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) &&
Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) &&
Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3);
}
// compare two ulong
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals2(MyGuid g)
{
return Unsafe.As<int, ulong>(ref g._a) == Unsafe.As<int, ulong>(ref _a) &&
Unsafe.Add(ref Unsafe.As<int, ulong>(ref g._a), 1) == Unsafe.Add(ref Unsafe.As<int, ulong>(ref _a), 1);
}
// compare two int and one ulong
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals3(MyGuid g)
{
return g._a == _a &&
Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) &&
Unsafe.Add(ref Unsafe.As<int, ulong>(ref g._a), 1) == Unsafe.Add(ref Unsafe.As<int, ulong>(ref _a), 1);
}
// compare two ulong via value tuples
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals4(MyGuid g)
{
return Unsafe.As<int, (ulong, ulong)>(ref _a) == Unsafe.As<int, (ulong, ulong)>(ref g._a);
}
/* there were implementations with SSE and pure unsafe (fixed) but the results were not really good */
public override string ToString() => string.Empty;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment