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

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

Конечно, я 9X_c# могу это сделать:

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

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

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

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

Обратите 9X_jsonarray внимание на мой сверхбыстрый ответ here.

619
2

  • Это своего рода расчет на тот факт, что массивы начинают выровненное по qword ". Это большое" если ". Вы должны исправить ко ...
22
Общее количество ответов: 22

Ответ #1

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

Вы можете использовать метод 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 

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

696
8

  • Да, это примерно в 50 раз медленнее, ...

Ответ #2

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

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

9X_.net-framework

265
12

  • В этом случае закрепление не требуется. Маршаллер выполняет автоматическое закрепление при вызове собственного кода с п ...

Ответ #3

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

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

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

164
4

  • Согласно [этому сообщению в блоге] (http://uhurumkate.blogspot.com/2012/07/byte-array-comparison-benchmarks.html) это на самом деле очень м ...

Ответ #4

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

Span предлагает чрезвычайно конкурентоспособную 9X_dot-net альтернативу без необходимости бросать запутанный 9X_csharp и / или непереносимый мусор в базу кода 9X_arrays вашего собственного приложения:

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

Реализацию 9X_c-sharp (внутренности) для .NET 5.0.0 можно найти 9X_efficiency here.

У меня есть суть revised @ EliArbel, чтобы добавить 9X_character-arrays этот метод как SpansEqual, отбросить большинство менее 9X_c-sharp интересных исполнителей в других тестах, запустить 9X_j# его с разными размерами массивов, выходными 9X_array графиками и пометить SpansEqual как базовый уровень, чтобы 9X_c-sharp он сообщал, как разные методы сравниваются 9X_c# с SpansEqual.

Приведенные ниже числа взяты из результатов, слегка 9X_j# отредактированы, чтобы удалить столбец «Ошибка».

| 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 | 

Я 9X_performance был удивлен, увидев, что SpansEqual не выходит на 9X_arrays первое место для методов максимального размера 9X_speed массива, но разница настолько незначительна, что 9X_slow я не думаю, что это когда-либо будет иметь 9X_visual-c# значение.

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

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 

154
5

  • install-package syste ...

Ответ #5

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

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

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

, который выполняет сравнение 9X_c# на основе 64-бит для максимально возможной 9X_performance части массива. Этот вид рассчитан на то, что 9X_c-sharp массивы начинают выровнены по qword. Он 9X_arrays будет работать, если не выровнять qword, но 9X_speed не так быстро, как если бы это было.

Он выполняет 9X_performance примерно на семь таймеров быстрее, чем простой 9X_dotnet цикл for. Использование библиотеки J # выполняется 9X_c# аналогично исходному циклу for. Использование 9X_.net-framework .SequenceEqual работает примерно в семь 9X_performance раз медленнее; Я думаю, просто потому, что 9X_jsonarray он использует IEnumerator.MoveNext. Я полагаю, что 9X_jsonarray решения на основе LINQ будут по крайней 9X_dot-net мере настолько медленными или даже хуже.

84
7

  • Хорошее решение. Но один (небольшой) совет: сравнение, если ссылки a1 и a2 равны, может ускорить процесс, е ...

Ответ #6

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

Если вы не против, то можете импортировать 9X_.net-framework сборку J# "vjslib.dll" и использовать 9X_character-arrays ее Arrays.equals(byte[], byte[]) method...

Не вини меня, если кто-то посмеется 9X_visual-c# над тобой...


РЕДАКТИРОВАТЬ: Я использовал 9X_visual-c# Reflector для дизассемблирования кода для 9X_character-arrays этого, и вот как это выглядит:

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

32
0

Ответ #7

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

.NET 3.5 и новее имеют новый общедоступный 9X_c#.net тип, System.Data.Linq.Binary, который инкапсулирует byte[]. Он реализует 9X_dotnet IEquatable, который (по сути) сравнивает два байтовых 9X_arrays массива. Обратите внимание, что System.Data.Linq.Binary также имеет 9X_dotnet оператор неявного преобразования из byte[].

Документация 9X_.net 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; } 

Интересный 9X_arrays поворот заключается в том, что они переходят 9X_character-arrays к циклу побайтового сравнения только в том 9X_.net-framework случае, если хэши двух двоичных объектов 9X_speed совпадают. Однако это происходит за счет 9X_dotnet вычисления хэша в конструкторе объектов 9X_performance Binary (путем обхода массива с помощью цикла for :-)).

