Seberapa lambat pengecualian .NET?

143

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?

Beberapa tautan dari jawaban: Skeet , Mariani , Brumme .

Goran
sumber
13
ada kebohongan, kebohongan dan tolok ukur. :)
gbjbaanb
Sayangnya, beberapa jawaban pilihan tinggi di sini tidak menjawab bahwa pertanyaannya adalah menanyakan "seberapa lambat pengecualian?", Dan secara khusus diminta untuk menghindari topik seberapa sering menggunakannya. Satu jawaban sederhana untuk pertanyaan yang sebenarnya ditanyakan adalah ..... Pada Windows CLR, pengecualian 750 kali lebih lambat dari nilai yang dikembalikan.
David Jeske

Jawaban:

207

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.TryParsesepenuhnya 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.

Jon Skeet
sumber
2
sayangnya, orang diberitahu bahwa pengecualian itu gratis, menggunakannya untuk fungsi 'benar' sepele, mereka harus digunakan seperti yang Anda katakan, ketika ada masalah - dalam keadaan 'luar biasa'
gbjbaanb
4
Ya, orang tentu harus menyadari bahwa ada biaya kinerja yang terkait dengan menggunakan pengecualian secara tidak tepat. Saya hanya berpikir itu adalah non-isu ketika mereka sedang digunakan secara tepat :)
Jon Skeet
7
@ PaulLockwood: Saya akan mengatakan bahwa jika Anda memiliki 200+ pengecualian per detik , Anda menggunakan pengecualian. Ini jelas bukan acara "luar biasa" jika terjadi 200 kali per detik. Perhatikan kalimat terakhir dari jawaban: "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."
Jon Skeet
4
@ PaulLockwood: Maksud saya adalah jika Anda memiliki lebih dari 200 pengecualian per detik, itu mungkin sudah menunjukkan bahwa Anda menyalahgunakan pengecualian. Itu tidak mengejutkan saya bahwa itu sering terjadi, tetapi itu berarti bahwa aspek kinerja tidak akan menjadi perhatian pertama saya - penyalahgunaan pengecualian akan menjadi. Setelah saya menghapus semua penggunaan pengecualian yang tidak pantas , saya tidak akan mengharapkan mereka menjadi bagian penting dari kinerja.
Jon Skeet
4
@ DavidJeske: Anda telah melewatkan titik jawabannya. Jelas melempar pengecualian jauh lebih lambat daripada mengembalikan nilai normal. Tidak ada yang memperdebatkan itu. Pertanyaannya adalah apakah mereka terlalu lambat. Jika Anda berada dalam situasi yang cocok untuk melempar pengecualian dan itu menyebabkan masalah kinerja, maka Anda mungkin memiliki masalah yang lebih besar - karena itu menunjukkan bahwa ada sejumlah besar kesalahan dengan sistem Anda. Biasanya, masalahnya adalah Anda benar - benar menggunakan pengecualian saat tidak pantas untuk memulainya.
Jon Skeet
31

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:

Bahkan, CLR secara internal menggunakan pengecualian bahkan di bagian mesin yang tidak dikelola. Namun, ada masalah kinerja jangka panjang yang serius dengan pengecualian dan ini harus diperhitungkan dalam keputusan Anda.

gbjbaanb
sumber
Saya dapat menyebutkan ini dalam tes dunia nyata, di mana tipe nullable menyebabkan pengecualian dinaikkan berkali-kali dalam "ini adalah aliran program normal", yang berakhir dengan masalah kinerja yang signifikan. Selalu ingat, pengecualian untuk kasus luar biasa, jangan percaya siapa pun yang mengatakan sebaliknya atau Anda akan berakhir dengan utas github seperti itu!
gbjbaanb
8

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.

Cory Foy
sumber
1
Tidak ada yang Anda tulis yang bertentangan dengan klaim bahwa pengecualian hanya lambat jika dilempar. Anda hanya berbicara tentang situasi di mana mereka yang dilemparkan. Ketika Anda telah "secara signifikan membantu kinerja" dengan menghapus pengecualian: 1) Apakah itu benar-benar kondisi kesalahan, atau hanya kesalahan pengguna ?
Jon Skeet
2) Apakah Anda berjalan di bawah debugger, atau tidak?
Jon Skeet
Satu-satunya hal yang mungkin dapat Anda lakukan dengan pengecualian jika tidak melemparnya adalah membuatnya sebagai objek, yang tidak ada artinya. Berada di bawah debugger atau tidak masalah - itu masih akan lebih lambat. Ya, ada kait yang akan terjadi dengan debugger terpasang, tetapi masih lambat
Cory Foy
4
Saya tahu - saya adalah bagian dari tim Premier di MSFT. :) Katakan saja, banyak - ribuan per detik dalam beberapa kasus ekstrim yang kami lihat. Tidak seperti menghubungkan dengan debugger langsung dan hanya melihat pengecualian secepat yang Anda bisa baca. Ex's lambat - begitu juga terhubung ke DB, jadi Anda melakukannya ketika itu masuk akal.
Cory Foy
5
Cory, saya pikir titik "hanya lambat ketika mereka dilemparkan" adalah bahwa Anda tidak perlu khawatir tentang kinerja karena hanya kehadiran menangkap / akhirnya menghalangi. Yaitu ini sendiri tidak menyebabkan hit kinerja, hanya terjadinya instance pengecualian yang sebenarnya.
Ian Horwill
6

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.

