Сравнение двух байтовых массивов in.NET


Как я могу сделать это быстро?

конечно, я могу это сделать:

static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
    if (a1.Length != a2.Length)
        return false;

    for (int i=0; i<a1.Length; i++)
        if (a1[i]!=a2[i])
            return false;

    return true;
}

но я ищу либо a BCL функция или какой-то высоко оптимизированный проверенный способ сделать это.

java.util.Arrays.equals((sbyte[])(Array)a1, (sbyte[])(Array)a2);

работает хорошо, но не похоже, что это будет работать для x64.

обратите внимание на мой супер-быстрый ответ здесь.

28 434

28 ответов:

можно использовать перечисли.SequenceEqual метод.

using System;
using System.Linq;
...
var a1 = new int[] { 1, 2, 3};
var a2 = new int[] { 1, 2, 3};
var a3 = new int[] { 1, 2, 4};
var x = a1.SequenceEqual(a2); // true
var y = a1.SequenceEqual(a3); // false

Если вы не можете использовать .NET 3.5 по какой-то причине, ваш метод в порядке.
Компилятор\среда выполнения оптимизирует цикл, поэтому вам не нужно беспокоиться о производительности.

P / Invoke полномочия активировать!

[DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int memcmp(byte[] b1, byte[] b2, long count);

static bool ByteArrayCompare(byte[] b1, byte[] b2)
{
    // Validate buffers are the same length.
    // This also ensures that the count does not exceed the length of either buffer.  
    return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0;
}

есть новое встроенное решение для этого в .NET 4 -IStructuralEquatable

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
    return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2);
}

пользователей Гиль предложил небезопасный код, который породил такое решение:

// Copyright (c) 2008-2013 Hafthor Stefansson
// Distributed under the MIT/X11 software license
// Ref: http://www.opensource.org/licenses/mit-license.php.
static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) {
  if(a1==a2) return true;
  if(a1==null || a2==null || a1.Length!=a2.Length)
    return false;
  fixed (byte* p1=a1, p2=a2) {
    byte* x1=p1, x2=p2;
    int l = a1.Length;
    for (int i=0; i < l/8; i++, x1+=8, x2+=8)
      if (*((long*)x1) != *((long*)x2)) return false;
    if ((l & 4)!=0) { if (*((int*)x1)!=*((int*)x2)) return false; x1+=4; x2+=4; }
    if ((l & 2)!=0) { if (*((short*)x1)!=*((short*)x2)) return false; x1+=2; x2+=2; }
    if ((l & 1)!=0) if (*((byte*)x1) != *((byte*)x2)) return false;
    return true;
  }
}

который делает 64-битное сравнение для как можно большей части массива. Этот вид рассчитывает на то, что массивы начинают выравниваться qword. Это будет работать, если не qword выровнены, просто не так быстро, как если бы это было.

он выполняет около семи таймеров быстрее, чем простой for петли. Использование библиотеки J# выполняется эквивалентно оригиналу for петли. С помощью.SequenceEqual работает примерно в семь раз медленнее; я думаю, только потому, что он использует IEnumerator.метод MoveNext. Я предполагаю, что решения на основе LINQ, по крайней мере, такие медленные или хуже.

Если вы не против этого, вы можете импортировать сборку J# " vjslib.DLL" и использовать его массивы.равно (байт[], байт[]) метод...

Не вините Меня, если кто-то смеется, хотя вы...


EDIT: для того, что мало стоит, я использовал Reflector, чтобы разобрать код для этого, и вот как это выглядит:

public static bool equals(sbyte[] a1, sbyte[] a2)
{
  if (a1 == a2)
  {
    return true;
  }
  if ((a1 != null) && (a2 != null))
  {
    if (a1.Length != a2.Length)
    {
      return false;
    }
    for (int i = 0; i < a1.Length; i++)
    {
      if (a1[i] != a2[i])
      {
        return false;
      }
    }
    return true;
  }
  return false;
}

.NET 3.5 и новее имеют новый публичный тип,System.Data.Linq.Binary, который инкапсулирует byte[]. Он реализует IEquatable<Binary> что (по сути) сравнивает два байтовых массива. Обратите внимание, что System.Data.Linq.Binary также имеет неявный оператор преобразования от byte[].

Примечание:

Я написал аналогичный вопрос о проверке, если байт[] полон нулей. (Код SIMD был избит, поэтому я удалил его из этого ответа.) Вот самый быстрый код из моих сравнений:

static unsafe bool EqualBytesLongUnrolled (byte[] data1, byte[] data2)
{
    if (data1 == data2)
        return true;
    if (data1.Length != data2.Length)
        return false;

    fixed (byte* bytes1 = data1, bytes2 = data2) {
        int len = data1.Length;
        int rem = len % (sizeof(long) * 16);
        long* b1 = (long*)bytes1;
        long* b2 = (long*)bytes2;
        long* e1 = (long*)(bytes1 + len - rem);

        while (b1 < e1) {
            if (*(b1) != *(b2) || *(b1 + 1) != *(b2 + 1) || 
                *(b1 + 2) != *(b2 + 2) || *(b1 + 3) != *(b2 + 3) ||
                *(b1 + 4) != *(b2 + 4) || *(b1 + 5) != *(b2 + 5) || 
                *(b1 + 6) != *(b2 + 6) || *(b1 + 7) != *(b2 + 7) ||
                *(b1 + 8) != *(b2 + 8) || *(b1 + 9) != *(b2 + 9) || 
                *(b1 + 10) != *(b2 + 10) || *(b1 + 11) != *(b2 + 11) ||
                *(b1 + 12) != *(b2 + 12) || *(b1 + 13) != *(b2 + 13) || 
                *(b1 + 14) != *(b2 + 14) || *(b1 + 15) != *(b2 + 15))
                return false;
            b1 += 16;
            b2 += 16;
        }

        for (int i = 0; i < rem; i++)
            if (data1 [len - 1 - i] != data2 [len - 1 - i])
                return false;

        return true;
    }
}

измеряется на двух массивах 256 Мбайт:

UnsafeCompare                           : 86,8784 ms
EqualBytesSimd                          : 71,5125 ms
EqualBytesSimdUnrolled                  : 73,1917 ms
EqualBytesLongUnrolled                  : 39,8623 ms

Span<T> предлагает чрезвычайно конкурентоспособную альтернативу без необходимости бросать запутанный и / или непортативный пух в кодовую базу вашего собственного приложения:

// byte[] is implicitly convertible to ReadOnlySpan<byte>
static bool ByteArrayCompare(ReadOnlySpan<byte> a1, ReadOnlySpan<byte> a2)
{
    return a1.SequenceEqual(a2);
}

(кишки) реализации можно найти здесь.

Я пересмотренный @EliArbel, чтобы добавить этот метод в качестве SpansEqual, отбросьте большинство менее интересных исполнителей в чужих бенчмарках, запустите его с разными размерами массива, выходными графиками и отметьте SpansEqual как базовый уровень, чтобы он сообщал, как различные методы сравниваются с SpansEqual.

приведенные ниже цифры взяты из результатов, слегка отредактированных для удаления столбца "ошибка".

|        Method |  ByteCount |               Mean |         StdDev | Scaled |
|-------------- |----------- |-------------------:|---------------:|-------:|
|    SpansEqual |         15 |           3.614 ns |      0.0069 ns |   1.00 |
|  LongPointers |         15 |           4.762 ns |      0.0009 ns |   1.32 |
|      Unrolled |         15 |          16.933 ns |      0.0024 ns |   4.68 |
| PInvokeMemcmp |         15 |          11.448 ns |      0.0183 ns |   3.17 |
|               |            |                    |                |        |
|    SpansEqual |       1026 |          25.957 ns |      0.0081 ns |   1.00 |
|  LongPointers |       1026 |          60.336 ns |      0.0211 ns |   2.32 |
|      Unrolled |       1026 |          37.216 ns |      0.0042 ns |   1.43 |
| PInvokeMemcmp |       1026 |          43.531 ns |      0.0229 ns |   1.68 |
|               |            |                    |                |        |
|    SpansEqual |    1048585 |      42,708.279 ns |      6.7683 ns |   1.00 |
|  LongPointers |    1048585 |      57,952.010 ns |      6.0004 ns |   1.36 |
|      Unrolled |    1048585 |      52,768.967 ns |      5.1800 ns |   1.24 |
| PInvokeMemcmp |    1048585 |      53,270.846 ns |     11.9056 ns |   1.25 |
|               |            |                    |                |        |
|    SpansEqual | 2147483591 | 243,281,911.498 ns | 65,006.3172 ns |   1.00 |
|  LongPointers | 2147483591 | 237,786,969.675 ns | 96,332.7202 ns |   0.98 |
|      Unrolled | 2147483591 | 237,151,053.500 ns | 74,137.6513 ns |   0.97 |
| PInvokeMemcmp | 2147483591 | 235,829,644.641 ns | 50,390.2144 ns |   0.97 |