Вышеупомянутая 9X_c-sharp реализация означает, что в худшем случае 9X_j# вам, возможно, придется пройти по массивам 9X_visual-c# три раза: сначала для вычисления хэша array1, затем 9X_.net-framework для вычисления хэша array2 и, наконец (потому 9X_c# что это худший сценарий, длины и хеши равны 9X_.net-framework ) для сравнения байтов в массиве 1 с байтами 9X_csharp в массиве 2.

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

28
0

Ответ #8

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

I posted аналогичный вопрос о проверке, заполнен 9X_character-arrays ли byte [] нулями. (Код SIMD был изменен, поэтому 9X_efficiency я удалил его из этого ответа.) Вот самый 9X_slow быстрый код из моих сравнений:

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

Измерено на 9X_dotnet двух массивах байтов по 256 МБ:

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

21
2

  • @AmberdeBlack Вы уверены? Вы тестировали крошечны ...

Ответ #9

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

 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"); } 
9X_dotnet
12
2

  • Вот что я использовал. Но это ммм ... звучит как последовательное сравнение, которое в противном случае вы бы использовали с п ...

Ответ #10

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

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

Недавно Microsoft 9X_character-arrays выпустила специальный пакет NuGet, System.Runtime.CompilerServices.Unsafe. Он 9X_c# особенный, потому что он написан на IL и обеспечивает 9X_speed низкоуровневую функциональность, недоступную 9X_bytearray напрямую в C#.

Один из его методов, Unsafe.As(object), позволяет 9X_jsonarray привести любой ссылочный тип к другому ссылочному 9X_array типу, пропуская любые проверки безопасности. Обычно 9X_efficiency это очень плохая идея, но если оба типа имеют 9X_efficiency одинаковую структуру, это может сработать. Таким 9X_.net-framework образом, мы можем использовать это для преобразования 9X_efficiency 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 по-прежнему 9X_j# будет возвращать исходную длину массива, поскольку 9X_speed она хранится в поле в структуре памяти массива.

Этот 9X_c# метод не так быстр, как другие методы, продемонстрированные 9X_efficiency здесь, но он намного быстрее, чем наивный 9X_jsonarray метод, не использует небезопасный код, P 9X_.net-framework / Invoke или закрепление, а его реализация 9X_dot-net довольно проста (IMO). Вот несколько результатов 9X_.net 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 | 

Я также создал gist with all the tests.

11
0

Ответ #11

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

Я разработал метод, который немного превосходит 9X_slow memcmp() (ответ плинтуса) и очень немного превосходит 9X_c-sharp EqualBytesLongUnrolled() (ответ Арека Бульски) на моем ПК. По сути, он 9X_array разворачивает цикл на 4 вместо 8.

Обновление от 30 марта 2019 г.:

Начиная 9X_c# с .NET core 3.0, у нас есть поддержка SIMD!

Это 9X_c#.net решение является самым быстрым со значительным 9X_c# отрывом на моем ПК:

#if NETCOREAPP3_0 using System.Runtime.Intrinsics.X86; #endif … public static unsafe bool Compare(byte[] arr0, byte[] arr1) { if (arr0 == arr1) { return true; } if (arr0 == null || arr1 == null) { return false; } if (arr0.Length != arr1.Length) { return false; } if (arr0.Length == 0) { return true; } fixed (byte* b0 = arr0, b1 = arr1) { #if NETCOREAPP3_0 if (Avx2.IsSupported) { return Compare256(b0, b1, arr0.Length); } else if (Sse2.IsSupported) { return Compare128(b0, b1, arr0.Length); } else #endif { return Compare64(b0, b1, arr0.Length); } } } #if NETCOREAPP3_0 public static unsafe bool Compare256(byte* b0, byte* b1, int length) { byte* lastAddr = b0 + length; byte* lastAddrMinus128 = lastAddr - 128; const int mask = -1; while (b0 < lastAddrMinus128) // unroll the loop so that we are comparing 128 bytes at a time. { if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0), Avx.LoadVector256(b1))) != mask) { return false; } if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0 + 32), Avx.LoadVector256(b1 + 32))) != mask) { return false; } if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0 + 64), Avx.LoadVector256(b1 + 64))) != mask) { return false; } if (Avx2.MoveMask(Avx2.CompareEqual(Avx.LoadVector256(b0 + 96), Avx.LoadVector256(b1 + 96))) != mask) { return false; } b0 += 128; b1 += 128; } while (b0 < lastAddr) { if (*b0 != *b1) return false; b0++; b1++; } return true; } public static unsafe bool Compare128(byte* b0, byte* b1, int length) { byte* lastAddr = b0 + length; byte* lastAddrMinus64 = lastAddr - 64; const int mask = 0xFFFF; while (b0 < lastAddrMinus64) // unroll the loop so that we are comparing 64 bytes at a time. { if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0), Sse2.LoadVector128(b1))) != mask) { return false; } if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0 + 16), Sse2.LoadVector128(b1 + 16))) != mask) { return false; } if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0 + 32), Sse2.LoadVector128(b1 + 32))) != mask) { return false; } if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.LoadVector128(b0 + 48), Sse2.LoadVector128(b1 + 48))) != mask) { return false; } b0 += 64; b1 += 64; } while (b0 < lastAddr) { if (*b0 != *b1) return false; b0++; b1++; } return true; } #endif public static unsafe bool Compare64(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; } 

