Как я могу сделать это быстро?
Конечно, я могу сделать это:
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.
Обратите внимание на мой сверхбыстрый ответ здесь .
IStructuralEquatable
Ответы:
Вы можете использовать метод Enumerable.SequenceEqual .
Если по какой-то причине вы не можете использовать .NET 3.5, ваш метод в порядке.
Среда компилятора \ среды выполнения оптимизирует ваш цикл, поэтому вам не нужно беспокоиться о производительности.
источник
P / Invoke полномочия активируются!
источник
Для этого в .NET 4 есть новое встроенное решение - IStructuralEquatable
источник
StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)
. НетNullReferenceException
здесь.Пользователь gil предложил небезопасный код, который породил это решение:
который выполняет 64-битное сравнение для максимально возможного массива. Этот вид рассчитывает на то, что массивы начинаются с выравнивания qword. Это сработает, если не выровнять qword, но не так быстро, как если бы это было.
Он работает примерно на семь таймеров быстрее, чем простой
for
цикл. Использование библиотеки J # выполняется аналогично оригинальномуfor
циклу. Использование .SequenceEqual работает в семь раз медленнее; Я думаю только потому, что он использует IEnumerator.MoveNext. Я думаю, что решения на основе LINQ, по крайней мере, такие медленные или хуже.источник
Span<T>
предлагает чрезвычайно конкурентоспособную альтернативу без необходимости вводить запутанный и / или непереносимый пух в базу кода вашего собственного приложения:Реализация (внутренности) .NET Core 3.1.0 может быть найдена здесь .
Я пересмотрел суть @ EliArbel, добавив этот метод так
SpansEqual
, чтобы отбрасывать большинство менее интересных исполнителей в тестах других, запускать его с различными размерами массива, выводить графики и отмечатьSpansEqual
в качестве базового уровня, чтобы он сообщал, как различные методы сравниваются сSpansEqual
,Приведенные ниже цифры взяты из результатов, слегка отредактированных для удаления столбца «Ошибка».
Я был удивлен, увидев,
SpansEqual
что методы max-array-size не вышли на первое место, но разница настолько мала, что я не думаю, что это когда-либо будет иметь значение.Информация о моей системе:
источник
{ReadOnly,}Span<T>
имеет свою собственную версиюSequenceEqual
(то же имя, потому что у него тот же контракт, что и у соответствующегоIEnumerable<T>
метода расширения, он просто быстрее). Обратите внимание, что{ReadOnly,}Span<T>
нельзя использоватьIEnumerable<T>
методы расширения из-за ограничений наref struct
типы.Span<T>
реализации дляnetstandard1.1
и выше (поэтому поиграйтесь с этой интерактивной диаграммой, чтобы увидеть, что это такое). «Быстрый»Span<T>
доступен в настоящее время только в .NET Core 2.1, но учтите, что вSequenceEqual<T>
особенности между «быстрым» и «медленным» / «переносимым» должно быть очень мало различий (хотяnetstandard2.0
цели должны видеть небольшое улучшение, потому что они иметь векторизованный путь кода).Если вы не против этого, вы можете импортировать сборку J # "vjslib.dll" и использовать ее метод Arrays.equals (byte [], byte []) ...
Не вините меня, если кто-то смеется над вами, хотя ...
РЕДАКТИРОВАТЬ: Для чего мало, я использовал Reflector, чтобы разобрать код для этого, и вот как это выглядит:
источник
.NET 3.5 и новее имеют новый открытый тип,
System.Data.Linq.Binary
который инкапсулируетbyte[]
. Он реализует,IEquatable<Binary>
что (по сути) сравнивает два байтовых массива. Обратите внимание, чтоSystem.Data.Linq.Binary
также есть оператор неявного преобразования изbyte[]
.Документация MSDN: System.Data.Linq.Binary
Отражатель декомпилируется методом Equals:
Интересным моментом является то, что они переходят к байтовому циклу сравнения, только если хэши двух двоичных объектов совпадают. Это, однако, происходит за счет вычисления хэша в конструкторе
Binary
объектов (путем обхода массива с помощьюfor
цикла :-)).Вышеприведенная реализация означает, что в худшем случае вам, возможно, придется обходить массивы три раза: сначала для вычисления хеш-массива array1, затем для вычисления хеш-массива array2 и, наконец, (поскольку это худший сценарий, длины и хеш-значения равны) для сравнения байты в массиве 1 с байтами в массиве 2.
В целом, несмотря на то, что
System.Data.Linq.Binary
он встроен в BCL, я не думаю, что это самый быстрый способ сравнить два байтовых массива: - |.источник
Я опубликовал аналогичный вопрос о проверке, заполнен ли ноль byte []. (SIMD-код был взломан, поэтому я удалил его из этого ответа.) Вот самый быстрый код из моих сравнений:
Измеряется на двух 256 МБ байтовых массивах:
источник
источник
Давайте добавим еще один!
Недавно Microsoft выпустила специальный пакет NuGet, System.Runtime.CompilerServices.Unsafe . Он особенный, потому что он написан на IL , и предоставляет низкоуровневую функциональность, прямо недоступную в C #.
Один из его методов
Unsafe.As<T>(object)
позволяет приводить любой ссылочный тип к другому ссылочному типу, пропуская любые проверки безопасности. Обычно это очень плохая идея, но если оба типа имеют одинаковую структуру, это может сработать. Таким образом, мы можем использовать это, чтобы привестиbyte[]
кlong[]
:Обратите внимание, что
long1.Length
все равно будет возвращаться длина исходного массива, поскольку он хранится в поле в структуре памяти массива.Этот метод не так быстр, как другие методы, продемонстрированные здесь, но он намного быстрее, чем простой метод, не использует небезопасный код, P / Invoke или пиннинг, и реализация довольно проста (IMO). Вот некоторые результаты BenchmarkDotNet с моей машины:
Я также создал суть со всеми тестами .
источник
NewMemCmp
ответ, чтобы использовать AVX-2Я разработал метод, который немного бьет
memcmp()
(ответ плинтуса) и очень мало бьетEqualBytesLongUnrolled()
(ответ ) на моем ПК. По сути, он разворачивает цикл на 4 вместо 8.Обновление 30 марта 2019 :
Начиная с .NET core 3.0, у нас есть поддержка SIMD!
Это решение является самым быстрым с большим отрывом на моем ПК:
источник
null
.Span
иmemcpy
?Я бы использовал небезопасный код и запускал
for
цикл, сравнивающий указатели Int32.Может быть, вам следует также рассмотреть проверку массивов, чтобы быть ненулевым.
источник
Если вы посмотрите, как .NET выполняет string.Equals, вы увидите, что он использует закрытый метод EqualsHelper, который имеет «небезопасную» реализацию указателя. .NET Reflector - ваш друг, чтобы увидеть, как все происходит внутри.
Это можно использовать в качестве шаблона для сравнения байтового массива, о котором я рассказывал в блоге. Быстрое сравнение байтового массива в C # . Я также сделал несколько элементарных тестов, чтобы увидеть, когда безопасная реализация быстрее небезопасной.
Тем не менее, если вам действительно не нужна убийственная производительность, я бы пошел на простое сравнение цикла fr.
источник
Не смог найти решения, которым я полностью доволен (разумная производительность, но небезопасный код / пин-код), поэтому я придумал это, ничего оригинального, но работает:
Производительность по сравнению с некоторыми другими решениями на этой странице:
Простой цикл: 19837 тиков, 1,00
* BitConverter: 4886 тиков, 4.06
UnsafeCompare: 1636 тиков, 12.12
EqualBytesLongUnrolled: 637 тиков, 31.09
P / Invoke memcmp: 369 тиков, 53,67
Протестировано в linqpad, 1000000 байтов идентичных массивов (в худшем случае), 500 итераций каждый.
источник
Похоже, что EqualBytesLongUnrolled является лучшим из предложенного выше.
Пропущенные методы (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals) не были медленными для пациента. На 265 МБ массивах я измерил это:
источник
NewMemCmp
ответ, чтобы использовать AVX-2Я не видел много решений linq здесь.
Я не уверен в последствиях для производительности, однако, как правило, придерживаюсь практического
linq
правила, а затем оптимизирую его позже, если это необходимо.Обратите внимание, что это работает, только если они имеют одинаковый размер массива. расширение может выглядеть так
источник
Я провел некоторые измерения, используя прилагаемую программу .net 4.7 Release build без отладчика. Я думаю, что люди использовали неправильную метрику, так как то, о чем вы говорите, если вы заботитесь о скорости, - это сколько времени нужно, чтобы выяснить, равны ли два байтовых массива. т.е. пропускная способность в байтах.
Как видите, лучшего способа нет,
memcmp
и он на несколько порядков быстрее. Простойfor
цикл - второй лучший вариант. И это все еще поражает, почему Microsoft не может просто включитьBuffer.Compare
метод.[Program.cs]:
источник
Для сравнения коротких байтовых массивов интересным является следующее:
Тогда я бы наверняка выпал на решение, указанное в вопросе.
Было бы интересно сделать анализ производительности этого кода.
источник
Для тех из вас, кто заботится о порядке (т. Е. Хочет, чтобы вы
memcmp
возвращали «int
как надо» вместо «ничего»), .NET Core 3.0 (и, по-видимому, .NET Standard 2.1 или .NET 5.0) будет включатьSpan.SequenceCompareTo(...)
метод расширения (плюс aSpan.SequenceEqualTo
), который может использоваться для сравнения двухReadOnlySpan<T>
экземпляров (where T: IComparable<T>
).В первоначальном предложении GitHub обсуждение включало сравнение подходов с вычислениями таблиц переходов, чтение
byte[]
aslong[]
, использование SIMD и p / invoke для реализации CLR.memcmp
.В дальнейшем это должен быть метод перехода для сравнения массивов байтов или диапазонов байтов (как следует использовать
Span<byte>
вместоbyte[]
API-интерфейсов .NET Standard 2.1), и он достаточно быстр, чтобы вам больше не нужно было его оптимизировать (и нет, несмотря на сходство в названии, оно не так ужасно, как ужасноEnumerable.SequenceEqual
).источник
Я думал о методах ускорения передачи блоков, встроенных во многие видеокарты. Но тогда вам придется копировать все данные побайтно, так что это вам мало поможет, если вы не хотите реализовывать всю часть своей логики в неуправляемом и аппаратно-зависимом коде ...
Другой способ оптимизации, подобный подходу, показанному выше, заключается в том, чтобы хранить как можно больше ваших данных в long [], а не byte [] с самого начала, например, если вы последовательно читаете их из двоичного файла, или если вы используете файл с отображением в памяти, считывайте данные как длинные [] или одиночные длинные значения. Тогда вашему циклу сравнения потребуется только 1/8 от количества итераций, которые он должен выполнить для байта [], содержащего такое же количество данных. Речь идет о том, когда и как часто вам нужно сравнивать данные с тем, когда и как часто вам нужно обращаться к данным побайтово, например, использовать их в вызове API в качестве параметра в методе, который ожидает байт []. В конце концов, вы можете только сказать, действительно ли вы знаете вариант использования ...
источник
Это почти наверняка намного медленнее, чем любая другая версия, приведенная здесь, но было весело писать.
источник
Я остановился на решении, основанном на методе EqualBytesLongUnrolled, опубликованном ArekBulski, с дополнительной оптимизацией. В моем случае различия массивов в массивах, как правило, находятся рядом с хвостом массивов. В ходе тестирования я обнаружил, что в случае больших массивов возможность сравнивать элементы массива в обратном порядке дает этому решению огромный выигрыш в производительности по сравнению с решением на основе memcmp. Вот это решение:
источник
Извините, если вы ищете управляемый способ, вы уже делаете это правильно, и, насколько мне известно, в BCL нет встроенного метода для этого.
Вы должны добавить несколько начальных нулевых проверок, а затем просто повторно использовать их, как если бы они были в BCL.
источник
Используйте
SequenceEquals
для этого для сравнения.источник
Если вы ищете очень быстрый компаратор равенства байтовых массивов, я предлагаю вам взглянуть на эту статью STSdb Labs: Компаратор сравнения байтовых массивов.В нем представлены некоторые из самых быстрых реализаций для сравнения равенства массивов byte [], которые представлены, протестированы и обобщены на производительность.
Вы также можете сосредоточиться на этих реализациях:
BigEndianByteArrayComparer - быстрое сравнение байтов [] массива слева направо (BigEndian) BigEndianByteArrayEqualityComparer - быстрое сравнение байтов [] слева направо (BigEndian) LittleEndianByteArrayComparer - быстрое сравнение байтов [] справа налево (LittleE) LittleEndianByteArrayEqualityComparer - быстрый байт [] равенство равенства справа налево (LittleEndian)
источник
Краткий ответ таков:
Таким образом, вы можете использовать оптимизированное сравнение строк .NET для сравнения байтового массива без необходимости писать небезопасный код. Вот как это делается в фоновом режиме :
источник
Compare(new byte[]{128}, new byte[]{ 255 }) == true
совсем неПоскольку многие из вышеперечисленных причудливых решений не работают с UWP и потому что я люблю Linq и функциональные подходы, я представляю вам свою версию этой проблемы. Чтобы избежать сравнения при возникновении первого различия, я выбрал .FirstOrDefault ()
источник
IndexOutOfRangeException
при сравнении непустых массивов , потому что вы к элементам1
через ,ba0.Length
когда он должен быть0
черезba0.Length - 1
. Если вы исправите это,Enumerable.Range(0, ba0.Length)
он по-прежнему некорректно возвращаетtrue
массивы равной длины, где отличаются только первые элементы, потому что вы не можете различить удовлетворяющие первые элементыpredicate
и не удовлетворяющие элементыpredicate
;FirstOrDefault<int>()
возвращается0
в обоих случаях.