Membandingkan dua byte array di .NET

541

Bagaimana saya bisa melakukan ini dengan cepat?

Tentu saya bisa melakukan ini:

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

Tapi saya sedang mencari fungsi BCL atau beberapa cara yang terbukti sangat optimal untuk melakukan ini.

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

berfungsi dengan baik, tetapi sepertinya itu tidak akan berfungsi untuk x64.

Perhatikan jawaban super cepat saya di sini .

Hafthor
sumber
1
"Ini agak mengandalkan fakta bahwa array mulai disejajarkan dengan qword." Itu besar jika. Anda harus memperbaiki kode untuk mencerminkannya.
Joe Chung
4
return a1.Length == a2.Length &&! a1.Where ((t, i) => t! = a2 [i]). Any ();
alerya
Saya menyukai jawaban @OhadSchneider tentangIStructuralEquatable
LCJ

Jawaban:

613

Anda dapat menggunakan metode 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

Jika Anda tidak dapat menggunakan .NET 3.5 karena alasan tertentu, metode Anda OK.
Lingkungan compiler \ run-time akan mengoptimalkan loop Anda sehingga Anda tidak perlu khawatir tentang kinerja.

aku
sumber
4
Tapi bukankah SequenceEqual butuh waktu lebih lama untuk diproses daripada perbandingan yang tidak aman? Terutama ketika Anda melakukan 1000 perbandingan?
kabel
90
Ya, ini berjalan sekitar 50x lebih lambat dari perbandingan yang tidak aman.
Hafthor
27
Ini benar-benar membangkitkan orang mati di sini, tetapi lambat adalah kata yang buruk untuk digunakan di sini. 50x lebih lambat terdengar buruk, tetapi tidak sering Anda membandingkan data yang cukup untuk membuat perbedaan, dan jika ya, Anda benar-benar perlu membandingkan ini untuk kasus Anda sendiri, untuk berbagai alasan. Misalnya, perhatikan pembuat jawaban tidak aman mencatat perbedaan 7x lambat, dibandingkan dengan 50x lebih lambat (kecepatan metode tidak aman juga tergantung pada penyelarasan data). Dalam kasus yang jarang terjadi di mana angka-angka ini penting, P / Invoke bahkan lebih cepat.
Selali Adobor
4
Jadi implementasi yang lebih lambat mendapatkan lebih dari 300 suka? Saya sarankan mengaitkan msvcrt.dll karena itu akan menjadi cara tercepat untuk menyelesaikan pekerjaan.
TGarrett
69
Tercepat bukanlah hal yang paling penting bagi bisnis. Maintainability jauh "lebih cepat" daripada penghematan pada kode ini akan berjumlah 99% dari kasus. Saya menggunakan SequenceEqual dan seluruh kode saya <1ms. Μs yang Anda simpan tidak akan pernah menambah hingga 5 menit kurangnya keterbacaan P / Invoke.
PRMan
236

P / aktifkan kekuatan!

[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;
}
alas tiang
sumber
48
P / Panggil yaay - ini terbukti paling cepat sejauh ini di bitmap setidaknya: stackoverflow.com/questions/2031217/…
Erik Forbes
25
Pinning tidak diperlukan dalam hal ini. Marshaller melakukan pinning otomatis ketika memanggil kode asli dengan PInvoke. Referensi: stackoverflow.com/questions/2218444/…
Mark Glasgow
14
P / Invoke mungkin menimbulkan boos tetapi sejauh ini yang tercepat dari semua solusi yang disajikan, termasuk implementasi yang saya buat dengan menggunakan perbandingan ukuran pointer yang tidak aman. Ada beberapa optimasi yang dapat Anda lakukan sebelum memanggil kode asli termasuk persamaan referensi dan membandingkan elemen pertama dan terakhir.
Josh
38
Kenapa boo? Poster menginginkan implementasi yang cepat dan perbandingan bahasa assembly yang dioptimalkan tidak dapat dikalahkan. Saya tidak tahu bagaimana cara mendapatkan "REPE CMPSD" dari. NET tanpa P / INVOKE.
Jason Goemaat
14
Nitpick: MSVCR.dll tidak seharusnya digunakan oleh kode pengguna. Untuk menggunakan MSVCR, Anda harus mendistribusikan runtime menggunakan versi yang Anda distribusikan. ( msdn.microsoft.com/en-us/library/… dan blogs.msdn.com/b/oldnewthing/archive/2014/04/11/10516280.aspx )
Mitch
160

