Span<T> offers an extremely competitive alternative without having to throw confusing and/or non-portable fluff into your own application's code base:
// byte[] is implicitly convertible to ReadOnlySpan<byte>
static bool ByteArrayCompare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)
{
return a1.SequenceEqual(a2);
}
The (guts of the) implementation as of .NET 5.0.0 can be found here.
I've revised @EliArbel's gist to add this method as SpansEqual, drop most of the less interesting performers in others' benchmarks, run it with different array sizes, output graphs, and mark SpansEqual as the baseline so that it reports how the different methods compare to SpansEqual.
The below numbers are from the results, lightly edited to remove "Error" column.
| Method | ByteCount | Mean | StdDev | Ratio | RatioSD |
|-------------- |----------- |-------------------:|------------------:|------:|--------:|
| SpansEqual | 15 | 4.629 ns | 0.0289 ns | 1.00 | 0.00 |
| LongPointers | 15 | 4.598 ns | 0.0416 ns | 0.99 | 0.01 |
| Unrolled | 15 | 18.199 ns | 0.0291 ns | 3.93 | 0.02 |
| PInvokeMemcmp | 15 | 9.872 ns | 0.0441 ns | 2.13 | 0.02 |
| | | | | | |
| SpansEqual | 1026 | 19.965 ns | 0.0880 ns | 1.00 | 0.00 |
| LongPointers | 1026 | 63.005 ns | 0.5217 ns | 3.16 | 0.04 |
| Unrolled | 1026 | 38.731 ns | 0.0166 ns | 1.94 | 0.01 |
| PInvokeMemcmp | 1026 | 40.355 ns | 0.0202 ns | 2.02 | 0.01 |
| | | | | | |
| SpansEqual | 1048585 | 43,761.339 ns | 30.8744 ns | 1.00 | 0.00 |
| LongPointers | 1048585 | 59,585.479 ns | 17.3907 ns | 1.36 | 0.00 |
| Unrolled | 1048585 | 54,646.243 ns | 35.7638 ns | 1.25 | 0.00 |
| PInvokeMemcmp | 1048585 | 55,198.289 ns | 23.9732 ns | 1.26 | 0.00 |
| | | | | | |
| SpansEqual | 2147483591 | 240,607,692.857 ns | 2,733,489.4894 ns | 1.00 | 0.00 |
| LongPointers | 2147483591 | 238,223,478.571 ns | 2,033,769.5979 ns | 0.99 | 0.02 |
| Unrolled | 2147483591 | 236,227,340.000 ns | 2,189,627.0164 ns | 0.98 | 0.00 |
| PInvokeMemcmp | 2147483591 | 238,724,660.000 ns | 3,726,140.4720 ns | 0.99 | 0.02 |
I was surprised to see SpansEqual not come out on top for the max-array-size methods, but the difference is so minor that I don't think it'll ever matter.
My system info:
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
Intel Core i7-6850K CPU 3.60GHz (Skylake), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=5.0.100
[Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT
DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT