Сравнение двух байтовых массивов в .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; } 

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

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

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

Обратите внимание на мой сверхбыстрый ответ здесь .

Вы можете использовать метод Enumerable.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, ваш метод в порядке.
Compiler \ run-time environment оптимизирует ваш цикл, поэтому вам не нужно беспокоиться о производительности.

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 есть новое встроенное решение для .NET 4 – IStructuralEquatable

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

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

 // 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; } } 1 // 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; } } 1 // 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, но не так быстро, как если бы это было так.

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

Если вы не против этого, вы можете импортировать сборку J # «vjslib.dll» и использовать ее методы Arrays.equals (byte [], byte [])

Не обвиняй меня, если кто-то смеется над тобой, хотя …


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 и new имеют новый открытый тип System.Data.Linq.Binary который инкапсулирует byte[] . Он реализует IEquatable который (по сути) сравнивает два байтовых массива. Обратите внимание, что System.Data.Linq.Binary также имеет оператор неявного преобразования из byte[] .

Документация MSDN: System.Data.Linq.Binary

Отражатель декомпилирует метод Equals:

 private bool EqualsTo(Binary binary) { if (this != binary) { if (binary == null) { return false; } if (this.bytes.Length != binary.bytes.Length) { return false; } if (this.hashCode != binary.hashCode) { return false; } int index = 0; int length = this.bytes.Length; while (index < length) { if (this.bytes[index] != binary.bytes[index]) { return false; } index++; } } return true; } 

Интересный поворот в том, что они переходят только к байтовому циклу сравнения, если hashи двух двоичных объектов одинаковы. Это, однако, стоит за счет вычисления hashа в конструкторе Binary объектов (путем перемещения массива с помощью цикла :-)).

Вышеупомянутая реализация означает, что в худшем случае вам, возможно, придется трассировать массивы три раза: сначала вычислить hash массива 1, затем вычислить hash массива2 и, наконец, (поскольку это худший сценарий, длина и hash равны) для сравнения байтов в массиве 1 с байтами в массиве 2.

В целом, хотя System.Data.Linq.Binary встроен в BCL, я не думаю, что это самый быстрый способ сравнить два байтовых массива: - |.

Я отправил аналогичный вопрос о проверке, если 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 предлагает чрезвычайно конкурентную альтернативу, не бросая путаницу и / или не переносимый пушок в базу кода вашего собственного приложения:

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

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

Я переработал gist @ EliArbel, чтобы добавить этот метод в SpansEqual , бросить большинство менее интересных исполнителей в других тестах, запустить его с разными размерами массива, выводить графики и отмечать 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 не выходит сверху для методов максимального размера массива, но разница настолько незначительна, что я не думаю, что это когда-нибудь будет иметь значение.

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

 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(ByteArray2) == true) { MessageBox.Show("Match"); } else { MessageBox.Show("Don't match"); } 

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

Недавно Microsoft выпустила специальный пакет NuGet, System.Runtime.CompilerServices.Unsafe . Это особенное, потому что оно написано в IL , и обеспечивает низкоуровневую функциональность, не доступную непосредственно на C #.

Один из его методов, Unsafe.As(object) позволяет лить любой ссылочный тип другому ссылочному типу, пропуская любые проверки безопасности. Это, как правило, очень плохая идея, но если оба типа имеют одинаковую структуру, она может работать. Таким образом, мы можем использовать это, чтобы сделать byte[] long[] :

 bool CompareWithUnsafeLibrary(byte[] a1, byte[] a2) { if (a1.Length != a2.Length) return false; var longSize = (int)Math.Floor(a1.Length / 8.0); var long1 = Unsafe.As(a1); var long2 = Unsafe.As(a2); for (var i = 0; i < longSize; i++) { if (long1[i] != long2[i]) return false; } for (var i = longSize * 8; i < a1.Length; i++) { if (a1[i] != a2[i]) return false; } return true; } 

Обратите внимание, что long1.Length все равно вернет исходную длину массива, так как она хранится в поле в структуре памяти массива.

