Saya memiliki program yang membutuhkan kinerja cepat. Dalam salah satu loop dalamnya, saya perlu menguji jenis objek untuk melihat apakah itu mewarisi dari antarmuka tertentu.
Salah satu cara untuk melakukannya adalah dengan fungsionalitas pemeriksaan tipe built-in CLR. Metode paling elegan yang mungkin ada adalah kata kunci 'is':
if (obj is ISpecialType)
Pendekatan lain adalah memberikan kelas dasar fungsi GetType () virtual saya sendiri yang mengembalikan nilai enum yang telah ditentukan sebelumnya (dalam kasus saya, sebenarnya, saya hanya perlu bool). Metode itu akan cepat, tetapi kurang elegan.
Saya telah mendengar bahwa ada instruksi IL khusus untuk kata kunci 'is', tetapi itu tidak berarti itu dijalankan dengan cepat ketika diterjemahkan ke dalam perakitan asli. Adakah yang bisa berbagi beberapa wawasan tentang kinerja 'adalah' versus metode lainnya?
UPDATE: Terima kasih atas semua jawaban yang diinformasikan! Tampaknya beberapa poin berguna tersebar di antara jawaban: Poin Andrew tentang 'adalah' secara otomatis melakukan pemeran itu penting, tetapi data kinerja yang dikumpulkan oleh Binary Worrier dan Ian juga sangat berguna. Alangkah baiknya jika salah satu jawaban diedit untuk menyertakan semua informasi ini.
sumber
Jawaban:
Penggunaan
is
dapat mengganggu kinerja jika, setelah Anda memeriksa jenisnya, Anda mentransmisikan ke jenis itu.is
sebenarnya mentransmisikan objek ke jenis yang Anda periksa sehingga setiap pengecoran berikutnya berlebihan.Jika Anda tetap akan melakukan cast, berikut ini pendekatan yang lebih baik:
sumber
as
pada dasarnya melakukan operasi yang sama sepertiis
(yaitu, pemeriksaan tipe). Satu-satunya perbedaan adalah bahwa ia kembali,null
bukanfalse
.Saya dengan Ian , Anda mungkin tidak ingin melakukan ini.
Namun, asal tahu saja, ada sangat sedikit perbedaan antara keduanya, lebih dari 10.000.000 iterasi
Saya pribadi tidak akan memperbaiki masalah ini dengan cara ini, tetapi jika saya terpaksa memilih satu metode, itu akan menjadi pemeriksaan IS bawaan, perbedaan kinerja tidak layak mempertimbangkan overhead pengkodean.
Kelas dasar dan turunan saya
JubJub: Seperti yang diminta info lebih lanjut tentang tes.
Saya menjalankan kedua pengujian dari aplikasi konsol (build debug) setiap pengujian terlihat seperti berikut
Berjalan dalam rilis, saya mendapatkan perbedaan 60 - 70 ms, seperti Ian.
Pembaruan Lebih Lanjut - 25 Oktober 2012
Setelah beberapa tahun saya melihat sesuatu tentang ini, kompilator dapat memilih untuk menghilangkan
bool b = a is MyClassB
dalam rilis karena b tidak digunakan dimanapun.Kode ini. . .
. . . secara konsisten menunjukkan
is
cek masuk pada kira-kira 57 milidetik, dan perbandingan enum masuk pada 29 milidetik.NB Saya masih lebih suka
is
cek, perbedaannya terlalu kecil untuk diperhatikansumber
is
operator, dan bahwa terlalu banyak mendengar tentang perancangan & pengkodean di sekitaris
operator akan menghabiskan banyak uang kualitas kode dan pada akhirnya akan menjadi kinerja yang merugikan diri sendiri juga. Dalam hal ini saya mendukung pernyataan saya. The 'adalah' operator tidak akan menjadi yang masalah dengan kinerja runtime Anda.Oke, jadi saya mengobrol tentang ini dengan seseorang dan memutuskan untuk menguji ini lebih lanjut. Sejauh yang saya tahu, kinerja
as
danis
keduanya sangat baik, dibandingkan dengan menguji anggota Anda sendiri atau fungsi untuk menyimpan informasi tipe.Saya menggunakan
Stopwatch
, yang baru saja saya pelajari mungkin bukan pendekatan yang paling dapat diandalkan, jadi saya juga mencobaUtcNow
. Kemudian, saya juga mencoba pendekatan waktu Prosesor yang tampaknya mirip denganUtcNow
menyertakan waktu pembuatan yang tidak dapat diprediksi. Saya juga mencoba membuat kelas dasar non-abstrak tanpa virtual tetapi tampaknya tidak memiliki efek yang signifikan.Saya menjalankan ini pada Quad Q6600 dengan RAM 16GB. Bahkan dengan iterasi 50mil, angkanya masih memantul sekitar +/- 50 atau lebih milidetik jadi saya tidak akan terlalu banyak membaca perbedaan kecil.
Menarik untuk melihat bahwa x64 dibuat lebih cepat tetapi dieksekusi karena / lebih lambat dari x86
Mode Rilis x64:
Stopwatch:
As: 561ms
Is: 597ms
Properti dasar: 539ms
Bidang dasar: 555ms
Bidang RO Basis: 552ms Tes
Virtual GetEnumType (): 556ms Tes
Virtual IsB (): 588ms
Buat Waktu: 10416ms
UtcNow:
As: 499ms
Is: 532ms
Properti dasar: 479ms
Bidang dasar: 502ms
Bidang RO dasar: 491ms
Virtual GetEnumType (): 502ms
Virtual bool IsB (): 522ms
Buat Waktu: 285ms (Angka ini tampaknya tidak dapat diandalkan dengan UtcNow. Saya juga mendapatkan 109ms dan 806ms.)
Mode Rilis x86:
Stopwatch:
As: 391ms
Is: 423ms
Properti dasar: 369ms
Bidang dasar: 321ms
Bidang RO Basis: 339ms Tes
Virtual GetEnumType (): 361ms Tes
Virtual IsB (): 365ms
Buat Waktu: 14106ms
UtcNow:
As: 348ms
Is: 375ms
Properti dasar: 329ms
Bidang dasar: 286ms
Bidang RO dasar: 309ms
Virtual GetEnumType (): 321ms
Virtual bool IsB (): 332ms
Buat Waktu: 544ms (Angka ini tampaknya tidak dapat diandalkan dengan UtcNow.)
Inilah sebagian besar kodenya:
sumber
Andrew benar. Faktanya dengan analisis kode ini dilaporkan oleh Visual Studio sebagai pemeran yang tidak perlu.
Satu gagasan (tanpa mengetahui apa yang Anda lakukan adalah sedikit bidikan dalam kegelapan), tetapi saya selalu disarankan untuk menghindari pemeriksaan seperti ini, dan sebaliknya memiliki kelas lain. Jadi, daripada melakukan beberapa pemeriksaan dan melakukan tindakan yang berbeda tergantung pada jenisnya, buat kelas tahu cara memprosesnya sendiri ...
misalnya Obj bisa ISpecialType atau IType;
keduanya memiliki metode DoStuff () yang ditentukan. Untuk IType bisa saja mengembalikan atau mengerjakan custom, sedangkan ISpecialType bisa melakukan hal lain.
Tindakan ini kemudian akan sepenuhnya menghapus transmisi apa pun, membuat kode lebih bersih dan lebih mudah dikelola, dan kelas tahu cara melakukan tugasnya sendiri.
sumber
Saya melakukan perbandingan kinerja pada dua kemungkinan perbandingan tipe
Hasilnya adalah: Menggunakan "is" 10x lebih cepat !!!
Keluaran:
Waktu untuk Jenis-Perbandingan: 00: 00: 00.456
Waktu untuk Perbandingan-Is: 00: 00: 00.042
Kode Saya:
sumber
Poin yang dibuat Andrew Hare tentang kinerja yang hilang ketika Anda melakukan
is
pemeriksaan dan kemudian pemeran valid tetapi di C # 7.0 yang dapat kita lakukan adalah memeriksa kecocokan pola penyihir untuk menghindari pemeran tambahan nanti:Lebih jauh lagi jika Anda perlu memeriksa antara beberapa tipe konstruksi pencocokan pola C # 7.0 sekarang memungkinkan Anda untuk melakukan
switch
pada tipe:Anda dapat membaca lebih lanjut tentang pencocokan pola di C # dalam dokumentasi di sini .
sumber
OneOf<T...>
tetapi mereka memiliki kekurangan utama) .Jika ada yang bertanya-tanya, saya telah melakukan pengujian di Unity engine 2017.1, dengan skrip versi runtime .NET4.6 (Experimantal) pada notebook dengan CPU i5-4200U. Hasil:
Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35
Artikel lengkap: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
sumber
Saya selalu disarankan untuk menghindari pemeriksaan seperti ini, dan sebaliknya memiliki kelas lain. Jadi, daripada melakukan beberapa pemeriksaan dan melakukan tindakan yang berbeda tergantung pada jenisnya, buat kelas tahu cara memprosesnya sendiri ...
misalnya Obj bisa ISpecialType atau IType;
keduanya memiliki metode DoStuff () yang ditentukan. Untuk IType bisa saja mengembalikan atau mengerjakan custom, sedangkan ISpecialType bisa melakukan hal lain.
Tindakan ini kemudian akan sepenuhnya menghapus transmisi apa pun, membuat kode lebih bersih dan lebih mudah dikelola, dan kelas tahu cara melakukan tugasnya sendiri.
sumber