Created
December 3, 2018 11:55
-
-
Save EgorBo/75348d5f194cbe91342d1b77d73b6c04 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
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