Этот метод не такой быстрый, как другие методы, показанные здесь, но он намного быстрее, чем наивный метод, не использует небезопасный код или P / Invoke или pinning, а реализация довольно проста (IMO). Вот некоторые результаты BenchmarkDotNet на моей машине:

 BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0 Processor=Intel(R) Core(TM) i7-4870HQ CPU 2.50GHz, ProcessorCount=8 Frequency=2435775 Hz, Resolution=410.5470 ns, Timer=TSC [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0 DefaultJob : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0 Method | Mean | StdDev | ----------------------- |-------------- |---------- | UnsafeLibrary | 125.8229 ns | 0.3588 ns | UnsafeCompare | 89.9036 ns | 0.8243 ns | JSharpEquals | 1,432.1717 ns | 1.3161 ns | EqualBytesLongUnrolled | 43.7863 ns | 0.8923 ns | NewMemCmp | 65.4108 ns | 0.2202 ns | ArraysEqual | 910.8372 ns | 2.6082 ns | PInvokeMemcmp | 52.7201 ns | 0.1105 ns | 

Я также создал суть всех тестов .

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

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

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

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

Тем не менее, если вам действительно не нужна производительность убийцы, я бы выбрал простое сравнение с петлями.

Я разработал метод, который слегка бьет memcmp() (ответ плинтуса) и очень слабые удары EqualBytesLongUnrolled() (ответ Арека 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 является лучшим из вышеперечисленного.

Пропущенные методы (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals), были не-пациенты для замедления. На массивах 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 | , 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), поэтому я придумал это, ничего действительно оригинального, но работает:

  ///  /// ///  ///  ///  ///  0 means compare entire arrays ///  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, без встроенного отладчика. Я думаю, что люди использовали неправильную метрику, так как то, о чем вы думаете, если вам нужна скорость, вот сколько времени нужно, чтобы выяснить, равны ли два байтовых массива. т.е. пропускная способность в байтах.

 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 и это на порядок быстрее. Простой цикл – второй вариант. И это все еще пугает мой разум, почему Microsoft не может просто включить метод Buffer.Compare .

[Program.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; } 

Use SequenceEquals for this to comparison.

The short answer is this:

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

In such a way you can use the optimized .NET string compare to make a byte array compare without the need to write unsafe code. This is how it is done in the background :

 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); } } 

Since many of the fancy solutions above don’t work with UWP and because I love Linq and functional approaches I pressent you my version to this problem. To escape the comparison when the first difference occures, I chose .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); 

Kind of brute force, but its straightforward to convert a byte array to a Base64 string and compare the two strings. Handy if you’ve got big arrays to compare. Or if one of the byte arrays are already in Base64 format.

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

I imagine that the fastest way (with the best performance for large arrays) is to hash both byte arrays and compare the hashes.

If you are looking for a very fast byte array equality comparer, I suggest you take a look at this STSdb Labs article: Byte array equality comparer. It features some of the fastest implementations for byte[] array equality comparing, which are presented, performance tested and summarized.

You can also focus on these implementations:

BigEndianByteArrayComparer – fast byte[] array comparer from left to right (BigEndian) BigEndianByteArrayEqualityComparer – – fast byte[] equality comparer from left to right (BigEndian) LittleEndianByteArrayComparer – fast byte[] array comparer from right to left (LittleEndian) LittleEndianByteArrayEqualityComparer – fast byte[] equality comparer from right to left (LittleEndian)

In case you have a huge byte array, you can compare them by converting them to string.

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

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

I have used this and I have seen a huge performance impact.

  • Android webview slow
  • Являются ли статические вызовы Java более или менее дорогостоящими, чем нестатические вызовы?
  • Является ли оператор неравенства быстрее, чем оператор равенства?
  • Производительность TypeCasting
  • Относительная производительность команды x86 inc vs. add
  • Замена 32-битного счетчика циклов на 64-битные значения приводит к сумасшедшим отклонениям производительности
  • Инструменты профилирования Delphi
  • Лучше ли использовать std :: memcpy () или std :: copy () в терминах производительности?
  • C # оптимизирует конкатенацию строковых литералов?
  • Выполнение стресс-теста в веб-приложении?
  • Самый эффективный код для первых 10000 простых чисел?
  • Interesting Posts

    Где можно найти настройки таймаута по умолчанию для всех браузеров?

    Невозможно применить к неспецифическому вложенному типу с дженериками

    Как я могу запустить Windows 8 на рабочий стол по умолчанию?

    В Windows Azure: какова роль веб-роли, роли сотрудников и роли VM?

    Есть ли способ обхода CSS classов с именами, которые начинаются с действительных чисел?

    Как использовать относительные / абсолютные пути в URL-адресах css?

    Как запустить Windows, скомпилированную для PowerPC на современных компьютерах PowerPC без набора инструкций,?

    Как проверить, установлены ли какие-либо флаги комбинации флагов?

    Что технически неправильно при быстрой перестановке ОС?

    Слияние большого количества data.frames

    Можем ли мы обновить значения первичных ключей таблицы?

    Поддерживает ли Linux TRIM для SSD на LVM?

    Как отключить горячие клавиши ориентации экрана

    Как получить данные из отдельных листов, отображаемых в отдельной отдельной рабочей таблице

    Как автоматическое изменение размера и настройка элементов управления формы с изменением разрешения

    Давайте будем гением компьютера.