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