Ada solusi bawaan baru untuk ini di .NET 4 - IStructuralEquatable

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
    return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2);
}
Ohad Schneider
sumber
17
Menurut posting blog ini sebenarnya sangat lambat.
Matt Johnson-Pint
48
Gila lambat. Sekitar 180x lebih lambat dari loop sederhana.
Hafthor
Ini bekerja, tetapi saya tidak mengerti mengapa. Byte [] adalah tipe primitif yang tidak mengimplementasikan IStructuralEquatable, jadi mengapa Anda dapat melemparkannya - dan pemain implisit pada saat itu! Dan kemudian antarmuka "Persamaan" metode ajaib tersedia ... di mana implementasi metode itu berasal? Bisakah seseorang memberi tahu saya?
Josh
1
Kenapa tidak adil StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)? Tidak ada NullReferenceExceptiondi sini
ta.speot.is
1
@ ta.speot.is Terima kasih, Tidak dapat berdebat dengan satu liner! Solusi sebelumnya sedikit lebih efisien karena menyelamatkan para pemain ke IStructuralEquatable (array secara statis dikenal sebagai IStructuralEquatable), tetapi memang saran Anda membuat metode ini berfungsi untuk argumen nol juga.
Ohad Schneider
76

Pengguna gil menyarankan kode tidak aman yang menghasilkan solusi ini:

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

yang melakukan perbandingan berbasis 64-bit untuk sebanyak mungkin array. Semacam ini diperhitungkan pada fakta bahwa array mulai selaras qword. Ini akan bekerja jika tidak selaras qword, hanya saja tidak secepat itu.

Ia melakukan sekitar tujuh timer lebih cepat dari forloop sederhana . Menggunakan perpustakaan J # dilakukan secara setara dengan forloop asli . Menggunakan .SequenceEqual berjalan sekitar tujuh kali lebih lambat; Saya pikir hanya karena menggunakan IEnumerator.MoveNext. Saya membayangkan solusi berbasis LINQ setidaknya yang lambat atau lebih buruk.

Hafthor
sumber
3
Solusi yang bagus. Tapi satu petunjuk (kecil): Perbandingan jika referensi a1 dan a2 sama dapat mempercepat hal-hal jika seseorang memberikan array yang sama untuk a1 dan b1.
mmmmmmmm
12
Data uji baru pada .NET 4 x64 rilis: IStructualEquatable.equals ~ 180x lebih lambat, SequenceEqual 15x lebih lambat, SHA1 hash membandingkan 11x lebih lambat, bitconverter ~ sama, tidak aman 7x lebih cepat, pinvoke 11x lebih cepat. Cukup keren bahwa tidak aman hanya sedikit lebih lambat dari P / Invoke pada memcmp.
Hafthor
3
Tautan ini memberikan perincian yang bagus tentang mengapa penyelarasan memori penting ibm.com/developerworks/library/pa-dalign - jadi, pengoptimalan bisa untuk memeriksa penyelarasan dan jika kedua array tidak sejajar dengan jumlah yang sama, lakukan perbandingan byte hingga keduanya pada batas qword.
Hafthor
5
bukankan ini memberikan false ketika a1 dan a2 adalah null?
nawfal
2
@CristiDiaconescu Saya mengulangi jawaban KevinDriedger. Apa yang mungkin harus saya lakukan adalah membuat test suite dan hasil saya tersedia di github dan menghubungkannya dengan jawaban saya.
Hafthor
74

