Saya tidak ingin diskusi tentang kapan dan tidak membuang pengecualian. Saya ingin menyelesaikan masalah sederhana. 99% dari waktu argumen untuk tidak melempar pengecualian berputar di sekitar mereka menjadi lambat sementara pihak lain mengklaim (dengan uji benchmark) bahwa kecepatan bukan masalah. Saya telah membaca banyak blog, artikel, dan posting yang berkaitan dengan satu sisi atau yang lain. Jadi yang mana?
c#
.net
performance
exception
Goran
sumber
sumber
Jawaban:
Saya berada di sisi "tidak lambat" - atau lebih tepatnya "tidak cukup lambat untuk membuatnya layak untuk menghindari mereka dalam penggunaan normal". Saya telah menulis dua artikel pendek tentang ini. Ada kritik terhadap aspek tolok ukur, yang sebagian besar turun ke "dalam kehidupan nyata akan ada lebih banyak tumpukan yang harus dilalui, sehingga Anda akan meledakkan cache dll" - tetapi menggunakan kode kesalahan untuk meningkatkan tumpukan juga hancurkan cache, jadi saya tidak melihat itu sebagai argumen yang sangat bagus.
Hanya untuk memperjelas - Saya tidak mendukung menggunakan pengecualian di mana mereka tidak logis. Misalnya,
int.TryParse
sepenuhnya tepat untuk mengonversi data dari pengguna. Ini tepat ketika membaca file yang dihasilkan mesin, di mana kegagalan berarti "File tidak dalam format yang seharusnya, saya benar-benar tidak ingin mencoba menangani ini karena saya tidak tahu apa lagi yang salah. "Saat menggunakan pengecualian dalam "hanya keadaan yang masuk akal" Saya belum pernah melihat aplikasi yang kinerjanya sangat terganggu oleh pengecualian. Pada dasarnya, pengecualian tidak boleh sering terjadi kecuali Anda memiliki masalah kebenaran yang signifikan, dan jika Anda memiliki masalah kebenaran yang signifikan maka kinerja bukanlah masalah terbesar yang Anda hadapi.
sumber
Ada jawaban pasti untuk ini dari orang yang menerapkannya - Chris Brumme. Dia menulis artikel blog yang sangat baik tentang subjek (peringatan - sangat panjang) (warning2 - ditulis dengan sangat baik, jika Anda seorang teknisi Anda akan membacanya sampai akhir dan kemudian harus mengganti jam Anda setelah bekerja :) )
Ringkasan eksekutif: mereka lambat. Mereka diimplementasikan sebagai pengecualian Win32 SEH, sehingga beberapa bahkan akan melewati batas 0 ring CPU! Tentunya di dunia nyata, Anda akan melakukan banyak pekerjaan lain sehingga pengecualian aneh tidak akan diperhatikan sama sekali, tetapi jika Anda menggunakannya untuk aliran program mengharapkan aplikasi Anda akan dipalu. Ini adalah contoh lain dari mesin pemasaran MS yang merugikan kita. Saya ingat satu microsoftie memberitahu kami bagaimana mereka mengeluarkan benar-benar nol overhead, yang tosh lengkap.
Chris memberikan penawaran terkait:
sumber
Saya tidak tahu apa yang orang bicarakan ketika mereka mengatakan mereka lambat hanya jika mereka dilempar.
EDIT: Jika Pengecualian tidak dilempar, maka itu berarti Anda sedang melakukan Pengecualian baru () atau sesuatu seperti itu. Kalau tidak, pengecualian akan menyebabkan utas ditangguhkan, dan tumpukan berjalan. Ini mungkin OK dalam situasi yang lebih kecil, tetapi di situs web dengan lalu lintas tinggi, mengandalkan pengecualian sebagai alur kerja atau mekanisme jalur eksekusi pasti akan menyebabkan masalah kinerja Anda. Pengecualian, pada dasarnya, tidak buruk, dan berguna untuk mengekspresikan kondisi luar biasa
Alur kerja pengecualian dalam aplikasi .NET menggunakan pengecualian kesempatan pertama dan kedua. Untuk semua pengecualian, bahkan jika Anda menangkap dan menangani mereka, objek pengecualian masih dibuat dan kerangka kerja masih harus berjalan tumpukan untuk mencari pawang. Jika Anda menangkap dan memikirkan kembali tentu saja itu akan memakan waktu lebih lama - Anda akan mendapatkan pengecualian kesempatan pertama, menangkapnya, mengolahnya kembali, menyebabkan pengecualian peluang pertama lainnya, yang kemudian tidak menemukan penangan, yang kemudian menyebabkan pengecualian kesempatan kedua.
Pengecualian juga objek pada tumpukan - jadi jika Anda melempar banyak pengecualian, maka Anda menyebabkan masalah kinerja dan memori.
Selanjutnya, menurut salinan "Aplikasi Pengujian Microsoft .NET Web Aplikasi" yang saya tulis oleh tim ACE:
"Penanganan pengecualian itu mahal. Eksekusi dari thread yang terlibat ditangguhkan sementara CLR berulang melalui tumpukan panggilan untuk mencari penangan pengecualian yang tepat, dan ketika ditemukan, penangan pengecualian dan beberapa jumlah blok akhirnya semua harus memiliki kesempatan untuk mengeksekusi sebelum pemrosesan reguler dapat dilakukan. "
Pengalaman saya sendiri di lapangan menunjukkan bahwa mengurangi pengecualian secara signifikan membantu kinerja. Tentu saja, ada hal-hal lain yang Anda perhitungkan saat pengujian kinerja - misalnya, jika Disk I / O Anda diambil, atau pertanyaan Anda ada di detik, maka itu harus menjadi fokus Anda. Tetapi menemukan dan menghapus pengecualian harus menjadi bagian penting dari strategi itu.
sumber
Argumen yang saya mengerti bukanlah bahwa melemparkan pengecualian adalah buruk, mereka lambat per se. Alih-alih, ini adalah tentang menggunakan konstruk throw / catch sebagai cara kelas pertama mengendalikan logika aplikasi normal, alih-alih konstruk kondisional yang lebih tradisional.
Seringkali dalam logika aplikasi normal Anda melakukan perulangan di mana tindakan yang sama diulang ribuan / jutaan kali. Dalam hal ini, dengan beberapa profil yang sangat sederhana (lihat kelas Stopwatch), Anda dapat melihat sendiri bahwa melempar pengecualian alih-alih mengatakan pernyataan sederhana jika pernyataan ternyata jauh lebih lambat.
Bahkan saya pernah membaca bahwa tim .NET di Microsoft memperkenalkan metode TryXXXXX di .NET 2.0 untuk banyak jenis basis FCL khusus karena pelanggan mengeluh bahwa kinerja aplikasi mereka sangat lambat.
Ternyata dalam banyak kasus ini adalah karena pelanggan mencoba jenis konversi nilai dalam satu lingkaran, dan setiap upaya gagal. Pengecualian konversi dilempar dan kemudian ditangkap oleh penangan pengecualian yang kemudian menelan pengecualian dan melanjutkan loop.
Microsoft sekarang merekomendasikan metode TryXXX harus digunakan terutama dalam situasi ini untuk menghindari masalah kinerja yang mungkin terjadi.
Saya bisa saja salah, tetapi sepertinya Anda tidak yakin tentang kebenaran "tolok ukur" yang telah Anda baca. Solusi sederhana: Cobalah sendiri.
sumber
Server XMPP saya memperoleh peningkatan kecepatan besar (maaf, tidak ada angka aktual, murni pengamatan) setelah saya secara konsisten mencoba mencegahnya terjadi (seperti memeriksa apakah soket tersambung sebelum mencoba membaca lebih banyak data) dan memberi saya cara untuk menghindarinya (metode TryX yang disebutkan). Itu hanya dengan sekitar 50 pengguna virtual aktif (mengobrol).
sumber
Hanya untuk menambahkan pengalaman saya sendiri baru-baru ini ke diskusi ini: sejalan dengan sebagian besar dari apa yang ditulis di atas, saya menemukan melempar pengecualian menjadi sangat lambat ketika dilakukan secara berulang, bahkan tanpa menjalankan debugger. Saya baru saja meningkatkan kinerja program besar yang saya tulis hingga 60% dengan mengubah sekitar lima baris kode: beralih ke model kode-kembali alih-alih melemparkan pengecualian. Memang, kode tersebut berjalan ribuan kali dan berpotensi membuang ribuan pengecualian sebelum saya mengubahnya. Jadi saya setuju dengan pernyataan di atas: membuang pengecualian ketika sesuatu yang penting benar-benar salah, bukan sebagai cara mengendalikan aliran aplikasi dalam situasi "yang diharapkan".
sumber
Jika Anda membandingkannya dengan kode yang dikembalikan, mereka lambat sekali. Namun seperti yang dinyatakan poster sebelumnya, Anda tidak ingin melakukan operasi program normal sehingga Anda hanya mendapatkan kinerja yang baik ketika masalah terjadi dan dalam sebagian besar kasus kinerja tidak lagi penting (karena pengecualiannya menyiratkan road-block).
Mereka pasti layak digunakan lebih dari kode kesalahan, kelebihannya adalah IMO yang luas.
sumber
Saya tidak pernah memiliki masalah kinerja dengan pengecualian. Saya sering menggunakan pengecualian - Saya tidak pernah menggunakan kode balik jika saya bisa. Itu adalah praktik yang buruk, dan menurut saya, baunya seperti kode spageti.
Saya pikir itu semua bermuara pada bagaimana Anda menggunakan pengecualian: jika Anda menggunakannya seperti kode kembali (setiap panggilan metode dalam stack catch and rethrows) maka, ya, mereka akan lambat, karena Anda memiliki overhead setiap tangkapan / lemparan tunggal.
Tetapi jika Anda melemparkan di bagian bawah tumpukan dan menangkap di bagian atas (Anda mengganti seluruh rantai kode pengembalian dengan satu lemparan / tangkapan), semua operasi yang mahal dilakukan sekali.
Pada akhirnya, mereka adalah fitur bahasa yang valid.
Hanya untuk membuktikan maksud saya
Silakan jalankan kode di tautan ini (terlalu besar untuk dijawab).
Hasil di komputer saya:
marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM
Cap waktu adalah keluaran di awal, antara kode pengembalian dan pengecualian, di akhir. Dibutuhkan waktu yang sama dalam kedua kasus. Perhatikan bahwa Anda harus mengompilasi dengan optimisasi.
sumber
Tetapi mono melempar pengecualian 10x lebih cepat dari mode .net standalone, dan mode .net standalone melempar pengecualian 60x lebih cepat daripada mode. (Mesin uji memiliki model CPU yang sama)
sumber
Dalam mode rilis, overhead minimal.
Kecuali jika Anda akan menggunakan pengecualian untuk kontrol aliran (misalnya, keluar non-lokal) secara rekursif, saya ragu Anda akan dapat melihat perbedaannya.
sumber
Pada Windows CLR, untuk rantai panggilan kedalaman-8, melempar pengecualian adalah 750 kali lebih lambat daripada memeriksa dan menyebarkan nilai kembali. (lihat di bawah untuk benchmark)
Biaya pengecualian yang tinggi ini karena windows CLR terintegrasi dengan sesuatu yang disebut Windows Structured Exception Handling . Ini memungkinkan pengecualian untuk ditangkap dan dilempar ke berbagai runtime dan bahasa. Namun, ini sangat lambat.
Pengecualian dalam runtime Mono (pada platform apa pun) jauh lebih cepat, karena tidak terintegrasi dengan SEH. Namun, ada kehilangan fungsionalitas ketika melewati pengecualian di beberapa runtime karena tidak menggunakan apa pun seperti SEH.
Berikut adalah hasil yang disingkat dari patokan pengecualian saya dan nilai pengembalian untuk Windows CLR.
Dan ini kodenya ..
sumber
Satu catatan singkat di sini tentang kinerja yang terkait dengan menangkap pengecualian.
Ketika jalur eksekusi memasuki blok 'coba', tidak ada yang ajaib terjadi. Tidak ada instruksi 'coba', dan tidak ada biaya yang terkait dengan memasukkan atau keluar dari blok try. Informasi tentang blok percobaan disimpan dalam metadata metode, dan metadata ini digunakan saat runtime setiap kali pengecualian dimunculkan. Mesin eksekusi berjalan di tumpukan mencari panggilan pertama yang terkandung dalam blok percobaan. Setiap overhead yang terkait dengan penanganan pengecualian hanya terjadi ketika pengecualian dilemparkan.
sumber
Saat menulis kelas / fungsi untuk digunakan orang lain, tampaknya sulit untuk mengatakan kapan pengecualian sesuai. Ada beberapa bagian BCL yang berguna yang harus saya buang dan gunakan pinvoke karena mereka mengeluarkan pengecualian daripada mengembalikan kesalahan. Untuk beberapa kasus Anda dapat mengatasinya tetapi untuk yang lain seperti System.Management dan Penghitung Kinerja ada penggunaan di mana Anda perlu melakukan loop di mana pengecualian sering dilontarkan oleh BCL.
Jika Anda menulis pustaka dan ada kemungkinan jauh bahwa fungsi Anda dapat digunakan dalam satu lingkaran dan ada potensi untuk sejumlah besar iterasi, gunakan pola Coba .. atau cara lain untuk mengekspos kesalahan di samping pengecualian. Dan bahkan kemudian, sulit untuk mengatakan berapa banyak fungsi Anda akan dipanggil jika sedang digunakan oleh banyak proses di lingkungan bersama.
Dalam kode saya sendiri, pengecualian hanya dilemparkan ketika hal-hal begitu luar biasa sehingga perlu untuk melihat jejak stack dan melihat apa yang salah dan kemudian memperbaikinya. Jadi saya cukup banyak menulis ulang bagian BCL untuk menggunakan penanganan kesalahan berdasarkan Try .. pola bukan pengecualian.
sumber