Porównywanie dwóch tablic bajtowych in.NET

Jak mogę to zrobić szybko?

Jasne, że dam radę:

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

Ale szukam albo BCL funkcji lub jakiegoś wysoce zoptymalizowanego sprawdzonego sposobu na to.

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

Działa ładnie, ale nie wygląda na to, żeby to działało dla x64.

Zwróć uwagę na moją superszybką odpowiedź tutaj .

Author: Community, 2008-09-04

28 answers

Możesz użyć Enumerable.Metoda 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

Jeśli z jakiegoś powodu nie możesz używać. NET 3.5, twoja metoda jest OK.
Środowisko kompilatora \ run-time zoptymalizuje Twoją pętlę, więc nie musisz się martwić o wydajność.

 522
Author: aku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-04 08:08:31

P / Invoke moc aktywować!

[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;
}
 223
Author: plinth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-28 23:45:14

Jest nowe wbudowane rozwiązanie do tego w. Net 4 - IStructuralEquatable

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
    return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2);
}
 150
Author: Ohad Schneider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-25 11:36:36

Użytkownik gil zasugerował ten kod:

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

, które wykonuje 64-bitowe porównanie Dla jak największej ilości tablicy. Ten rodzaj liczy się z tym, że tablice rozpoczynają QWord aligned. To będzie działać, jeśli nie QWord wyrównane, tylko nie tak szybko, jak gdyby było.

Wykonuje około siedem timerów szybciej niż prosta pętla for. Używając biblioteki J# wykonanej równoważnie z oryginalną pętlą for. Używam .SequenceEqual biegnie wokół siedem razy wolniej; myślę, że tylko dlatego, że używa IEnumerator.MoveNext. Wyobrażam sobie, że rozwiązania oparte na LINQ są co najmniej tak powolne lub gorsze.

 68
Author: Hafthor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-17 11:09:30

Jeśli nie masz nic przeciwko temu, możesz zaimportować J # assembly " vjslib.dll " i używać jego Tablice.metoda equals (byte[], byte[]) ...

Nie wiń mnie, jeśli ktoś się z Ciebie śmieje...

EDIT: ile to jest warte, użyłem Reflector do demontażu kodu i oto jak to wygląda:

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;
}
 25
Author: Jason Bunting,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-04 08:03:45

. NET 3.5 i nowsze mają nowy typ publiczny, System.Data.Linq.Binary, który enkapsuluje byte[]. Implementuje IEquatable<Binary>, które (w efekcie) porównuje dwie tablice bajtowe. Zauważ, że System.Data.Linq.Binary ma również implicit conversion operator z byte[].

Dokumentacja MSDN: System.Data.Linq.Binary

Dekompilacja Reflektora metody 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;
}

Interesujące jest to, że przechodzą do pętli porównawczej bajt po bajcie tylko wtedy, gdy hasze dwóch obiektów binarnych są takie same. To jednak przychodzi za cenę obliczenia hasha w konstruktorze obiektów Binary (przez przemieszczenie tablicy za pomocą pętli for: -)).

Powyĺźsza implementacja oznacza, Ĺźe w najgorszym przypadku moĹźna byĹ 'o przeĹ' Ä ... czyÄ ‡ tablice trzy razy: najpierw obliczyÄ ‡ hash tablicy 1, Potem obliczyÄ ‡ hash tablicy 2 i wreszcie (poniewaĹź jest to najgorszy scenariusz, dĹ 'ugoĹ" ci i hasze răłwne) porăłwnaä ‡ bajty w tablicy 1 z bajtami w tablicy 2.

Ogólnie, mimo że System.Data.Linq.Binary jest wbudowany w BCL, nie sądzę, aby jest najszybszym sposobem porównania dwóch tablic bajtowych:|/.

 24
Author: Milan Gardian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-01 13:14:40

Zamieściłem podobne pytanie dotyczące sprawdzania, czy bajt[] jest pełen zer. (Kod SIMD został pobity, więc usunąłem go z tej odpowiedzi.) Oto najszybszy kod z moich porównań:

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

Mierzone na dwóch tablicach 256MB bajtów:

UnsafeCompare                           : 86,8784 ms
EqualBytesSimd                          : 71,5125 ms
EqualBytesSimdUnrolled                  : 73,1917 ms
EqualBytesLongUnrolled                  : 39,8623 ms
 15
Author: ArekBulski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-24 15:40:44

Span<T> oferuje niezwykle konkurencyjną alternatywę bez konieczności rzucania mylącego i / lub nieporównywalnego puchu do bazy kodu własnej aplikacji: {]}

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

Implementację można znaleźć tutaj .

Poprawiłem @EliArbel ' s gist, aby dodać tę metodę jako SpansEqual, upuścić większość mniej interesujących wykonawców w innych benchmarkach, uruchomić ją z różnymi rozmiarami tablic, wykresami wyjściowymi i oznaczyć SpansEqual jako linię bazową, aby raportowała, w jaki sposób różne metody porównują się do SpansEqual.

Poniższe liczby pochodzą z wyników, lekko edytowane, aby usunąć kolumnę "błąd".

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

Byłem zaskoczony, widząc SpansEqual nie wychodzi na szczyt dla metod max-array-size, ale różnica jest tak niewielka, że nie sądzę, że to kiedykolwiek będzie miało znaczenie.

Moje info o systemie:

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
 12
Author: Joe Amenta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-19 12:06:21
 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");
 }
 10
Author: user565710,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-06 16:22:03

Dodajmy jeszcze jedną!

[[6]}niedawno Microsoft wydał specjalny pakiet NuGet, System.Runtime.CompilerServices.Niebezpieczne . Jest wyjątkowy, ponieważ jest napisany w IL i zapewnia funkcje niskiego poziomu, niedostępne bezpośrednio w C#.

Jedna z jego metod, Unsafe.As<T>(object) pozwala na przeniesienie dowolnego typu odniesienia do innego typu odniesienia, pomijając wszelkie kontrole bezpieczeństwa. Jest to zazwyczaj bardzo zły pomysł, ale jeśli oba typy mają tę samą strukturę, może działać. Więc możemy użyj tego, aby rzucić byte[] na 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;
}

Zauważ, że long1.Length nadal zwraca długość oryginalnej tablicy, ponieważ jest ona przechowywana w polu w strukturze pamięci tablicy.

Ta metoda nie jest tak szybka jak inne metody tutaj zademonstrowane, ale jest o wiele szybsza niż metoda naiwna, nie używa niebezpiecznego kodu, P / Invoke czy przypinania, a implementacja jest dość prosta (IMO). Oto niektóre BenchmarkDotNet wyniki z mojego maszyna:

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 |

Stworzyłem również gist ze wszystkimi testami.

 7
Author: Eli Arbel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-03 15:27:29

Użyłbym niebezpiecznego kodu i uruchomiłbym pętlę for porównując wskaźniki Int32.

Może powinieneś również rozważyć sprawdzenie, czy tablice nie są null.

 6
Author: gil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-28 23:42:32

Jeśli spojrzysz na to, jak. NET robi string.Equals, widać, że używa prywatnej metody o nazwie EqualsHelper, która ma implementację wskaźnika "unsafe". . NET Reflector jest twoim przyjacielem, aby zobaczyć, jak rzeczy są wykonywane wewnętrznie.

To może być użyte jako szablon do porównania tablicy bajtów, nad którym wykonałem implementację w poście na bloguszybkie porównanie tablicy bajtów w C#. Zrobiłem też kilka podstawowych benchmarków, aby zobaczyć, kiedy bezpieczna implementacja jest szybsza niż niebezpieczne.

To powiedziawszy, chyba że naprawdę potrzebujesz zabójczej wydajności, wybrałbym proste porównanie pętli fr.

 5
Author: Mikael Svenson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-28 23:43:37

Opracowałem metodę, która lekko bije memcmp() (ODPOWIEDŹ plinth) i bardzo slighly bije EqualBytesLongUnrolled() (odpowiedź Arka Bulskiego). Zasadniczo rozwija pętlę o 4 zamiast 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);
    }
}
To działa o 25% szybciej niż memcmp() i o 5% szybciej niż EqualBytesLongUnrolled() na mojej maszynie.
 5
Author: Mr Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-26 23:01:36

Wydaje się, żeEqualBytesLongUnrolled jest najlepszy z powyższych sugerowanych.

Pominięte metody (Enumerable.Sekwencja Równa, Strukturalna.Strukturalna równość.Równi), nie byli-cierpliwi-dla-powolnych. Na macierzach 265mb zmierzyłem to:

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 |
 3
Author: Motlicek Petr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 12:08:20

Do porównywania tablic krótkich bajtów interesujący jest następujący hack:

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

Wtedy prawdopodobnie wypadłbym na rozwiązanie wymienione w pytaniu.

Interesujące byłoby wykonanie analizy wydajności tego kodu.
 2
Author: Kevin Driedger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-18 15:29:25

Nie mogłem znaleźć rozwiązania, z którego jestem całkowicie zadowolony (rozsądna wydajność, ale bez niebezpiecznego kodu/pinvoke) więc wymyśliłem to, nic naprawdę oryginalnego, ale działa: {]}

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

Wydajność w porównaniu z innymi rozwiązaniami na tej stronie:

Prosta pętla: 19837 kleszczy, 1.00

*BitConverter: 4886, 4.06

Porównaj: 1636 ticks, 12.12

EqualBytesLongUnrolled: 637 kleszczy, 31.09

P / wywołanie memcmp: 369 kleszczy, 53.67

Testowane w linqpad, 1000000 bajtów identycznych tablic (najgorszy scenariusz), po 500 iteracji każda.

 2
Author: Zar Shardan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-31 11:37:54

Myślałem o metodach akceleracji transferu bloków wbudowanych w wiele kart graficznych. Ale wtedy będziesz musiał skopiować wszystkie bajty danych, więc nie pomoże Ci to zbytnio, jeśli nie chcesz zaimplementować całej części swojej logiki w niezarządzanym i zależnym od sprzętu kodzie...

Innym sposobem optymalizacji podobnym do pokazanego powyżej byłoby Przechowywanie jak największej ilości danych w długim [], a nie bajcie [] od samego początku, na przykład, jeśli czytasz sekwencyjnie z pliku binarnego, lub jeśli używasz pliku mapowanego pamięcią, odczytywać dane jako długie[] lub pojedyncze długie wartości. Następnie pętla porównawcza będzie potrzebowała tylko 1/8 liczby iteracji, które musiałaby wykonać dla bajtu [] zawierającego tę samą ilość danych. Chodzi o to, kiedy i jak często trzeba porównywać, a kiedy i jak często trzeba uzyskać dostęp do danych w sposób bajt-by-bajt, np. aby użyć ich w wywołaniu API jako parametru w metodzie, która oczekuje bajtu []. W końcu tylko Ty można powiedzieć, czy naprawdę znasz przypadek użycia...

 1
Author: Mirko Klemm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-07 11:09:32

Zrobiłem kilka pomiarów używając dołączonego programu. Net 4.7 release build bez dołączonego debuggera. Myślę, że ludzie używali niewłaściwej metryki, ponieważ to, o co ci chodzi, jeśli zależy ci na prędkości, to ile czasu zajmuje ustalenie, czy dwie tablice bajtowe są równe. tj. przepustowość w bajtach.

StructuralComparison :       2838.8 MiB/s
for                  :   30553811.0 MiB/s
ToUInt32             :   23864406.8 MiB/s
ToUInt64             :    5526595.7 MiB/s
memcmp               : 1848977556.1 MiB/s

Jak widzisz, nie ma lepszego sposobu niż memcmp i jest o rzędy wielkości szybszy. Prosta pętla for jest drugą najlepszą opcją. I ciągle mi przeszkadza dlaczego Microsoft nie może zawierać po prostu metody Buffer.Compare.