Я был удивлен, увидев SpansEqual не выходят на первое место для методов max-array-size, но разница настолько незначительна, что я не думаю, что это когда-либо будет иметь значение.

моя система информация:

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
Intel Core i7-6850K CPU 3.60GHz (Skylake), 1 CPU, 12 logical and 6 physical cores
Frequency=3515619 Hz, Resolution=284.4449 ns, Timer=TSC
.NET Core SDK=2.1.300
  [Host]     : .NET Core 2.1.0 (CoreCLR 4.6.26515.07, CoreFX 4.6.26515.06), 64bit RyuJIT
  DefaultJob : .NET Core 2.1.0 (CoreCLR 4.6.26515.07, CoreFX 4.6.26515.06), 64bit RyuJIT
 using System.Linq; //SequenceEqual

 byte[] ByteArray1 = null;
 byte[] ByteArray2 = null;

 ByteArray1 = MyFunct1();
 ByteArray2 = MyFunct2();

 if (ByteArray1.SequenceEqual<byte>(ByteArray2) == true)
 {
    MessageBox.Show("Match");
 }
 else
 {
   MessageBox.Show("Don't match");
 }

давайте добавим еще одну!

недавно Microsoft выпустила специальный пакет NuGet,

Я бы использовал небезопасный код и запустил for цикл сравнения указателей Int32.

возможно, Вам также следует рассмотреть возможность проверки массивов на ненулевое значение.

Если вы посмотрите, как .NET делает строку.Equals, вы видите, что он использует частный метод EqualsHelper, который имеет "небезопасную" реализацию указателя. .NET Reflector ваш друг, чтобы увидеть, как все делается внутри.

Это может быть использовано в качестве шаблона для сравнения байтовый массив, который я сделал реализацию в блог быстрое сравнение массива байтов в C#. Я также сделал некоторые рудиментарные тесты, чтобы увидеть, когда сейф реализация происходит быстрее, чем небезопасно.

тем не менее, если вам действительно не нужна производительность убийцы, Я бы пошел на простое сравнение цикла fr.

Я разработал метод, который слегка бьет memcmp() (ответ плинтуса) и очень слабо бьет EqualBytesLongUnrolled() (ответ Арека Бульски). В принципе, он разворачивает цикл на 4 вместо 8.

public static unsafe bool NewMemCmp(byte* b0, byte* b1, int length)
{
    byte* lastAddr = b0 + length;
    byte* lastAddrMinus32 = lastAddr - 32;
    while (b0 < lastAddrMinus32) // unroll the loop so that we are comparing 32 bytes at a time.
    {
        if (*(ulong*)b0 != *(ulong*)b1) return false;
        if (*(ulong*)(b0 + 8) != *(ulong*)(b1 + 8)) return false;
        if (*(ulong*)(b0 + 16) != *(ulong*)(b1 + 16)) return false;
        if (*(ulong*)(b0 + 24) != *(ulong*)(b1 + 24)) return false;
        b0 += 32;
        b1 += 32;
    }
    while (b0 < lastAddr)
    {
        if (*b0 != *b1) return false;
        b0++;
        b1++;
    }
    return true;
}

public static unsafe bool NewMemCmp(byte[] arr0, byte[] arr1, int length)
{
    fixed (byte* b0 = arr0, b1 = arr1)
    {
        return b0 == b1 || NewMemCmp(b0, b1, length);
    }
}

это работает примерно на 25% быстрее, чем memcmp() и примерно на 5% быстрее, чем EqualBytesLongUnrolled() на моей машине.

Кажется,EqualBytesLongUnrolled является лучшим из предложенных выше.

пропущенные методы (перечислимые.Последовательность Равная, Структурные Сравнения.StructuralEqualityComparer.Равные), не были-терпеливы-для-медленного. На массивах 265 МБ я измерил это:

Host Process Environment Information:
BenchmarkDotNet.Core=v0.9.9.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-3770 CPU 3.40GHz, ProcessorCount=8
Frequency=3323582 ticks, Resolution=300.8802 ns, Timer=TSC
CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
GC=Concurrent Workstation
JitModules=clrjit-v4.6.1590.0