Abu
sumber
Saya pikir secara internal fungsi "coba" itu menggunakan pengecualian juga?
greg
1
Fungsi "Coba" ini tidak melempar pengecualian secara internal untuk kegagalan mengurai nilai input. Namun mereka masih melempar pengecualian untuk situasi kesalahan lainnya, seperti ArgumentException.
Ash
Saya pikir jawaban ini lebih dekat ke inti masalah daripada yang lain. Mengatakan 'gunakan pengecualian hanya dalam keadaan wajar' tidak benar-benar menjawab pertanyaan - wawasan sebenarnya adalah bahwa menggunakan pengecualian C # untuk aliran kontrol jauh lebih lambat daripada konstruksi kondisional yang biasa. Anda bisa dimaafkan jika berpikir sebaliknya. Dalam OCaml, pengecualian kurang lebih merupakan GOTO dan cara yang diterima untuk mengimplementasikan break ketika menggunakan fitur-fitur penting. Dalam kasus khusus saya, mengganti int.Parse loop ketat () ditambah try / catch vs int.TryParse () memberikan peningkatan kinerja yang signifikan.
Hugh W
4

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).

Jonathan C Dickinson
sumber
3
Angka akan berguna, sayangnya :( Hal-hal seperti operasi socket harus jauh lebih besar daripada biaya pengecualian, tentu saja ketika tidak melakukan debugging. Jika Anda pernah melakukan benchmark sepenuhnya, saya akan sangat tertarik untuk melihat hasilnya.
Jon Skeet
3

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".

Prisament Ray
sumber
2

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.

Berdalih
sumber
2

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.

Sklivvz
sumber
2

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)

int c = 1000000;
int s = Environment.TickCount;
for (int i = 0; i < c; i++)
{
    try { throw new Exception(); }
    catch { }
}
int d = Environment.TickCount - s;

Console.WriteLine(d + "ms / " + c + " exceptions");
linquize
sumber
1

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.

leppie
sumber
1

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.

baseline: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.25 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.5 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 0.75 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 1 (0), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0 (0), time elapsed 13.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.25 (249999), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.5 (499999), time elapsed 16.0009 ms
retval_error: recurse_depth 5, error_freqeuncy 0.75 (999999), time elapsed 16.001 ms
retval_error: recurse_depth 5, error_freqeuncy 1 (999999), time elapsed 16.0009 ms
retval_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 20.0011 ms
retval_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 21.0012 ms
retval_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 24.0013 ms
exception_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 31.0017 ms
exception_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 5607.3208     ms
exception_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 11172.639  ms
exception_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 22297.2753 ms
exception_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 22102.2641 ms

Dan ini kodenya ..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

public class TestIt {
    int value;

    public class TestException : Exception { } 

    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    public bool baseline_null(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            return shouldfail;
        } else {
            return baseline_null(shouldfail,recurse_depth-1);
        }
    }

    public bool retval_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                return false;
            } else {
                return true;
            }
        } else {
            bool nested_error = retval_error(shouldfail,recurse_depth-1);
            if (nested_error) {
                return true;
            } else {
                return false;
            }
        }
    }

    public void exception_error(bool shouldfail, int recurse_depth) {
        if (recurse_depth <= 0) {
            if (shouldfail) {
                throw new TestException();
            }
        } else {
            exception_error(shouldfail,recurse_depth-1);
        }

    }

    public static void Main(String[] args) {
        int i;
        long l;
        TestIt t = new TestIt();
        int failures;

        int ITERATION_COUNT = 1000000;


        // (0) baseline null workload
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    t.baseline_null(shoulderror,recurse_depth);
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "baseline: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }


        // (1) retval_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    if (!t.retval_error(shoulderror,recurse_depth)) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "retval_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));
            }
        }

        // (2) exception_error
        for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
            for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
                int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

                failures = 0;
                DateTime start_time = DateTime.Now;
                t.reset();              
                for (i = 1; i < ITERATION_COUNT; i++) {
                    bool shoulderror = (i % EXCEPTION_MOD) == 0;
                    try {
                        t.exception_error(shoulderror,recurse_depth);
                    } catch (TestException e) {
                        failures++;
                    }
                }
                double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
                Console.WriteLine(
                    String.Format(
                      "exception_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
                        recurse_depth, exception_freq, failures,elapsed_time));         }
        }
    }
}


}
David Jeske
sumber
5
Selain melewatkan inti pertanyaan, jangan gunakan DateTime. Sekarang untuk tolok ukur - gunakan Stopwatch, yang dirancang untuk mengukur waktu yang telah berlalu. Seharusnya tidak menjadi masalah di sini karena Anda mengukur periode waktu yang cukup lama tetapi layak untuk membiasakan diri.
Jon Skeet
Sebaliknya, pertanyaannya adalah "apakah pengecualian lambat", titik. Itu secara khusus diminta untuk menghindari topik kapan harus melempar pengecualian, karena topik itu mengaburkan fakta. Apa kinerja pengecualian?
David Jeske
0

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.

Drew Noakes
sumber
1
Namun, keberadaan pengecualian dapat memengaruhi optimisasi - metode dengan penangan pengecualian eksplisit lebih sulit untuk di sebaris, dan pengurutan ulang instruksi dibatasi olehnya.
Eamon Nerbonne
-1

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.

Pengecut Anonim
sumber
2
Ini sepertinya tidak sesuai dengan pernyataan poster " Saya tidak ingin diskusi tentang kapan dan tidak membuang pengecualian ".
hrbrmstr