[Program.cs]:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace memcmp
{
    class Program
    {
        static byte[] TestVector(int size)
        {
            var data = new byte[size];
            using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
            {
                rng.GetBytes(data);
            }
            return data;
        }

        static TimeSpan Measure(string testCase, TimeSpan offset, Action action, bool ignore = false)
        {
            var t = Stopwatch.StartNew();
            var n = 0L;
            while (t.Elapsed < TimeSpan.FromSeconds(10))
            {
                action();
                n++;
            }
            var elapsed = t.Elapsed - offset;
            if (!ignore)
            {
                Console.WriteLine($"{testCase,-16} : {n / elapsed.TotalSeconds,16:0.0} MiB/s");
            }
            return elapsed;
        }

        [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern int memcmp(byte[] b1, byte[] b2, long count);

        static void Main(string[] args)
        {
            // how quickly can we establish if two sequences of bytes are equal?

            // note that we are testing the speed of different comparsion methods

            var a = TestVector(1024 * 1024); // 1 MiB
            var b = (byte[])a.Clone();

            var offset = Measure("offset", new TimeSpan(), () => { return; }, ignore: true);

            Measure("StructuralComparison", offset, () =>
            {
                StructuralComparisons.StructuralEqualityComparer.Equals(a, b);
            });

            Measure("for", offset, () =>
            {
                for (int i = 0; i < a.Length; i++)
                {
                    if (a[i] != b[i]) break;
                }
            });

            Measure("ToUInt32", offset, () =>
            {
                for (int i = 0; i < a.Length; i += 4)
                {
                    if (BitConverter.ToUInt32(a, i) != BitConverter.ToUInt32(b, i)) break;
                }
            });

            Measure("ToUInt64", offset, () =>
            {
                for (int i = 0; i < a.Length; i += 8)
                {
                    if (BitConverter.ToUInt64(a, i) != BitConverter.ToUInt64(b, i)) break;
                }
            });

            Measure("memcmp", offset, () =>
            {
                memcmp(a, b, a.Length);
            });
        }
    }
}
 1
Author: John Leidegren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-28 14:23:02

Sorry, jeśli szukasz zarządzanego sposobu, to już robisz to poprawnie i według mojej wiedzy nie ma wbudowanej metody w BCL do tego.

Powinieneś dodać kilka początkowych sprawdzeń null, a następnie ponownie użyć go tak, jakby był w BCL.

 0
Author: Markus Olsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-04 07:45:49

Jest to prawie na pewno dużo wolniejsze niż jakakolwiek inna wersja podana tutaj, ale pisanie było zabawne.

static bool ByteArrayEquals(byte[] a1, byte[] a2) 
{
    return a1.Zip(a2, (l, r) => l == r).All(x => x);
}
 0
Author: James Curran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 23:23:43

Nie widziałem tu wielu rozwiązań linq.

Nie jestem pewien wpływu na wydajność, jednak generalnie trzymam się linq jako zasady, a następnie optymalizacji później, jeśli to konieczne.

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

Należy pamiętać, że działa to tylko wtedy, gdy są to tablice o tym samym rozmiarze. rozszerzenie może wyglądać tak

public bool CompareTwoArrays(byte[] array1, byte[] array2)
 {
   if (array1.Length != array2.Length) return false;
   return !array1.Where((t, i) => t != array2[i]).Any();
 }
 0
Author: Zapnologica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-02 06:53:46

Zdecydowałem się na rozwiązanie inspirowane metodą EqualBytesLongUnrolled opublikowaną przez Arekbulskiego z dodatkową optymalizacją. W moim przypadku różnice tablic w tablicach wydają się być blisko ogona tablic. Podczas testów odkryłem, że w przypadku dużych tablic możliwość porównywania elementów tablicy w odwrotnej kolejności daje temu rozwiązaniu ogromny wzrost wydajności w porównaniu z rozwiązaniem opartym na memcmp. Oto rozwiązanie:

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;
}
 0
Author: Casey Chester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-26 14:07:04

Użyj SequenceEquals do porównania.

 -1
Author: API_Base,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-23 20:19:09

Krótka odpowiedź brzmi:

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

W ten sposób można użyć zoptymalizowanego. NET string compare, aby porównać tablicę bajtów bez konieczności pisania niebezpiecznego kodu. Tak to się robi w tle :

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);
    }
}
 -1
Author: Alon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-04 17:48:24

Ponieważ wiele z powyższych fantazyjnych rozwiązań nie działa z UWP, a ponieważ kocham Linq i podejścia funkcjonalne, naciskam na moją wersję tego problemu. Aby uniknąć porównania, gdy pojawi się pierwsza różnica, wybrałem .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);
 -1
Author: Raymond Osterbrink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-07 14:43:39

Rodzaj brutalnej siły, ale jest to proste, aby przekonwertować tablicę bajtów na ciąg base64 i porównać dwa ciągi. Przydatne, jeśli masz duże tablice do porównania. Lub jeśli jedna z tablic bajtów jest już w formacie 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
}

Wyobrażam sobie, że najszybszym sposobem (z najlepszą wydajnością dla dużych tablic) jest hashowanie obu tablic bajtowych i porównywanie ich.

 -1
Author: Timothy John Laird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 13:09:25

Jeśli szukasz bardzo szybkiego porównywania równości w tablicy bajtów, proponuję rzucić okiem na ten artykuł STSdb Labs: porównywanie równości w tablicy bajtów. zawiera jedne z najszybszych implementacji porównywania równości bajtów [], które są prezentowane, testowane i podsumowane.

Możesz również skupić się na tych implementacjach:

BigEndianByteArrayComparer - Fast byte[] array comparer from left to right (BigEndian) BigEndianByteArrayEqualityComparer - - fast byte [] equality comparer od lewej do prawej (BigEndian) LittleEndianByteArrayComparer - Fast byte[] array comparer from right to left (LittleEndian) LittleEndianByteArrayEqualityComparer - fast byte[] equality comparer from right to left (LittleEndian)

 -2
Author: Kris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-02 11:07:46

W przypadku, gdy masz dużą tablicę bajtów, możesz je porównać, konwertując je na string.

Możesz użyć czegoś takiego jak

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

Użyłem tego i widziałem ogromny wpływ na wydajność.

 -5
Author: pratikv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-11 14:18:50