Type=CompareMemoriesBenchmarks  Mode=Throughput  

                 Method |      Median |    StdDev | Scaled | Scaled-SD |
----------------------- |------------ |---------- |------- |---------- |
             NewMemCopy |  30.0443 ms | 1.1880 ms |   1.00 |      0.00 |
 EqualBytesLongUnrolled |  29.9917 ms | 0.7480 ms |   0.99 |      0.04 |
          msvcrt_memcmp |  30.0930 ms | 0.2964 ms |   1.00 |      0.03 |
          UnsafeCompare |  31.0520 ms | 0.7072 ms |   1.03 |      0.04 |
       ByteArrayCompare | 212.9980 ms | 2.0776 ms |   7.06 |      0.25 |

OS=Windows
Processor=?, ProcessorCount=8
Frequency=3323582 ticks, Resolution=300.8802 ns, Timer=TSC
CLR=CORE, Arch=64-bit ? [RyuJIT]
GC=Concurrent Workstation
dotnet cli version: 1.0.0-preview2-003131

Type=CompareMemoriesBenchmarks  Mode=Throughput  

                 Method |      Median |    StdDev | Scaled | Scaled-SD |
----------------------- |------------ |---------- |------- |---------- |
             NewMemCopy |  30.1789 ms | 0.0437 ms |   1.00 |      0.00 |
 EqualBytesLongUnrolled |  30.1985 ms | 0.1782 ms |   1.00 |      0.01 |
          msvcrt_memcmp |  30.1084 ms | 0.0660 ms |   1.00 |      0.00 |
          UnsafeCompare |  31.1845 ms | 0.4051 ms |   1.03 |      0.01 |
       ByteArrayCompare | 212.0213 ms | 0.1694 ms |   7.03 |      0.01 |

для сравнения коротких байтовых массивов следующий интересный Хак:

if(myByteArray1.Length != myByteArray2.Length) return false;
if(myByteArray1.Length == 8)
   return BitConverter.ToInt64(myByteArray1, 0) == BitConverter.ToInt64(myByteArray2, 0); 
else if(myByteArray.Length == 4)
   return BitConverter.ToInt32(myByteArray2, 0) == BitConverter.ToInt32(myByteArray2, 0); 

тогда я, вероятно, выпал бы на решение, указанное в вопросе.

было бы интересно сделать анализ выполнения этого кода.

не смог найти решение, которым я полностью доволен (разумная производительность, но нет небезопасного кода / pinvoke), поэтому я придумал это, ничего действительно оригинального, но работает:

    /// <summary>
    /// 
    /// </summary>
    /// <param name="array1"></param>
    /// <param name="array2"></param>
    /// <param name="bytesToCompare"> 0 means compare entire arrays</param>
    /// <returns></returns>
    public static bool ArraysEqual(byte[] array1, byte[] array2, int bytesToCompare = 0)
    {
        if (array1.Length != array2.Length) return false;

        var length = (bytesToCompare == 0) ? array1.Length : bytesToCompare;
        var tailIdx = length - length % sizeof(Int64);

        //check in 8 byte chunks
        for (var i = 0; i < tailIdx; i += sizeof(Int64))
        {
            if (BitConverter.ToInt64(array1, i) != BitConverter.ToInt64(array2, i)) return false;
        }

        //check the remainder of the array, always shorter than 8 bytes
        for (var i = tailIdx; i < length; i++)
        {
            if (array1[i] != array2[i]) return false;
        }

        return true;
    }

производительность по сравнению с некоторыми другими решениями на этой странице:

простой цикл: 19837 тиков, 1.00

* BitConverter: 4886 тиков, 4.06

UnsafeCompare: 1636 тиков, 12.12

EqualBytesLongUnrolled: 637 тиков, 31.09

P / Invoke memcmp: 369 тиков, 53.67

протестировано в linqpad, 1000000 байт идентичных массивов (худший сценарий), 500 итераций каждый.

Я думал о методах ускорения блочной передачи, встроенных во многие графические карты. Но тогда вам придется скопировать все данные по байтам, поэтому это не очень поможет вам, если вы не хотите реализовать целую часть своей логики в неуправляемом и аппаратно-зависимом коде...