Span<T> menawarkan alternatif yang sangat kompetitif tanpa harus membuang bulu yang membingungkan dan / atau tidak portabel ke dalam basis kode aplikasi Anda sendiri:

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

Implementasi (nyali dari) pada .NET Core 3.1.0 dapat ditemukan di sini .

Saya telah merevisi inti @ EliArbel untuk menambahkan metode ini sebagai SpansEqual, letakkan sebagian besar pemain yang kurang menarik di tolok ukur orang lain, jalankan dengan berbagai ukuran array, grafik output, dan tandai SpansEqualsebagai garis dasar sehingga melaporkan bagaimana metode yang berbeda dibandingkan dengan SpansEqual.

Angka-angka di bawah ini dari hasil, diedit dengan ringan untuk menghapus kolom "Kesalahan".

|        Method |  ByteCount |               Mean |            StdDev | Ratio |
|-------------- |----------- |-------------------:|------------------:|------:|
|    SpansEqual |         15 |           3.562 ns |         0.0035 ns |  1.00 |
|  LongPointers |         15 |           4.611 ns |         0.0028 ns |  1.29 |
|      Unrolled |         15 |          18.035 ns |         0.0195 ns |  5.06 |
| PInvokeMemcmp |         15 |          11.210 ns |         0.0353 ns |  3.15 |
|               |            |                    |                   |       |
|    SpansEqual |       1026 |          20.048 ns |         0.0286 ns |  1.00 |
|  LongPointers |       1026 |          63.347 ns |         0.1062 ns |  3.16 |
|      Unrolled |       1026 |          39.175 ns |         0.0304 ns |  1.95 |
| PInvokeMemcmp |       1026 |          40.830 ns |         0.0350 ns |  2.04 |
|               |            |                    |                   |       |
|    SpansEqual |    1048585 |      44,070.526 ns |        35.3348 ns |  1.00 |
|  LongPointers |    1048585 |      59,973.407 ns |        80.4145 ns |  1.36 |
|      Unrolled |    1048585 |      55,032.945 ns |        24.4745 ns |  1.25 |
| PInvokeMemcmp |    1048585 |      55,593.719 ns |        22.4301 ns |  1.26 |
|               |            |                    |                   |       |
|    SpansEqual | 2147483591 | 253,648,180.000 ns | 1,112,524.3074 ns |  1.00 |
|  LongPointers | 2147483591 | 249,412,064.286 ns | 1,079,409.5670 ns |  0.98 |
|      Unrolled | 2147483591 | 246,329,091.667 ns |   852,021.7992 ns |  0.97 |
| PInvokeMemcmp | 2147483591 | 247,795,940.000 ns | 3,390,676.3644 ns |  0.98 |

Saya terkejut melihat SpansEqualtidak keluar di atas untuk metode max-array-size, tetapi perbedaannya sangat kecil sehingga saya tidak berpikir itu akan menjadi masalah.

Info sistem saya:

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i7-6850K CPU 3.60GHz (Skylake), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=3.1.100
  [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
  DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
Joe Amenta
sumber
Saya tidak pernah berpikir saya akan menggunakan Rentang <T> atau sesuatu yang dekat dengannya dalam semua hal yang saya lakukan. Terima kasih kepada Anda, saya sekarang dapat membual tentang hal ini kepada rekan kerja saya.
jokab
Apakah SequenceEqual diimplementasikan secara khusus sebagai metode Span? Kupikir itu hanya salah satu metode ekstensi IEnumerable.
Zastai
1
@ Zastai ya, {ReadOnly,}Span<T>memiliki versi sendiri SequenceEqual(nama yang sama karena memiliki kontrak yang sama dengan IEnumerable<T>metode ekstensi yang sesuai , hanya saja lebih cepat). Perhatikan bahwa {ReadOnly,}Span<T>tidak dapat menggunakan IEnumerable<T>metode ekstensi karena pembatasan ref structjenis.
Joe Amenta
1
@Sentinel paket System.Memory memiliki Span<T>implementasi "portabel" / "lambat" untuk netstandard1.1dan di atasnya (jadi mainkan dengan bagan interaktif ini untuk melihat yang mana). "Cepat" Span<T>hanya tersedia di .NET Core 2.1, pada saat ini, tetapi perhatikan bahwa secara SequenceEqual<T>khusus, harus ada sedikit perbedaan antara "cepat" dan "lambat" / "portabel" (meskipun netstandard2.0target akan melihat sedikit peningkatan karena mereka memiliki jalur kode vektor).
Joe Amenta
1
install-package system.memory
Chris Moschini
30

Jika Anda tidak menentang untuk melakukannya, Anda dapat mengimpor rakitan J # "vjslib.dll" dan menggunakan metode Arrays.equals (byte [], byte []) ...

Jangan salahkan saya jika seseorang menertawakan Anda ...


EDIT: Untuk apa nilainya sedikit, saya menggunakan Reflector untuk membongkar kode untuk itu, dan di sini seperti apa itu:

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

.NET 3.5 dan yang lebih baru memiliki tipe publik baru, System.Data.Linq.Binaryyang merangkum byte[]. Ini mengimplementasikan IEquatable<Binary>bahwa (efeknya) membandingkan array dua byte. Perhatikan bahwa System.Data.Linq.Binaryoperator konversi implisit juga berasal dari byte[].

Dokumentasi MSDN: System.Data.Linq.Binary

Dekompilasi reflektor dari metode 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;
}

Sentuhan yang menarik adalah bahwa mereka hanya melanjutkan ke loop perbandingan byte-by-byte jika hash dari dua objek Biner adalah sama. Ini, bagaimanapun, datang pada biaya komputasi hash dalam konstruktor Binaryobjek (dengan melintasi array dengan forloop :-)).

Implementasi di atas berarti bahwa dalam kasus terburuk Anda mungkin harus melintasi array tiga kali: pertama untuk menghitung hash dari array1, kemudian untuk menghitung hash dari array2 dan akhirnya (karena ini adalah skenario terburuk, panjang dan hash sama) untuk membandingkan byte dalam array1 dengan byte dalam array 2.

Secara keseluruhan, meskipun System.Data.Linq.Binarydibangun ke dalam BCL, saya tidak berpikir itu adalah cara tercepat untuk membandingkan array dua byte: - |.

Milan Gardian
sumber
20

Saya memposting pertanyaan serupa tentang memeriksa apakah byte [] penuh dengan nol. (Kode SIMD dipukuli jadi saya menghapusnya dari jawaban ini.) Berikut adalah kode tercepat dari perbandingan saya:

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

Diukur pada dua array byte 256MB:

UnsafeCompare                           : 86,8784 ms
EqualBytesSimd                          : 71,5125 ms
EqualBytesSimdUnrolled                  : 73,1917 ms
EqualBytesLongUnrolled                  : 39,8623 ms
ArekBulski
sumber
1
Saya mengkonfirmasi. Saya juga menjalankan tes. Ini lebih cepat daripada jawaban yang menggunakan memcmp panggilan tidak aman.
ujeenator
1
@ AmberdeBlack Apakah Anda yakin? Apakah Anda menguji dengan array kecil?
Zar Shardan
@ ArekBulski Apakah Anda yakin ini lebih cepat dari memcmp, karena pengujian saya menunjukkan sebaliknya?
Zar Shardan
Saya mendapatkan kinerja yang hampir identik antara ini dan memcmp, jadi +1 untuk solusi yang sepenuhnya dikelola.
Mike Marynowski
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");
 }
