# Deep dive into .NET 11 Performance Improvements #6

In this series, I explore some of the most interesting .NET 11 performance improvements described in Stephen Toub's blog post, [Performance Improvements in .NET 11](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-11).

Each post focuses on one example from the original article while digging a little deeper into an additional aspect that was not covered there. The series covers the following topics:

1. [Allocation free numbers](https://blog.ladeak.net/posts/net11-perf-imp-01.md)
1. [Virtual generic method calls](https://blog.ladeak.net/posts/net11-perf-imp-02.md)
1. [Vector zero equality](https://blog.ladeak.net/posts/net11-perf-imp-03.md)
1. [Guid creation on Linux](https://blog.ladeak.net/posts/net11-perf-imp-04.md)
1. [JIT generating implicit-else assembly instructions](https://blog.ladeak.net/posts/net11-perf-imp-05.md)
1. *Current post: [Guid equality](https://blog.ladeak.net/posts/net11-perf-imp-06.md)*
1. Copy-on-write locking strategy

## Guid equality

One .NET 11 improvement optimizes `SequenceEqual` on spans of bitwise comparable types such as `Guid`, allowing the runtime to compare their data as bytes. A `Guid` is 128 bits wide, so a single value can also be compared using a 128 bit vector operation. The [original post](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-11/#searching-and-comparing) discusses the span based optimization; here, I wanted to see whether comparing two individual `Guid` values also uses SIMD.

Let's test it with the following code:

```csharp
private readonly Guid _guid1 = default;
private readonly Guid _guid2 = default;
public bool GuidEqual() => _guid1 == _guid2;
public bool GuidEqualEmpty() => _guid1 == Guid.Empty;
```

The `GuidEqual` method is JIT compiled into the following x64 assembly code:

```assembly
; Benchmarks.GuidEqual()
       vmovups   xmm0,[rcx+8]
       vpcmpeqb  xmm0,xmm0,[rcx+18]
       vpmovmskb eax,xmm0
       cmp       eax,0FFFF
       sete      al
       movzx     eax,al
       ret
; Total bytes of code 26
```

The `GuidEqualEmpty` method is JIT compiled into the following x64 assembly:

```assembly
; Benchmarks.GuidEqualEmpty()
       vmovups   xmm0,[rcx+8]
       vptest    xmm0,xmm0
       sete      al
       movzx     eax,al
       ret
; Total bytes of code 17
```

Both methods use 128 bit vector instructions on my AVX2 capable machine. `GuidEqual` compares the two vectors with `vpcmpeqb`, then uses `vpmovmskb` to pack the comparison results into a scalar mask. It compares that mask with `0xFFFF` to check whether all bytes matched. `GuidEqualEmpty` uses `vptest` to check whether the vector contains only zeroes; `sete` then writes the zero flag to the boolean return value.

The BCL implementation of `Guid` equality also uses `Vector128` when hardware acceleration is available, as shown in the [source code](https://source.dot.net/#System.Private.CoreLib/src/runtime/src/libraries/System.Private.CoreLib/src/System/Guid.cs,75f4a722ae8be359):

```csharp
private static bool EqualsCore(in Guid left, in Guid right)
{
    if (Vector128.IsHardwareAccelerated)
    {
        return Unsafe.BitCast<Guid, Vector128<byte>>(left) == Unsafe.BitCast<Guid, Vector128<byte>>(right);
    }
    //...
```

## Conclusion

This post focused on the 6th of seven nuances I learned from the [Performance Improvements in .NET 11](https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-11) article. The post finds that the .NET JIT uses SIMD instructions for both `Guid` equality and comparison with `Guid.Empty` on the tested AVX2 machine, with the empty comparison compiling to shorter code.