другой способ оптимизации, подобный подходу, показанному выше, будет хранить как можно больше ваших данных в длинном [], а не в байте[] с самого начала, например, если вы считывают его последовательно из двоичного файла или, если вы используете файл с отображением памяти, считывают данные в виде длинных[] или одиночных длинных значений. Затем вашему циклу сравнения потребуется только 1/8 от числа итераций, которые он должен был бы сделать для байта [], содержащего тот же объем данных. Это вопрос того, когда и как часто вам нужно сравнивать и когда и как часто вам нужно обращаться к данным байт за байтом, например, использовать его в вызове API в качестве параметра в методе, который ожидает байт[]. В конец, вы можете только сказать, если вы действительно знаете прецедент...

Я сделал некоторые измерения с помощью прилагаемой программы .net 4.7 release build без подключенного отладчика. Я думаю, что люди использовали неправильную метрику, так как то, что вы о том, если вы заботитесь о скорости здесь, - это то, сколько времени требуется, чтобы выяснить, равны ли два байтовых массива. т. е. пропускная способность в байтах.

StructuralComparison :       2838.8 MiB/s
for                  :   30553811.0 MiB/s
ToUInt32             :   23864406.8 MiB/s
ToUInt64             :    5526595.7 MiB/s
memcmp               : 1848977556.1 MiB/s

как вы можете видеть, нет лучшего способа, чем memcmp и это на порядки быстрее. Простой for цикл является вторым лучшим вариантом. И это все еще ошеломляет меня почему Microsoft не может просто включить Buffer.Compare метод.

[программы.cs]:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace memcmp
{
    class Program
    {
        static byte[] TestVector(int size)
        {
            var data = new byte[size];
            using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
            {
                rng.GetBytes(data);
            }
            return data;
        }

        static TimeSpan Measure(string testCase, TimeSpan offset, Action action, bool ignore = false)
        {
            var t = Stopwatch.StartNew();
            var n = 0L;
            while (t.Elapsed < TimeSpan.FromSeconds(10))
            {
                action();
                n++;
            }
            var elapsed = t.Elapsed - offset;
            if (!ignore)
            {
                Console.WriteLine($"{testCase,-16} : {n / elapsed.TotalSeconds,16:0.0} MiB/s");
            }
            return elapsed;
        }

        [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern int memcmp(byte[] b1, byte[] b2, long count);

        static void Main(string[] args)
        {
            // how quickly can we establish if two sequences of bytes are equal?

            // note that we are testing the speed of different comparsion methods

            var a = TestVector(1024 * 1024); // 1 MiB
            var b = (byte[])a.Clone();

            var offset = Measure("offset", new TimeSpan(), () => { return; }, ignore: true);

            Measure("StructuralComparison", offset, () =>
            {
                StructuralComparisons.StructuralEqualityComparer.Equals(a, b);
            });

            Measure("for", offset, () =>
            {
                for (int i = 0; i < a.Length; i++)
                {
                    if (a[i] != b[i]) break;
                }
            });

            Measure("ToUInt32", offset, () =>
            {
                for (int i = 0; i < a.Length; i += 4)
                {
                    if (BitConverter.ToUInt32(a, i) != BitConverter.ToUInt32(b, i)) break;
                }
            });

            Measure("ToUInt64", offset, () =>
            {
                for (int i = 0; i < a.Length; i += 8)
                {
                    if (BitConverter.ToUInt64(a, i) != BitConverter.ToUInt64(b, i)) break;
                }
            });

            Measure("memcmp", offset, () =>
            {
                memcmp(a, b, a.Length);
            });
        }
    }
}

Извините, если вы ищете управляемый способ, вы уже делаете это правильно, и, насколько мне известно, в BCL нет встроенного метода для этого.

вы должны добавить некоторые начальные нулевые проверки, а затем просто использовать его, как если бы он был в BCL.

Это почти наверняка намного медленнее, чем любая другая версия, приведенная здесь, но было весело писать.

static bool ByteArrayEquals(byte[] a1, byte[] a2) 
{
    return a1.Zip(a2, (l, r) => l == r).All(x => x);
}

Я не видел здесь много решений linq.

Я не уверен в последствиях для производительности, однако я обычно придерживаюсь linq как правило, а затем оптимизировать позже, если это необходимо.

public bool CompareTwoArrays(byte[] array1, byte[] array2)
 {
   return !array1.Where((t, i) => t != array2[i]).Any();
 }

обратите внимание, что это работает только в том случае, если они имеют одинаковый размер массивов. расширение может выглядеть так

public bool CompareTwoArrays(byte[] array1, byte[] array2)
 {
   if (array1.Length != array2.Length) return false;
   return !array1.Where((t, i) => t != array2[i]).Any();
 }

Я остановился на решении, вдохновленном методом EqualBytesLongUnrolled, опубликованным ArekBulski с дополнительной оптимизацией. В моем случае различия массивов в массивах, как правило, находятся рядом с хвостом массивов. В тестировании я обнаружил, что когда это имеет место для больших массивов, возможность сравнивать элементы массива в обратном порядке дает этому решению огромный прирост производительности по сравнению с решением на основе memcmp. Вот это решение:

public enum CompareDirection { Forward, Backward }

private static unsafe bool UnsafeEquals(byte[] a, byte[] b, CompareDirection direction = CompareDirection.Forward)
{
    // returns when a and b are same array or both null
    if (a == b) return true;

    // if either is null or different lengths, can't be equal
    if (a == null || b == null || a.Length != b.Length)
        return false;

    const int UNROLLED = 16;                // count of longs 'unrolled' in optimization
    int size = sizeof(long) * UNROLLED;     // 128 bytes (min size for 'unrolled' optimization)
    int len = a.Length;
    int n = len / size;         // count of full 128 byte segments
    int r = len % size;         // count of remaining 'unoptimized' bytes

    // pin the arrays and access them via pointers
    fixed (byte* pb_a = a, pb_b = b)
    {
        if (r > 0 && direction == CompareDirection.Backward)
        {
            byte* pa = pb_a + len - 1;
            byte* pb = pb_b + len - 1;
            byte* phead = pb_a + len - r;
            while(pa >= phead)
            {
                if (*pa != *pb) return false;
                pa--;
                pb--;
            }
        }

        if (n > 0)
        {
            int nOffset = n * size;
            if (direction == CompareDirection.Forward)
            {
                long* pa = (long*)pb_a;
                long* pb = (long*)pb_b;
                long* ptail = (long*)(pb_a + nOffset);
                while (pa < ptail)
                {
                    if (*(pa + 0) != *(pb + 0) || *(pa + 1) != *(pb + 1) ||
                        *(pa + 2) != *(pb + 2) || *(pa + 3) != *(pb + 3) ||
                        *(pa + 4) != *(pb + 4) || *(pa + 5) != *(pb + 5) ||
                        *(pa + 6) != *(pb + 6) || *(pa + 7) != *(pb + 7) ||
                        *(pa + 8) != *(pb + 8) || *(pa + 9) != *(pb + 9) ||
                        *(pa + 10) != *(pb + 10) || *(pa + 11) != *(pb + 11) ||
                        *(pa + 12) != *(pb + 12) || *(pa + 13) != *(pb + 13) ||
                        *(pa + 14) != *(pb + 14) || *(pa + 15) != *(pb + 15)
                    )
                    {
                        return false;
                    }
                    pa += UNROLLED;
                    pb += UNROLLED;
                }
            }
            else
            {
                long* pa = (long*)(pb_a + nOffset);
                long* pb = (long*)(pb_b + nOffset);
                long* phead = (long*)pb_a;
                while (phead < pa)
                {
                    if (*(pa - 1) != *(pb - 1) || *(pa - 2) != *(pb - 2) ||
                        *(pa - 3) != *(pb - 3) || *(pa - 4) != *(pb - 4) ||
                        *(pa - 5) != *(pb - 5) || *(pa - 6) != *(pb - 6) ||
                        *(pa - 7) != *(pb - 7) || *(pa - 8) != *(pb - 8) ||
                        *(pa - 9) != *(pb - 9) || *(pa - 10) != *(pb - 10) ||
                        *(pa - 11) != *(pb - 11) || *(pa - 12) != *(pb - 12) ||
                        *(pa - 13) != *(pb - 13) || *(pa - 14) != *(pb - 14) ||
                        *(pa - 15) != *(pb - 15) || *(pa - 16) != *(pb - 16)
                    )
                    {
                        return false;
                    }
                    pa -= UNROLLED;
                    pb -= UNROLLED;
                }
            }
        }

        if (r > 0 && direction == CompareDirection.Forward)
        {
            byte* pa = pb_a + len - r;
            byte* pb = pb_b + len - r;
            byte* ptail = pb_a + len;
            while(pa < ptail)
            {
                if (*pa != *pb) return false;
                pa++;
                pb++;
            }
        }
    }

    return true;
}

использовать SequenceEquals для сравнения.

короткий ответ:

    public bool Compare(byte[] b1, byte[] b2)
    {
        return Encoding.ASCII.GetString(b1) == Encoding.ASCII.GetString(b2);
    }

таким образом, вы можете использовать оптимизированную строку .NET compare для сравнения массива байтов без необходимости писать небезопасный код. Вот как это делается в фон:

private unsafe static bool EqualsHelper(String strA, String strB)
{
    Contract.Requires(strA != null);
    Contract.Requires(strB != null);
    Contract.Requires(strA.Length == strB.Length);

    int length = strA.Length;

    fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
    {
        char* a = ap;
        char* b = bp;

        // Unroll the loop

        #if AMD64
            // For the AMD64 bit platform we unroll by 12 and
            // check three qwords at a time. This is less code
            // than the 32 bit case and is shorter
            // pathlength.

            while (length >= 12)
            {
                if (*(long*)a     != *(long*)b)     return false;
                if (*(long*)(a+4) != *(long*)(b+4)) return false;
                if (*(long*)(a+8) != *(long*)(b+8)) return false;
                a += 12; b += 12; length -= 12;
            }
       #else
           while (length >= 10)
           {
               if (*(int*)a != *(int*)b) return false;
               if (*(int*)(a+2) != *(int*)(b+2)) return false;
               if (*(int*)(a+4) != *(int*)(b+4)) return false;
               if (*(int*)(a+6) != *(int*)(b+6)) return false;
               if (*(int*)(a+8) != *(int*)(b+8)) return false;
               a += 10; b += 10; length -= 10;
           }
       #endif

        // This depends on the fact that the String objects are
        // always zero terminated and that the terminating zero is not included
        // in the length. For odd string sizes, the last compare will include
        // the zero terminator.
        while (length > 0)
        {
            if (*(int*)a != *(int*)b) break;
            a += 2; b += 2; length -= 2;
        }

        return (length <= 0);
    }
}

поскольку многие из причудливых решений выше не работают с UWP, и потому что я люблю Linq и функциональные подходы, я настаиваю на своей версии этой проблемы. Чтобы избежать сравнения, когда происходит первое различие, я выбрал .FirstOrDefault ()

public static bool CompareByteArrays(byte[] ba0, byte[] ba1) =>
    !(ba0.Length != ba1.Length || Enumerable.Range(1,ba0.Length)
        .FirstOrDefault(n => ba0[n] != ba1[n]) > 0);

вид грубой силы, но его просто преобразовать массив байт в строку base64 и сравнить две строки. Удобно, если у вас есть большие массивы для сравнения. Или если один из байтовых массивов уже находится в формате Base64.

static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
    string s1 = Convert.ToBase64String(a1);
    string s2 = Convert.ToBase64String(a2);
    if(s1 == s2) return true;
    return false
}

Я полагаю, что самый быстрый способ (с лучшей производительностью для больших массивов) - это хэшировать оба байтовых массива и сравнивать хэши.

Если вы ищете очень быстрый компаратор равенства массива байтов, я предлагаю вам взглянуть на эту статью Stsdb Labs:байтовый массив компаратор проверки на равенство. Он имеет некоторые из самых быстрых реализаций для сравнения равенства массива byte [], которые представлены, протестированы и обобщены.

вы также можете сосредоточиться на этих реализаций:

BigEndianByteArrayComparer - быстрый байт [] массив компаратор слева направо (Сохранены) BigEndianByteArrayEqualityComparer - - быстрый байт [] компаратор равенства слева направо (BigEndian) LittleEndianByteArrayComparer - быстрый байт [] массив компаратор справа налево (LittleEndian) LittleEndianByteArrayEqualityComparer - быстрый байт [] компаратор равенства справа налево (LittleEndian)

если у вас есть огромный массив байтов, вы можете сравнить их, преобразовав их в строку.

вы можете использовать что-то вроде

byte[] b1 = // Your array
byte[] b2 = // Your array
string s1 = Encoding.Default.GetString( b1 );
string s2 = Encoding.Default.GetString( b2 );

я использовал это, и я видел огромное влияние на производительность.