Array.Copy dan Buffer.BlockCopy keduanya melakukan hal yang sama, tetapi BlockCopy
ditujukan untuk penyalinan array primitif tingkat-byte yang cepat, sedangkan Copy
implementasi untuk tujuan umum. Pertanyaan saya adalah - dalam keadaan apa Anda harus menggunakan BlockCopy
? Haruskah Anda menggunakannya kapan saja saat Anda menyalin array tipe primitif, atau haruskah Anda menggunakannya hanya jika Anda mengkode untuk kinerja? Apakah ada sesuatu yang secara inheren berbahaya tentang penggunaan Buffer.BlockCopy
berlebihan Array.Copy
?
124
Marshal.Copy
:-). Nah, gunakanArray.Copy
untuk tipe referensi, tipe nilai kompleks dan jika tipenya tidak berubah,Buffer.BlockCopy
untuk "konversi" antara tipe nilai, array byte, dan sihir byte. F.ex. kombinasi denganStructLayout
cukup kuat jika Anda tahu apa yang Anda lakukan. Mengenai kinerja, tampaknya panggilan tidak terkelola kememcpy
/cpblk
adalah yang tercepat untuk itu - lihat code4k.blogspot.nl/2010/10/… .byte[]
. Tidak ada perbedaan dalam versi Rilis. TerkadangArray.Copy
, terkadangBuffer.BlockCopy
(sedikit) lebih cepat.Array.Copy
lebih merupakan versi khusus - misalnya ia hanya dapat menyalin array peringkat yang sama.Jawaban:
Karena parameter yang
Buffer.BlockCopy
akan berbasis byte daripada berbasis indeks, Anda lebih cenderung mengacaukan kode Anda daripada jika Anda menggunakanArray.Copy
, jadi saya hanya akan menggunakanBuffer.BlockCopy
di bagian kinerja-kritis dari kode saya.sumber
UInt16
adalah dua byte per elemen. Jika Anda meneruskan array ini ke BlockCopy bersama dengan jumlah elemen dalam array, tentu saja hanya setengah dari array yang akan disalin. Agar ini berfungsi dengan baik, Anda harus meneruskan jumlah elemen dikalikan ukuran setiap elemen (2) sebagai parameter panjang. msdn.microsoft.com/en-us/library/… dan cariINT_SIZE
di contoh.Pendahuluan
Saya terlambat bergabung ke pesta, tetapi dengan 32 ribu penayangan, hal ini layak dilakukan. Sebagian besar kode microbenchmarking dalam jawaban yang diposting sejauh ini mengalami satu atau beberapa kelemahan teknis yang parah, termasuk tidak memindahkan alokasi memori keluar dari loop pengujian (yang memperkenalkan artefak GC yang parah), tidak menguji variabel vs. aliran eksekusi deterministik, pemanasan JIT, dan tidak melacak variabilitas intra-pengujian. Selain itu, sebagian besar jawaban tidak menguji efek dari berbagai ukuran buffer dan berbagai jenis primitif (berkenaan dengan sistem 32-bit atau 64-bit). Untuk menjawab pertanyaan ini secara lebih komprehensif, saya menghubungkannya ke kerangka kerja mikrobenchmarking khusus yang saya kembangkan yang mengurangi sebagian besar "gotcha" umum sejauh mungkin. Pengujian dijalankan dalam mode Rilis .NET 4.0 pada mesin 32-bit dan mesin 64-bit. Hasil rata-rata dilakukan selama 20 pengujian, di mana setiap pengujian memiliki 1 juta pengujian per metode. Jenis primitif yang diuji adalah
byte
(1 byte),int
(4 byte), dandouble
(8 byte). Tiga metode yang diuji:Array.Copy()
,Buffer.BlockCopy()
, dan sederhana tugas per-indeks dalam satu lingkaran. Datanya terlalu banyak untuk diposting di sini, jadi saya akan merangkum poin-poin penting.Takeaways
Array.Copy()
atauBuffer.BlockCopy()
untuk ketiga jenis primitif yang diuji pada mesin 32-bit dan 64-bit. Selain itu, rutinitas salinan loop eksplisit memiliki variabilitas kinerja yang lebih rendah dibandingkan dengan dua alternatif. Kinerja yang baik hampir pasti karena lokalitas referensi yang dieksploitasi oleh cache memori CPU L1 / L2 / L3 dalam hubungannya dengan tidak ada overhead panggilan metode.double
buffer di mesin 32-bit saja : Rutinitas penyalinan loop eksplisit lebih baik daripada kedua alternatif untuk semua ukuran buffer yang diuji hingga 100k. Peningkatannya 3-5% lebih baik daripada metode lainnya. Ini karena kinerjaArray.Copy()
danBuffer.BlockCopy()
menjadi sangat menurun setelah melewati lebar asli 32-bit. Jadi saya berasumsi efek yang sama akan berlaku untuklong
buffer juga.byte[]
, di mana penyalinan loop eksplisit bisa menjadi 7x atau lebih lambat pada ukuran buffer yang besar.Array.Copy()
danBuffer.BlockCopy()
dilakukan hampir identik. Rata-rata,Array.Copy()
tampaknya memiliki keunggulan yang sangat tipis sekitar 2% atau kurang waktu yang dibutuhkan (tetapi 0,2% - 0,5% lebih baik adalah tipikal), meskipunBuffer.BlockCopy()
kadang-kadang mengalahkannya. Untuk alasan yang tidak diketahui,Buffer.BlockCopy()
memiliki variabilitas intra-tes yang jauh lebih tinggi daripadaArray.Copy()
. Efek ini tidak dapat dihilangkan meskipun saya mencoba beberapa mitigasi dan tidak memiliki teori yang dapat dioperasikan tentang mengapa.Array.Copy()
merupakan metode yang "lebih cerdas", lebih umum, dan jauh lebih aman, selain sangat sedikit lebih cepat dan rata-rata memiliki variabilitas yang lebih kecil, metode ini harus dipilih untukBuffer.BlockCopy()
di hampir semua kasus umum. Satu-satunya kasus penggunaan yangBuffer.BlockCopy()
akan jauh lebih baik adalah ketika tipe nilai array sumber dan tujuan berbeda (seperti yang ditunjukkan dalam jawaban Ken Smith). Meskipun skenario ini tidak umum,Array.Copy()
dapat berkinerja sangat buruk di sini karena transmisi jenis nilai "aman" terus-menerus, dibandingkan dengan transmisi langsung dariBuffer.BlockCopy()
.Array.Copy()
lebih cepat daripadaBuffer.BlockCopy()
penyalinan array tipe yang sama dapat ditemukan di sini .sumber
Array.Clear()
pertama mulai mengalahkan kliring lingkaran tugas eksplisit dari sebuah array (pengaturan untukfalse
,0
ataunull
). Ini sesuai dengan temuan saya yang serupa di atas. Tolok ukur terpisah ini ditemukan secara online di sini: manski.net/2012/12/net-array-clear-vs-arrayx-0-performanceLoop Results for 1000000 iterations 17.9515ms. Buffer.BlockCopy Results for 1000000 iterations 39.8937ms. Array.Copy Results for 1000000 iterations 45.9059ms
Namun, jika ukuran salinan> ~ 20 byte loop eksplisit secara signifikan lebih lambat.Contoh lain ketika masuk akal untuk digunakan
Buffer.BlockCopy()
adalah ketika Anda diberikan array primitif (katakanlah, pendek), dan perlu mengubahnya menjadi array byte (misalnya, untuk transmisi melalui jaringan). Saya sering menggunakan metode ini saat menangani audio dari Silverlight AudioSink. Ini memberikan sampel sebagaishort[]
larik, tetapi Anda perlu mengubahnya menjadibyte[]
larik saat Anda membuat paket yang Anda kirimkanSocket.SendAsync()
. Anda dapat menggunakanBitConverter
, dan mengulang melalui array satu per satu, tetapi itu jauh lebih cepat (sekitar 20x dalam pengujian saya) hanya untuk melakukan ini:Dan trik yang sama juga bekerja secara terbalik:
Ini hampir sedekat yang Anda dapatkan di C # yang aman dengan
(void *)
jenis manajemen memori yang sangat umum di C dan C ++.sumber
MemoryMarshal.AsBytes<T>
atauMemoryMarshal.Cast<TFrom, TTo>
membiarkan Anda menafsirkan urutan satu primitif sebagai urutan primitif lainnya.Berdasarkan pengujian saya, kinerja bukanlah alasan untuk memilih Buffer.BlockCopy daripada Array.Copy. Dari pengujian saya Array.Copy sebenarnya lebih cepat dari Buffer.BlockCopy.
Contoh Keluaran:
sumber
ArrayCopy lebih pintar dari BlockCopy. Ini mencari cara untuk menyalin elemen jika sumber dan tujuan adalah array yang sama.
Jika kita mengisi array int dengan 0,1,2,3,4 dan menerapkan:
kami berakhir dengan 0,0,1,2,3 seperti yang diharapkan.
Coba ini dengan BlockCopy dan kami mendapatkan: 0,0,2,3,4. Jika saya menetapkan
array[0]=-1
setelah itu, itu menjadi -1,0,2,3,4 seperti yang diharapkan, tetapi jika panjang array genap, seperti 6, kita mendapatkan -1,256,2,3,4,5. Barang berbahaya. Jangan gunakan BlockCopy selain untuk menyalin satu array byte ke yang lain.Ada kasus lain di mana Anda hanya dapat menggunakan Array.Copy: jika ukuran array lebih panjang dari 2 ^ 31. Array.Copy memiliki kelebihan beban dengan
long
parameter ukuran. BlockCopy tidak memiliki itu.sumber
Untuk mempertimbangkan argumen ini, jika seseorang tidak berhati-hati dalam membuat tolok ukur ini, mereka dapat dengan mudah disesatkan. Saya menulis tes yang sangat sederhana untuk menggambarkan hal ini. Dalam pengujian saya di bawah ini jika saya menukar urutan pengujian saya antara memulai Buffer.BlockCopy terlebih dahulu atau Array. Salin yang berjalan lebih dulu hampir selalu paling lambat (meskipun mendekati). Ini berarti untuk banyak alasan yang saya tidak akan membahas hanya menjalankan tes beberapa kali esp satu demi satu tidak akan memberikan hasil yang akurat.
Saya terpaksa mempertahankan tes seperti dengan 1000000 percobaan masing-masing untuk array 1000000 ganda berurutan. Namun, saya kemudian mengabaikan 900000 siklus pertama dan rata-rata sisanya. Dalam hal ini Buffer lebih unggul.
https://github.com/chivandikwa/Random-Benchmarks
sumber
Hanya ingin menambahkan kasus pengujian saya yang menunjukkan lagi BlockCopy tidak memiliki manfaat 'PERFORMANCE' atas Array.Copy. Mereka tampaknya memiliki kinerja yang sama dalam mode rilis pada mesin saya (keduanya membutuhkan waktu sekitar 66ms untuk menyalin 50 juta bilangan bulat). Di bawah mode debug, BlockCopy hanya sedikit lebih cepat.
sumber