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

c# .net arrays performance j#

265981 просмотра

28 ответа

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

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

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.

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

Автор: Hafthor Источник Размещён: 17.05.2019 02:40

Ответы (28)


554 плюса

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

Автор: aku Размещён: 04.09.2008 07:53

230 плюса

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;
}
Автор: plinth Размещён: 18.09.2009 03:49

153 плюса

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

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
    return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2);
}
Автор: Ohad Schneider Размещён: 15.11.2011 05:38

70 плюса

Решение

Пользователь 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;
  }
}

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

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

Автор: Hafthor Размещён: 10.01.2012 06:11

25 плюса

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

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


РЕДАКТИРОВАТЬ: Для чего мало, я использовал 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;
}
Автор: Jason Bunting Размещён: 04.09.2008 07:48

24 плюса

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

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

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

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

Автор: Milan Gardian Размещён: 01.05.2009 01:14

23 плюса

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

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

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

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

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

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

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

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

BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
Intel Core i7-6850K CPU 3.60GHz (Skylake), 1 CPU, 12 logical and 6 physical cores
Frequency=3515619 Hz, Resolution=284.4449 ns, Timer=TSC
.NET Core SDK=2.1.300
  [Host]     : .NET Core 2.1.0 (CoreCLR 4.6.26515.07, CoreFX 4.6.26515.06), 64bit RyuJIT
  DefaultJob : .NET Core 2.1.0 (CoreCLR 4.6.26515.07, CoreFX 4.6.26515.06), 64bit RyuJIT
Автор: Joe Amenta Размещён: 03.02.2018 03:45

16 плюса

Я опубликовал аналогичный вопрос о проверке, заполнен ли ноль 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
Автор: ArekBulski Размещён: 23.10.2015 05:07

10 плюса

 using System.Linq; //SequenceEqual

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

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

 if (ByteArray1.SequenceEqual<byte>(ByteArray2) == true)
 {
    MessageBox.Show("Match");
 }
 else
 {
   MessageBox.Show("Don't match");
 }
Автор: user565710 Размещён: 06.01.2011 04:15

8 плюса

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

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

Один из его методов Unsafe.As<T>(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<long[]>(a1);
    var long2 = Unsafe.As<long[]>(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 или пиннинг, а реализация довольно проста (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 |

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

Автор: Eli Arbel Размещён: 03.04.2017 05:57

7 плюса

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

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

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

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

Автор: Mr Anderson Размещён: 23.06.2016 10:23

6 плюса

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

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

Автор: gil Размещён: 04.09.2008 07:45

5 плюса

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

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

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

Автор: Mikael Svenson Размещён: 09.08.2009 08:36

3 плюса

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

Пропущенные методы (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals) не были медленными для пациента. На 265MB массивах я измерил это:

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 |
Автор: Motlicek Petr Размещён: 26.09.2016 11:24

3 плюса

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

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

Как видите, нет лучшего способа, чем memcmpон, и он на несколько порядков быстрее. Простой forцикл - второй лучший вариант. И это все еще поражает, почему 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();

            // 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);
            });
        }
    }
}
Автор: John Leidegren Размещён: 28.09.2017 02:23

2 плюса

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

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

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

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

Автор: Kevin Driedger Размещён: 18.09.2009 03:29

2 плюса

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

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

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

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

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

        return true;
    }

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

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

* BitConverter: 4886 тиков, 4.06

UnsafeCompare: 1636 тиков, 12.12

EqualBytesLongUnrolled: 637 тиков, 31.09

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

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

Автор: Zar Shardan Размещён: 29.03.2016 05:32

2 плюса

Я не видел много решений 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();
 }
Автор: Zapnologica Размещён: 02.02.2018 06:53

1 плюс

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

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

Автор: Mirko Klemm Размещён: 07.08.2009 11:09

1 плюс

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

static bool ByteArrayEquals(byte[] a1, byte[] a2) 
{
    return a1.Zip(a2, (l, r) => l == r).All(x => x);
}
Автор: James Curran Размещён: 01.09.2017 05:56

1 плюс

Я остановился на решении, основанном на методе EqualBytesLongUnrolled, опубликованном АрекБульски, с дополнительной оптимизацией. В моем случае различия массивов в массивах, как правило, находятся рядом с хвостом массивов. В ходе тестирования я обнаружил, что в случае больших массивов возможность сравнивать элементы массива в обратном порядке дает этому решению огромный выигрыш в производительности по сравнению с решением на основе 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;
}
Автор: Casey Chester Размещён: 25.02.2018 04:19

0 плюса

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

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

Автор: Markus Olsson Размещён: 04.09.2008 07:45

-1 плюса

Используйте SequenceEqualsдля этого для сравнения.

Автор: API_Base Размещён: 23.04.2015 06:44

-1 плюса

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

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

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

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);
    }
}
Автор: Alon Размещён: 09.06.2015 06:33

-1 плюса

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

public static bool CompareByteArrays(byte[] ba0, byte[] ba1) =>
    !(ba0.Length != ba1.Length || Enumerable.Range(1,ba0.Length)
        .FirstOrDefault(n => ba0[n] != ba1[n]) > 0);
Автор: Raymond Osterbrink Размещён: 07.02.2018 02:43

-2 плюса

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

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

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

Автор: Kris Размещён: 02.07.2014 11:07

-5 плюса

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

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

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

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

Автор: pratikv Размещён: 11.11.2013 02:18

-6 плюса

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

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

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

Автор: Timothy John Laird Размещён: 26.06.2018 01:09
32x32