10
0

Ответ #12

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

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

Возможно, вам 9X_c# также следует рассмотреть возможность проверки 9X_arrays массивов на ненулевые значения.

8
0

Ответ #13

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

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

Это 9X_character-arrays можно использовать в качестве шаблона для 9X_c#.net сравнения байтовых массивов, реализацию 9X_j# которого я реализовал в сообщении блога 9X_character-arrays Fast byte array comparison in C#. Я также провел несколько элементарных 9X_speed тестов, чтобы увидеть, когда безопасная 9X_speed реализация быстрее небезопасной.

Тем не менее, если 9X_array вам действительно не нужна потрясающая производительность, я 9X_slow бы пошел на простое сравнение цикла fr.

7
0

Ответ #14

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

Для тех из вас, кто заботится о порядке 9X_array (например, хочет, чтобы ваш memcmp возвращал int как 9X_array должен, а не ничего), .NET Core 3.0 (и, предположительно, .NET 9X_arrays Standard 2.1, также известный как .NET 5.0) will include a Span.SequenceCompareTo(...) extension method (плюс 9X_j# Span.SequenceEqualTo), который можно использовать для сравнения 9X_bytearray двух экземпляров ReadOnlySpan (where T: IComparable).

В the original GitHub proposal обсуждение включало 9X_character-arrays сравнение подходов с вычислениями таблицы 9X_visual-c# переходов, чтение byte[] как long[], использование SIMD 9X_c-sharp и p / invoke в memcmp реализации CLR.

В дальнейшем 9X_.net-framework это должен быть ваш метод сравнения массивов 9X_c# байтов или диапазонов байтов (как и использование 9X_csharp Span вместо byte[] для API .NET Standard 2.1), и он 9X_dotnet достаточно быстрый, чтобы вы не следует 9X_performance больше заботиться об оптимизации (и нет, несмотря 9X_dot-net на сходство в названии, он не работает так 9X_.net ужасно, как ужасный Enumerable.SequenceEqual).

#if NETCOREAPP3_0 // Using the platform-native Span.SequenceEqual(..) public static int Compare(byte[] range1, int offset1, byte[] range2, int offset2, int count) { var span1 = range1.AsSpan(offset1, count); var span2 = range2.AsSpan(offset2, count); return span1.SequenceCompareTo(span2); // or, if you don't care about ordering // return span1.SequenceEqual(span2); } #else // The most basic implementation, in platform-agnostic, safe C# public static bool Compare(byte[] range1, int offset1, byte[] range2, int offset2, int count) { // Working backwards lets the compiler optimize away bound checking after the first loop for (int i = count - 1; i >= 0; --i) { if (range1[offset1 + i] != range2[offset2 + i]) { return false; } } return true; } #endif 

6
0

Ответ #15

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

Я провел некоторые измерения, используя 9X_c-sharp прилагаемую сборку выпуска .NET 4.7 без 9X_c-sharp прикрепленного отладчика. Я думаю, что люди 9X_jsonarray использовали неправильную метрику, поскольку, если 9X_bytearray вы заботитесь о скорости, здесь важно, сколько 9X_arrays времени нужно, чтобы выяснить, равны ли 9X_arrays два байтовых массива. т.е. пропускная способность 9X_array в байтах.

StructuralComparison : 4.6 MiB/s for : 274.5 MiB/s ToUInt32 : 263.6 MiB/s ToUInt64 : 474.9 MiB/s memcmp : 8500.8 MiB/s 

Как видите, нет лучшего способа, чем 9X_.net memcmp, и он на несколько порядков быстрее. Второй 9X_speed лучший вариант - простой цикл for. И я до сих 9X_arrays пор не понимаю, почему Microsoft не может 9X_efficiency просто включить метод 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(); // was meant to offset the overhead of everything but copying but my attempt was a horrible mistake... should have reacted sooner due to the initially ridiculous throughput values... // Measure("offset", new TimeSpan(), () => { return; }, ignore: true); var offset = TimeZone.Zero 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); }); } } } 

5
1

  • Вы можете импортировать практически любую функцию, если есть какие-то метаданные для ...

Ответ #16

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

Не удалось найти решение, которым я полностью 9X_array доволен (разумная производительность, но 9X_slow без небезопасного кода / пинвока), поэтому 9X_visual-c# я придумал это, ничего действительно оригинального, но 9X_dotnet работает:

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

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

Простой 9X_efficiency цикл: 19837 тиков, 1,00

* BitConverter: 4886 9X_c#.net тиков, 4,06

UnsafeCompare: 1636 тиков, 12,12

EqualBytesLongUnrolled: 637 9X_slow тиков, 31,09

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

Протестировано 9X_performance в linqpad, 1000000 байт идентичных массивов 9X_.net-framework (наихудший сценарий), 500 итераций каждый.

4
0

Ответ #17

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

Похоже, что EqualBytesLongUnrolled - лучший из предложенных выше.

Пропущенные 9X_dot-net методы (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals) не 9X_speed были терпеливыми для медленных. На 265-мегабайтных 9X_c-sharp массивах я измерил это:

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 | 

4
0

Ответ #18

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

Для сравнения коротких байтовых массивов 9X_array есть интересный прием:

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

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

Было бы 9X_j# интересно провести анализ производительности 9X_arrays этого кода.

3
0

Ответ #19

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

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

Я 9X_.net-framework не уверен в последствиях для производительности, однако 9X_dot-net обычно придерживаюсь linq в качестве практического 9X_arrays правила, а затем оптимизирую позже, если 9X_c# это необходимо.

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

Обратите внимание, что это 9X_csharp работает, только если они представляют собой 9X_arrays массивы одинакового размера. расширение 9X_.net-framework может выглядеть так

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

3
0

Ответ #20

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

Я подумал о методах ускорения передачи блоков, встроенных 9X_dot-net во многие видеокарты. Но тогда вам придется 9X_arrays копировать все данные побайтно, так что 9X_speed это вам не очень поможет, если вы не хотите 9X_jsonarray реализовывать целую часть своей логики в 9X_c# неуправляемом и аппаратно-зависимом коде 9X_dot-net ...

Другой способ оптимизации, аналогичный 9X_character-arrays подходу, показанному выше, - это хранить 9X_dotnet как можно больше ваших данных в длинном 9X_performance [], а не в байтовом [] с самого начала, например, если 9X_array вы читаете их последовательно из двоичный 9X_.net-framework файл, или, если вы используете файл с отображением 9X_slow памяти, считайте данные как длинные [] или 9X_bytearray одиночные длинные значения. Тогда вашему 9X_j# циклу сравнения потребуется только 1/8 от 9X_efficiency числа итераций, которые он должен был бы 9X_.net-framework сделать для байта [], содержащего такое 9X_speed же количество данных. Это вопрос того, когда 9X_character-arrays и как часто вам нужно сравнивать, а когда 9X_bytearray и как часто вам нужно обращаться к данным 9X_slow побайтово, например использовать его в вызове 9X_array API в качестве параметра в методе, ожидающем 9X_bytearray byte []. В конце концов, вы можете сказать 9X_performance только, действительно ли вы знаете сценарий 9X_c# использования ...

2
0

Ответ #21

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

Извините, если вы ищете управляемый способ, вы 9X_c# уже делаете это правильно, и, насколько 9X_csharp мне известно, в BCL нет встроенного метода 9X_.net-framework для этого.

Вы должны добавить некоторые начальные 9X_dotnet проверки на null, а затем просто повторно 9X_.net использовать их, как если бы они находились 9X_efficiency в BCL.

1
0

Ответ #22

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

Я остановился на решении, вдохновленном 9X_c-sharp методом EqualBytesLongUnrolled, опубликованным 9X_c#.net ArekBulski, с дополнительной оптимизацией. В 9X_dot-net моем случае различия массивов в массивах 9X_visual-c# имеют тенденцию быть ближе к концу массивов. При 9X_array тестировании я обнаружил, что когда это 9X_.net-framework имеет место для больших массивов, возможность 9X_character-arrays сравнивать элементы массива в обратном порядке 9X_j# дает этому решению огромный прирост производительности 9X_character-arrays по сравнению с решением на основе memcmp. Вот 9X_efficiency это решение:

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

1
0