Skip to content

Instantly share code, notes, and snippets.

@khellang
Forked from JimBobSquarePants/Xor.cs
Last active November 8, 2018 15:49
Show Gist options
  • Save khellang/04292b0d3be5e94878043e0db0e8f411 to your computer and use it in GitHub Desktop.
Save khellang/04292b0d3be5e94878043e0db0e8f411 to your computer and use it in GitHub Desktop.
Xor combination of two Guids.
public static Guid Xor(Guid a, Guid b)
{
var ad = new DecomposedGuid(a);
var bd = new DecomposedGuid(b);
ad.Hi ^= bd.Hi;
ad.Lo ^= bd.Lo;
return ad.Value;
}
[StructLayout(LayoutKind.Explicit)]
private struct DecomposedGuid
{
[FieldOffset(00)] public Guid Value;
[FieldOffset(00)] public long Hi;
[FieldOffset(08)] public long Lo;
public DecomposedGuid(Guid value) : this() => Value = value;
}
@Tornhoof
Copy link

Tornhoof commented Nov 8, 2018

Assuming I didn't mess up too much:

BenchmarkDotNet=v0.11.2, OS=Windows 10.0.17134.376 (1803/April2018Update/Redstone4)
Intel Xeon CPU E5-1620 0 3.60GHz, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.100-preview3-009430
  [Host]     : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
  DefaultJob : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT

Method Mean Error StdDev
XorPointer 16.84 ns 0.0282 ns 0.0220 ns
XorUnsafe 14.38 ns 0.1292 ns 0.1146 ns
XorSafe 10.71 ns 0.0410 ns 0.0342 ns
    [DisassemblyDiagnoser()]
    public class GuidXorBenchmark
    {
        private static readonly Guid A = Guid.NewGuid();
        private static readonly Guid B = Guid.NewGuid();

        [Benchmark]
        public Guid XorPointer()
        {
            Guid a = A;
            Guid b = B;
            unsafe
            {
                Int64* ap = (Int64*)&a;
                Int64* bp = (Int64*)&b;
                ap[0] ^= bp[0];
                ap[1] ^= bp[1];

                return *(Guid*)ap;
            }
        }

        [Benchmark]
        public Guid XorUnsafe()
        {
            Guid a = A;
            Guid b = B;
            Unsafe.As<Guid, UInt64>(ref a) = Unsafe.As<Guid, UInt64>(ref a) ^ Unsafe.As<Guid, UInt64>(ref b);
            Unsafe.Add(ref Unsafe.As<Guid, UInt64>(ref a), 1) =
                Unsafe.Add(ref Unsafe.As<Guid, UInt64>(ref a), 1) ^ Unsafe.Add(ref Unsafe.As<Guid, UInt64>(ref b), 1);
            return a;
        }

        [Benchmark]
        public Guid XorSafe()
        {
            DecomposedGuid ad = default;
            DecomposedGuid bd = default;

            ad.Guid = A;
            bd.Guid = B;

            ad.Hi ^= bd.Hi;
            ad.Lo ^= bd.Lo;

            return ad.Guid;
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct DecomposedGuid
        {
            [FieldOffset(00)] public Guid Guid;
            [FieldOffset(00)] public long Hi;
            [FieldOffset(08)] public long Lo;
        }
    }

@Tornhoof
Copy link

Tornhoof commented Nov 8, 2018

Hmm, the disassembly for XorPointer and XorSafe pretty much looks the same, difference might be alignment problem?

.NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT

; ConsoleApp23.GuidXorBenchmark.XorPointer()
       mov     rcx,7FFA1DB83518h
       mov     edx,2
       call    coreclr!MetaDataGetDispenser+0x37fa0
       mov     rax,27AED0D7220h
       mov     rax,qword ptr [rax]
       add     rax,8
       vmovdqu xmm0,xmmword ptr [rax]
       vmovdqu xmmword ptr [rsp+38h],xmm0
       mov     rax,27AED0D7228h
       mov     rax,qword ptr [rax]
       add     rax,8
       vmovdqu xmm0,xmmword ptr [rax]
       vmovdqu xmmword ptr [rsp+28h],xmm0
       lea     rax,[rsp+38h]
       lea     rdx,[rsp+28h]
       mov     rcx,qword ptr [rdx]
       xor     qword ptr [rax],rcx
       lea     rcx,[rax+8]
       mov     rdx,qword ptr [rdx+8]
       xor     qword ptr [rcx],rdx
       vmovdqu xmm0,xmmword ptr [rax]
       vmovdqu xmmword ptr [rsi],xmm0
       mov     rax,rsi
; Total bytes of code 118

.NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT

; ConsoleApp23.GuidXorBenchmark.XorUnsafe()
       mov     rcx,7FFA1DB93518h
       mov     edx,2
       call    coreclr!MetaDataGetDispenser+0x37fa0
       mov     rcx,28210007220h
       mov     rcx,qword ptr [rcx]
       add     rcx,8
       vmovdqu xmm0,xmmword ptr [rcx]
       vmovdqu xmmword ptr [rsp+40h],xmm0
       mov     rcx,28210007228h
       mov     rcx,qword ptr [rcx]
       add     rcx,8
       vmovdqu xmm0,xmmword ptr [rcx]
       vmovdqu xmmword ptr [rsp+30h],xmm0
       lea     rcx,[rsp+40h]
       mov     rdx,qword ptr [rsp+40h]
       xor     rdx,qword ptr [rsp+30h]
       mov     qword ptr [rcx],rdx
       lea     rcx,[rsp+40h]
       mov     edx,1
       call    System.Runtime.CompilerServices.Unsafe.Add[[System.UInt64, System.Private.CoreLib]](UInt64 ByRef, Int32)
       mov     rdi,rax
       lea     rcx,[rsp+40h]
       mov     edx,1
       call    System.Runtime.CompilerServices.Unsafe.Add[[System.UInt64, System.Private.CoreLib]](UInt64 ByRef, Int32)
       mov     rbx,qword ptr [rax]
       lea     rcx,[rsp+30h]
       mov     edx,1
       call    System.Runtime.CompilerServices.Unsafe.Add[[System.UInt64, System.Private.CoreLib]](UInt64 ByRef, Int32)
       xor     rbx,qword ptr [rax]
       mov     qword ptr [rdi],rbx
       vmovdqu xmm0,xmmword ptr [rsp+40h]
       vmovdqu xmmword ptr [rsi],xmm0
       mov     rax,rsi
; Total bytes of code 168
; System.Runtime.CompilerServices.Unsafe.Add[[System.UInt64, System.Private.CoreLib]](UInt64 ByRef, Int32)
       movsxd  rax,edx
       lea     rax,[rcx+rax*8]
; Total bytes of code 7

No ILOffsetMap found
System.Runtime.CompilerServices.Unsafe.As(!!0 ByRef)

.NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT

; ConsoleApp23.GuidXorBenchmark.XorSafe()
       lea     rcx,[rsp+38h]
       vxorps  xmm0,xmm0,xmm0
       vmovdqu xmmword ptr [rcx],xmm0
       mov     rcx,7FFA1DB83518h
       mov     edx,2
       call    coreclr!MetaDataGetDispenser+0x37fa0
       mov     rax,2CB90007220h
       mov     rax,qword ptr [rax]
       add     rax,8
       vmovdqu xmm0,xmmword ptr [rax]
       vmovdqu xmmword ptr [rsp+38h],xmm0
       mov     rax,2CB90007228h
       mov     rax,qword ptr [rax]
       add     rax,8
       vmovdqu xmm0,xmmword ptr [rax]
       vmovdqu xmmword ptr [rsp+28h],xmm0
       lea     rax,[rsp+38h]
       mov     rdx,qword ptr [rsp+28h]
       xor     qword ptr [rax],rdx
       lea     rax,[rsp+40h]
       mov     rdx,qword ptr [rsp+30h]
       xor     qword ptr [rax],rdx
       vmovdqu xmm0,xmmword ptr [rsp+38h]
       vmovdqu xmmword ptr [rsi],xmm0
       mov     rax,rsi
; Total bytes of code 134

@Tornhoof
Copy link

Tornhoof commented Nov 8, 2018

Ok, the differences appear to be system related, something else is running on my system, after reboot it looks like:

BenchmarkDotNet=v0.11.2, OS=Windows 10.0.17134.376 (1803/April2018Update/Redstone4)
Intel Xeon CPU E5-1620 0 3.60GHz, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.100-preview3-009430
  [Host]     : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
  DefaultJob : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT

Method Mean Error StdDev
XorPointer 10.47 ns 0.3285 ns 0.3226 ns
XorUnsafe 14.37 ns 0.1059 ns 0.0939 ns
XorSafe 11.21 ns 0.1026 ns 0.0801 ns

@khellang
Copy link
Author

khellang commented Nov 8, 2018

Yeah, that's what I expected 😄👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment