Artikel ini adalah artikel cermin dari terjemahan mesin, silakan klik di sini untuk melompat ke artikel aslinya.

Melihat: 6317|Jawab: 3

[Sumber] [Berputar]. Pengoptimalan performa NET - melintasi koleksi Daftar dengan cepat

[Salin tautan]
Dipaparkan pada 2022-8-28 20:51:16 | | | |
Pengantar singkat

System.Collections.Generic.List <T>adalah kelas koleksi generik di .NET, yang dapat menyimpan semua jenis data karena kenyamanan dan API-nya yang kaya, yang banyak digunakan dalam kehidupan kita sehari-hari, dan dapat dikatakan sebagai kelas koleksi yang paling banyak digunakan.

Dalam penulisan kode, kita sering perlu mengulangi koleksi Daftar <T>untuk mendapatkan elemen di dalamnya untuk beberapa pemrosesan bisnis. Biasanya, tidak banyak elemen dalam satu set dan sangat cepat untuk dilintasi. Tetapi untuk beberapa pemrosesan data besar, statistik, komputasi waktu nyata, dll.<T>, Bagaimana cara dengan cepat melintasi kumpulan daftar puluhan ribu atau ratusan ribu data? Itulah yang perlu saya bagikan kepada Anda hari ini.

Mode traversal

Mari kita lihat performa metode traversal yang berbeda, dan bangun tolok ukur performa berikut, menggunakan traversal koleksi urutan besarnya yang berbeda untuk melihat performa metode yang berbeda. Cuplikan kode terlihat seperti ini:

Gunakan pernyataan foreach

foreach adalah cara paling umum kita melintasi koleksi, ini adalah implementasi gula sintaks dari pola iterator, dan juga digunakan sebagai tolok ukur untuk kali ini.

Karena pernyataan foreach adalah gula sintaks, kompiler akhirnya memanggil GetEnumerator() dan MoveNext() dengan perulangan while untuk mengimplementasikan fungsionalitas. Kode yang dikompilasi terlihat seperti ini:



Implementasi metode MoveNext() akan memastikan bahwa tidak akan ada utas lain yang memodifikasi koleksi dalam iterasi, dan jika modifikasi terjadi, itu akan melemparkan pengecualian InvalidOperationException, dan akan memiliki pemeriksaan overflow untuk memeriksa apakah indeks saat ini sah, dan juga perlu menetapkan elemen yang sesuai ke enumerator. Atribut saat ini,Jadi nyatanya, kinerjanya bukan yang terbaik, cuplikan kode terlihat seperti ini:



Mari kita lihat bagaimana kinerjanya di berbagai ukuran set, dan hasilnya terlihat seperti ini:



Dapat dilihat bahwa dalam kasus ukuran yang berbeda, hubungan pertumbuhan linier dari proses yang memakan waktu diperlukan, bahkan jika melintasi data 100w tanpa logika pemrosesan apa pun, dibutuhkan setidaknya 1 detik.

Gunakan metode Daftar ForEach

Cara umum lainnya adalah dengan menggunakan List<T>. ForEach(), yang memungkinkan Anda meneruskan <T>delegasi Tindakan, yang akan memanggil delegasi Tindakan saat menelemen-iterasi elemen<T>.

Ini adalah <T>metode implementasi internal List, sehingga dapat langsung mengakses array pribadi dan menghindari pemeriksaan overflow. Secara teori, itu harus cepat; Tetapi dalam skenario kami hanya ada satu metode kosong, yang mungkin tidak berperilaku baik dengan panggilan sebaris sepenuhnya ke metode foreach. Di bawah ini adalah kode sumber metode ForEach, yang menunjukkan bahwa metode tersebut tidak memiliki pemeriksaan overflow, tetapi masih mempertahankan pemeriksaan nomor versi bersamaan.



Selain itu, karena perlu untuk meneruskan delegasi ke metode ForEach, dalam kode panggilan, itu akan memeriksa apakah objek delegasi di kelas pembuatan penutupan kosong setiap saat, dan jika tidak, Action<T>() baru, seperti yang ditunjukkan di bawah ini:



Mari kita lihat perbandingannya dengan kata kunci foreach dalam hal kinerja. Gambar berikut menunjukkan hasil tolok ukur:



Dilihat dari hasil pengujian, 40% lebih lambat daripada menggunakan kata kunci foreach secara langsung, tampaknya jika tidak perlu, itu adalah pilihan yang lebih baik untuk digunakan foreach secara langsung, jadi apakah ada cara yang lebih cepat?

untuk traversal loop

Kembali ke cara tertua kami, yaitu menggunakan kata kunci for untuk melintasi koleksi. Ini harus menjadi metode traversal berkinerja terbaik saat ini, karena tidak memerlukan beberapa kode redundan seperti yang sebelumnya (meskipun pengindeks juga diperiksa untuk mencegah luapan air), dan jelas tidak memeriksa nomor versi, jadi dalam lingkungan multithreaded koleksi diubah, dan tidak akan ada pengecualian yang dilemparkan saat menggunakan for. Kode pengujian terlihat seperti ini:

Mari kita lihat bagaimana hasilnya.



Sepertinya ini adalah cara yang kita harapkan.Menggunakan loop for secara langsung 60% lebih cepat daripada foreach, satu set yang dulu membutuhkan waktu 1 detik untuk melintasi, sekarang hanya membutuhkan waktu 400 milidetik. Jadi apakah ada cara yang lebih cepat?

Gunakan KoleksiMarshal

Setelah .NET5, komunitas dotnet mengimplementasikan kelas CollectionsMarshal untuk meningkatkan performa operasi pengumpulan. Kelas ini mengimplementasikan cara mengakses array native dari jenis koleksi (jika Anda telah melihat [. .NET Performance Optimization - You Should Set the Initial Size for Collection Types], Anda tahu bahwa implementasi yang mendasari banyak struktur data adalah array). Jadi dapat melewati semua jenis deteksi dan langsung mengakses array asli, yang seharusnya menjadi yang tercepat. Kodenya terlihat seperti ini:

Anda dapat melihat bahwa kode yang dihasilkan oleh kompiler sangat efisien.



Akses langsung ke array yang mendasarinya sangat berbahaya, Anda harus tahu apa yang Anda lakukan dengan setiap baris kode, dan memiliki pengujian yang cukup. Hasil benchmark adalah sebagai berikut:



WowMenggunakan CollectionsMarshal 79% lebih cepat daripada menggunakan foreach, tetapi itu harus menjadi alasan untuk pengoptimalan JIT, tidak ada perbedaan besar antara menggunakan foreach dan for keyword loop Span.

ringkasan

Hari ini saya berbicara dengan Anda tentang cara cepat melintasi koleksi List, dan dalam banyak kasus disarankan untuk menggunakan kata kunci foreach, yang memiliki pemeriksaan overflow dan kontrol nomor versi multi-threaded, yang dapat memudahkan kita untuk menulis kode yang benar.

Jika Anda memerlukan performa tinggi dan volume data besar, disarankan untuk menggunakan dan CollectionsMarshal.AsSpan secara langsung untuk melintasi koleksi.

Tautan kode sumber artikel ini:

Login hyperlink terlihat.

Tautan asli:Login hyperlink terlihat.





Mantan:Penjelasan terperinci tentang arsitektur pesan RabbitMQ AMQP
Depan:Kabel jaringan kepala kristal T568A dan T568B standar dan perbedaan
Dipaparkan pada 2022-9-4 22:15:52 |
Belajar belajar
Dipaparkan pada 2022-9-8 10:33:05 |
Belajar belajar
Dipaparkan pada 2023-6-27 22:39:13 |
Halo 12306 Bisakah Anda mengirimi saya pesan pribadi dengan data
Sanggahan:
Semua perangkat lunak, materi pemrograman, atau artikel yang diterbitkan oleh Code Farmer Network hanya untuk tujuan pembelajaran dan penelitian; Konten di atas tidak boleh digunakan untuk tujuan komersial atau ilegal, jika tidak, pengguna akan menanggung semua konsekuensi. Informasi di situs ini berasal dari Internet, dan sengketa hak cipta tidak ada hubungannya dengan situs ini. Anda harus sepenuhnya menghapus konten di atas dari komputer Anda dalam waktu 24 jam setelah pengunduhan. Jika Anda menyukai program ini, harap dukung perangkat lunak asli, pembelian pendaftaran, dan dapatkan layanan asli yang lebih baik. Jika ada pelanggaran, silakan hubungi kami melalui email.

Mail To:help@itsvse.com