pengguna565710
sumber
1
Itu yang saya gunakan. Tapi itu umm ... terdengar seperti perbandingan berurutan yang seharusnya Anda lakukan menggunakan loop sederhana, karenanya tidak terlalu cepat. Akan bagus untuk merefleksikannya dan melihat apa yang sebenarnya dilakukan. Dilihat dari namanya, tidak ada yang mewah.
Sergey Akopov
1
Ya, tetapi sudah disebutkan dalam jawaban yang diterima. btw, Anda bisa menghapus spesifikasi tipe di sana.
nawfal
10

Mari tambahkan satu lagi!

Baru-baru ini Microsoft merilis paket NuGet khusus, System.Runtime.CompilerServices.Unsafe . Ini istimewa karena ditulis dalam IL , dan menyediakan fungsionalitas tingkat rendah yang tidak tersedia secara langsung dalam C #.

Salah satu metodenya, Unsafe.As<T>(object)memungkinkan casting jenis referensi apa pun ke jenis referensi lain, melewatkan pemeriksaan keamanan apa pun. Ini biasanya ide yang sangat buruk, tetapi jika kedua jenis memiliki struktur yang sama, itu bisa berhasil. Jadi kita bisa menggunakan ini untuk melemparkan byte[]ke 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;
}

Perhatikan bahwa long1.Lengthmasih akan mengembalikan panjang array asli, karena itu disimpan dalam bidang dalam struktur memori array.

Metode ini tidak secepat metode lain yang ditunjukkan di sini, tetapi jauh lebih cepat daripada metode naif, tidak menggunakan kode yang tidak aman atau P / Invoke atau pinning, dan implementasinya cukup mudah (IMO). Berikut adalah beberapa hasil BenchmarkDotNet dari mesin saya:

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 |

Saya juga membuat intisari dengan semua tes .

Eli Arbel
sumber
Itu tidak menggunakan kata kunci yang tidak aman, namun ia memanggil kode yang tidak aman dengan menggunakan System.Runtime.CompilerServices.Unsafe
Paulo Zemek
Saya telah memperbarui NewMemCmpjawaban saya untuk menggunakan AVX-2
Tn. Anderson
8

Saya mengembangkan metode yang sedikit mengalahkan memcmp()(jawaban alas) dan ketukan sangat lambat EqualBytesLongUnrolled()(jawaban Arek Bulski) pada PC saya. Pada dasarnya, ini membuka gulungannya dengan 4 bukannya 8.

Pembaruan 30 Maret 2019 :

Dimulai dengan .NET core 3.0, kami memiliki dukungan SIMD!

Solusi ini tercepat dengan selisih yang cukup besar pada PC saya:

#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;
}
Tuan Anderson
sumber
Pengukuran saya berbeda untuk .NET 462 dapat NETCORE:
Motlicek Petr
Kode macet ketika membandingkan dua array 0-panjang, karena menyematkan pengembalian null.
Glenn Slayden
memcmp bukan hanya pembanding ekuitas. Ini memberikan informasi objek mana yang lebih besar atau lebih kecil. Bisakah Anda mengadopsi algoritme Anda untuk tujuan ini dan memeriksa kinerjanya?
nicolay.anykienko
Apakah lebih cepat dari Spandan memcpy?
Silkfire
@ silkfire Pada .NET core 3 dan CPU modern, seharusnya 2-3 kali lebih cepat untuk array besar.
Tn. Anderson
6

Saya akan menggunakan kode yang tidak aman dan menjalankan forloop membandingkan pointer Int32.

Mungkin Anda juga harus mempertimbangkan memeriksa array menjadi non-null.

gil
sumber
5

Jika Anda melihat bagaimana .NET melakukan string.Equals, Anda melihat bahwa itu menggunakan metode pribadi yang disebut EqualsHelper yang memiliki implementasi pointer "tidak aman". .NET Reflector adalah teman Anda untuk melihat bagaimana berbagai hal dilakukan secara internal.

Ini dapat digunakan sebagai template untuk perbandingan array byte yang saya lakukan implementasi di dalam posting blog perbandingan array byte cepat di C # . Saya juga melakukan beberapa tolok ukur yang belum sempurna untuk melihat kapan implementasi yang aman lebih cepat daripada yang tidak aman.

Yang mengatakan, kecuali Anda benar-benar membutuhkan kinerja pembunuh, saya akan pergi untuk perbandingan fr loop sederhana.

Mikael Svenson
sumber
3

Tidak dapat menemukan solusi yang benar-benar saya sukai (kinerja yang wajar, tetapi tidak ada kode / pinvoke yang tidak aman) jadi saya datang dengan ini, tidak ada yang benar-benar asli, tetapi berfungsi:

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

Kinerja dibandingkan dengan beberapa solusi lain di halaman ini:

Simple Loop: 19837 ticks, 1.00

* BitConverter: 4886 ticks, 4.06

Tidak Aman Membandingkan: 1636 ticks, 12.12

EqualBytesLongUnrolled: 637 ticks, 31.09

P / Invoke memcmp: 369 ticks, 53.67

Diuji dalam linqpad, 1000000 byte array identik (skenario kasus terburuk), masing-masing 500 iterasi.

Zar Shardan
sumber
ya, saya mencatat bahwa dalam komentar stackoverflow.com/a/1445280/4489 bahwa pengujian saya menunjukkan ini sebenarnya sedikit lebih lambat daripada sederhana untuk loop yang saya miliki dalam pertanyaan asli.
Hafthor
apakah kamu yakin Dalam pengujian saya ini 4 kali lebih cepat? Tidak ada yang mengalahkan kode asli lama yang baik, bahkan dengan biaya overhead marshaling.
Zar Shardan
3

Tampaknya EqualBytesLongUnrolled adalah yang terbaik dari yang disarankan di atas.

Metode yang dilewati (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals), tidak sabar untuk lambat. Pada array 265MB saya telah mengukur ini:

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
sumber
Saya telah memperbarui NewMemCmpjawaban saya untuk menggunakan AVX-2
Tn. Anderson
3

Saya belum melihat banyak solusi LINQ di sini.

Saya tidak yakin dengan implikasi kinerja, namun saya umumnya tetap berpegang pada linqaturan praktis dan kemudian mengoptimalkan nanti jika perlu.

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

Harap dicatat ini hanya berfungsi jika array ukurannya sama. ekstensi bisa terlihat seperti itu

public bool CompareTwoArrays(byte[] array1, byte[] array2)
 {
   if (array1.Length != array2.Length) return false;
   return !array1.Where((t, i) => t != array2[i]).Any();
 }
Zapnologica
sumber
Inti dari pertanyaan adalah solusi yang lebih cepat dari fungsi yang diposting dalam pertanyaan.
CodesInChaos
3

Saya melakukan beberapa pengukuran menggunakan program terlampir. Net 4.7 rilis build tanpa debugger terpasang. Saya pikir orang telah menggunakan metrik yang salah karena apa yang Anda tentang jika Anda peduli tentang kecepatan di sini adalah berapa lama waktu yang dibutuhkan untuk mengetahui apakah array dua byte sama. yaitu throughput dalam byte.

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

Seperti yang Anda lihat, tidak ada cara yang lebih baik daripada memcmpdan ini perintah besarnya lebih cepat. Simpul sederhana foradalah pilihan terbaik kedua. Dan itu masih mengejutkan saya mengapa Microsoft tidak bisa begitu saja memasukkan Buffer.Comparemetode.

