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 .
IStructuralEquatable
Jawaban:
Anda dapat menggunakan metode Enumerable.SequenceEqual .
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.
sumber
P / aktifkan kekuatan!
sumber
Ada solusi bawaan baru untuk ini di .NET 4 - IStructuralEquatable
sumber
StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)
? Tidak adaNullReferenceException
di siniPengguna gil menyarankan kode tidak aman yang menghasilkan solusi ini:
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
for
loop sederhana . Menggunakan perpustakaan J # dilakukan secara setara denganfor
loop 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.sumber
Span<T>
menawarkan alternatif yang sangat kompetitif tanpa harus membuang bulu yang membingungkan dan / atau tidak portabel ke dalam basis kode aplikasi Anda sendiri: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 tandaiSpansEqual
sebagai garis dasar sehingga melaporkan bagaimana metode yang berbeda dibandingkan denganSpansEqual
.Angka-angka di bawah ini dari hasil, diedit dengan ringan untuk menghapus kolom "Kesalahan".
Saya terkejut melihat
SpansEqual
tidak keluar di atas untuk metode max-array-size, tetapi perbedaannya sangat kecil sehingga saya tidak berpikir itu akan menjadi masalah.Info sistem saya:
sumber
{ReadOnly,}Span<T>
memiliki versi sendiriSequenceEqual
(nama yang sama karena memiliki kontrak yang sama denganIEnumerable<T>
metode ekstensi yang sesuai , hanya saja lebih cepat). Perhatikan bahwa{ReadOnly,}Span<T>
tidak dapat menggunakanIEnumerable<T>
metode ekstensi karena pembatasanref struct
jenis.Span<T>
implementasi "portabel" / "lambat" untuknetstandard1.1
dan 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 secaraSequenceEqual<T>
khusus, harus ada sedikit perbedaan antara "cepat" dan "lambat" / "portabel" (meskipunnetstandard2.0
target akan melihat sedikit peningkatan karena mereka memiliki jalur kode vektor).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:
sumber
.NET 3.5 dan yang lebih baru memiliki tipe publik baru,
System.Data.Linq.Binary
yang merangkumbyte[]
. Ini mengimplementasikanIEquatable<Binary>
bahwa (efeknya) membandingkan array dua byte. Perhatikan bahwaSystem.Data.Linq.Binary
operator konversi implisit juga berasal daribyte[]
.Dokumentasi MSDN: System.Data.Linq.Binary
Dekompilasi reflektor dari metode Equals:
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
Binary
objek (dengan melintasi array denganfor
loop :-)).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.Binary
dibangun ke dalam BCL, saya tidak berpikir itu adalah cara tercepat untuk membandingkan array dua byte: - |.sumber
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:
Diukur pada dua array byte 256MB:
sumber
sumber
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 melemparkanbyte[]
kelong[]
:Perhatikan bahwa
long1.Length
masih 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:
Saya juga membuat intisari dengan semua tes .
sumber
NewMemCmp
jawaban saya untuk menggunakan AVX-2Saya mengembangkan metode yang sedikit mengalahkan
memcmp()
(jawaban alas) dan ketukan sangat lambatEqualBytesLongUnrolled()
(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:
sumber
null
.Span
danmemcpy
?Saya akan menggunakan kode yang tidak aman dan menjalankan
for
loop membandingkan pointer Int32.Mungkin Anda juga harus mempertimbangkan memeriksa array menjadi non-null.
sumber
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.
sumber
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:
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.
sumber
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:
sumber
NewMemCmp
jawaban saya untuk menggunakan AVX-2Saya belum melihat banyak solusi LINQ di sini.
Saya tidak yakin dengan implikasi kinerja, namun saya umumnya tetap berpegang pada
linq
aturan praktis dan kemudian mengoptimalkan nanti jika perlu.Harap dicatat ini hanya berfungsi jika array ukurannya sama. ekstensi bisa terlihat seperti itu
sumber
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.
Seperti yang Anda lihat, tidak ada cara yang lebih baik daripada
memcmp
dan ini perintah besarnya lebih cepat. Simpul sederhanafor
adalah pilihan terbaik kedua. Dan itu masih mengejutkan saya mengapa Microsoft tidak bisa begitu saja memasukkanBuffer.Compare
metode.[Program.cs]:
sumber
Untuk membandingkan array byte pendek, berikut ini adalah hack yang menarik:
Maka saya mungkin akan jatuh ke solusi yang tercantum dalam pertanyaan.
Akan menarik untuk melakukan analisis kinerja kode ini.
sumber
Bagi Anda yang peduli pesanan (yaitu ingin Anda
memcmp
mengembalikan yangint
seharusnya bukan apa-apa), .NET Core 3.0 (dan mungkin .NET Standard 2.1 alias .NET 5.0) akan mencakupSpan.SequenceCompareTo(...)
metode ekstensi (plus aSpan.SequenceEqualTo
) yang dapat digunakan untuk membandingkan duaReadOnlySpan<T>
instance (where T: IComparable<T>
).Dalam proposal GitHub asli , diskusi termasuk perbandingan pendekatan dengan perhitungan tabel lompatan, membaca
byte[]
aslong[]
, penggunaan SIMD, dan p / invoke ke implementasi CLRmemcmp
.Ke depan, ini harus Anda pergi-metode untuk membandingkan array byte atau rentang byte (sebagai harus menggunakan
Span<byte>
bukanbyte[]
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 burukEnumerable.SequenceEqual
).sumber
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 ...
sumber
Ini hampir pasti jauh lebih lambat daripada versi lain yang diberikan di sini, tapi itu menyenangkan untuk ditulis.
sumber
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:
sumber
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.
sumber
Gunakan
SequenceEquals
untuk perbandingan ini.sumber
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)
sumber
Jawaban singkatnya adalah ini:
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 :
sumber
Compare(new byte[]{128}, new byte[]{ 255 }) == true
sama sekali tidak buggy ...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 ()
sumber
IndexOutOfRangeException
ketika membandingkan array yang tidak kosong karena Anda mengakses elemen1
melaluiba0.Length
kapan harus0
melaluiba0.Length - 1
. Jika Anda memperbaikinya denganEnumerable.Range(0, ba0.Length)
itu masih salah mengembalikantrue
untuk array dengan panjang yang sama di mana hanya elemen pertama berbeda karena Anda tidak dapat membedakan antara elemen pertama yang memuaskanpredicate
dan tidak ada elemen yang memuaskanpredicate
;FirstOrDefault<int>()
kembali0
dalam kedua kasus.