[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
sumber
2

Untuk membandingkan array byte pendek, berikut ini adalah hack yang menarik:

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

Maka saya mungkin akan jatuh ke solusi yang tercantum dalam pertanyaan.

Akan menarik untuk melakukan analisis kinerja kode ini.

Kevin Driedger
sumber
int i = 0; untuk (; i <a1.Length-7; i + = 8) jika (BitConverter.ToInt64 (a1, i)! = BitConverter.ToInt64 (a2, i)) return false; untuk (; i <a1.Length; i ++) jika (a1 [i]! = a2 [i]) mengembalikan false; kembali benar; // sedikit lebih lambat daripada sederhana untuk loop.
Hafthor
2

Bagi Anda yang peduli pesanan (yaitu ingin Anda memcmpmengembalikan yang intseharusnya bukan apa-apa), .NET Core 3.0 (dan mungkin .NET Standard 2.1 alias .NET 5.0) akan mencakup Span.SequenceCompareTo(...)metode ekstensi (plus a Span.SequenceEqualTo) yang dapat digunakan untuk membandingkan dua ReadOnlySpan<T>instance ( where T: IComparable<T>).

Dalam proposal GitHub asli , diskusi termasuk perbandingan pendekatan dengan perhitungan tabel lompatan, membaca byte[]as long[], penggunaan SIMD, dan p / invoke ke implementasi CLR memcmp.

Ke depan, ini harus Anda pergi-metode untuk membandingkan array byte atau rentang byte (sebagai harus menggunakan Span<byte>bukan byte[]untuk Anda NET Standard 2.1 API), dan itu cukup cukup cepat bahwa Anda harus hati tidak lagi tentang cara mengoptimalkan itu (dan tidak, meskipun ada kesamaan dalam namanya, itu tidak berkinerja sangat buruk Enumerable.SequenceEqual).

#if NETCOREAPP3_0
// Using the platform-native Span<T>.SequenceEqual<T>(..)
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
Mahmoud Al-Qudsi
sumber
1

Saya berpikir tentang metode akselerasi blok-transfer yang dibangun ke banyak kartu grafis. Tetapi kemudian Anda harus menyalin semua data byte-bijaksana, jadi ini tidak banyak membantu Anda jika Anda tidak ingin menerapkan seluruh bagian dari logika Anda dalam kode yang tidak dikelola dan tergantung perangkat keras ...

Cara optimasi lain yang mirip dengan pendekatan yang ditunjukkan di atas adalah menyimpan sebanyak mungkin data Anda dalam [] daripada byte [] sejak awal, misalnya jika Anda membacanya secara berurutan dari file biner, atau jika Anda menggunakan file yang dipetakan memori, baca data selama [] atau nilai tunggal tunggal. Kemudian, loop perbandingan Anda hanya perlu 1/8 dari jumlah iterasi yang harus dilakukan untuk byte [] yang berisi jumlah data yang sama. Ini adalah masalah kapan dan seberapa sering Anda perlu membandingkan vs kapan dan seberapa sering Anda perlu mengakses data dengan cara byte-by-byte, misalnya untuk menggunakannya dalam panggilan API sebagai parameter dalam metode yang mengharapkan satu byte []. Pada akhirnya, Anda hanya dapat mengetahui apakah Anda benar-benar mengetahui kasus penggunaan ...

Mirko Klemm
sumber
Jawaban yang diterima menampilkan buffer byte sebagai buffer panjang dan membandingkannya seperti yang Anda jelaskan.
Hafthor
1

Ini hampir pasti jauh lebih lambat daripada versi lain yang diberikan di sini, tapi itu menyenangkan untuk ditulis.

static bool ByteArrayEquals(byte[] a1, byte[] a2) 
{
    return a1.Zip(a2, (l, r) => l == r).All(x => x);
}
James Curran
sumber
1

Saya memilih solusi yang terinspirasi oleh metode EqualBytesLongUnrolled yang diposting oleh ArekBulski dengan optimasi tambahan. Dalam contoh saya, perbedaan array dalam array cenderung dekat dengan ujung array. Dalam pengujian, saya menemukan bahwa ketika ini adalah kasus untuk array besar, mampu membandingkan elemen array dalam urutan terbalik memberikan solusi ini keuntungan kinerja yang sangat besar dibandingkan solusi berbasis memcmp. Inilah solusinya:

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

Maaf, jika Anda mencari cara yang dikelola, Anda sudah melakukannya dengan benar dan setahu saya tidak ada metode bawaan di BCL untuk melakukan ini.

Anda harus menambahkan beberapa cek nol awal dan kemudian menggunakannya kembali seolah-olah itu di BCL.

Markus Olsson
sumber
Anda benar ketika Anda menulis itu, namun pada 2010 (.NET 4.0) metode BCL datang, lihat jawaban Ohad Schneider. Pada saat pertanyaan, .NET 3.5 memiliki Linq (lihat jawaban aku).
Jeppe Stig Nielsen
-1

Gunakan SequenceEqualsuntuk perbandingan ini.

API_Base
sumber
-2

Jika Anda mencari pembanding kesetaraan array byte yang sangat cepat, saya sarankan Anda melihat artikel Lab STSdb ​​ini: pembanding kesetaraan array byte.Ini menampilkan beberapa implementasi tercepat untuk membandingkan kesetaraan array byte [], yang disajikan, diuji kinerja dan dirangkum.

Anda juga dapat fokus pada implementasi ini:

BigEndianByteArrayComparer - byte cepat [] array pembanding dari kiri ke kanan (BigEndian) BigEndianByteArrayEqualityComparer - - byte cepat [] pembanding kesetaraan dari kiri ke kanan (BigEndian) LittleEndianByteArrayComparer - byte cepat [sedikit] array bit dari array ke kananLebih cepat [] pembanding kesetaraan dari kanan ke kiri (LittleEndian)

Keris
sumber
-2

Jawaban singkatnya adalah ini:

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

Sedemikian rupa Anda dapat menggunakan .NET string yang dioptimalkan untuk membuat perbandingan byte byte tanpa perlu menulis kode yang tidak aman. Ini adalah bagaimana hal itu dilakukan di latar belakang :

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
sumber
Dalam pengujian saya, konversi ke string menghancurkan keuntungan dari perbandingan yang lebih cepat. Ini sekitar 2,5 kali lebih lambat daripada loop sederhana.
Doug Clutter
Ketika saya melakukan hal yang sama sederhana untuk sekitar 8 kali lebih lambat. Bisakah Anda menulis kode Anda di sini?
Alon
1
Apakah ini akan pecah jika byte berisi nilai nol (0)?
Joseph Lennox
-1 Selain menjadi lambat karena konversi ke string seperti yang ditunjukkan oleh @DougClutter, ini akan gagal jika array byte berisi data non-ASCII. Untuk mendapatkan hasil yang benar perlu menggunakan iso-8859-1.
Joe
2
Compare(new byte[]{128}, new byte[]{ 255 }) == truesama sekali tidak buggy ...
CodesInChaos
-2

Karena banyak dari solusi mewah di atas tidak bekerja dengan UWP dan karena saya suka Linq dan pendekatan fungsional saya menekan Anda versi saya untuk masalah ini. Untuk menghindari perbandingan ketika perbedaan pertama terjadi, saya memilih .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
sumber
-1 karena kode ini rusak dan tampaknya belum diuji. Ini melempar IndexOutOfRangeExceptionketika membandingkan array yang tidak kosong karena Anda mengakses elemen 1melalui ba0.Lengthkapan harus 0melalui ba0.Length - 1. Jika Anda memperbaikinya dengan Enumerable.Range(0, ba0.Length)itu masih salah mengembalikan trueuntuk array dengan panjang yang sama di mana hanya elemen pertama berbeda karena Anda tidak dapat membedakan antara elemen pertama yang memuaskan predicatedan tidak ada elemen yang memuaskan predicate; FirstOrDefault<int>()kembali 0dalam kedua kasus.
BACON
Pelajaran di sini anak-anak: jangan membawa pisau ke pertarungan senjata
Richard Hauer