Menggunakan "menangkap pengecualian" untuk meningkatkan keterbacaan, Baik atau Buruk?

9

Di bagian When to Use Exception dalam The Pragmatic Programmer , buku ini menulis bahwa alih-alih:

retcode = OK;
     if (socket.read(name) != OK) {
      retcode = BAD_READ;
    }
     else {
      processName(name);
       if (socket.read(address) != OK) {
        retcode = BAD_READ;
      }
       else {
        processAddress(address);
         if (socket.read(telNo) != OK) {
          retcode = BAD_READ;
        }
         else {
          //  etc, etc...
        }
      }
    }
     return retcode;

, mereka lebih suka:

 retcode = OK;
     try {
      socket.read(name);
      process(name);
      socket.read(address);
      processAddress(address);
      socket.read(telNo);
      //  etc, etc...
    }
     catch (IOException e) {
      retcode = BAD_READ;
      Logger.log( "Error reading individual: " + e.getMessage());
    }
     return retcode;

hanya karena tampilannya lebih rapi. Aku semua untuk kode lebih rapi, namun tidak perlu menangkap Pengecualian hambatan kinerja?

Saya bisa mengerti bahwa kita harus menyerah optimisasi sangat kecil untuk kode yang lebih rapi (setidaknya 99% dari waktu), namun dari apa yang saya tahu, menangkap pengecualian milik kelas kode yang memiliki keterlambatan nyata dalam runtime. Oleh karena itu saya bertanya-tanya apa pembenaran bahwa potongan kode kedua lebih disukai daripada kode pertama?

Atau lebih tepatnya, kode mana yang Anda inginkan?

Pacerier
sumber
dimigrasikan dari Code Review karena ini merupakan pertanyaan praktik terbaik bukan pertanyaan review kode
Winston Ewert
1
IMO, jika Anda mengharapkan untuk dapat membaca valuse mereka dari soket, maka IOEx adalah luar biasa.
Steven Evers
Relevan dengan diskusi ini: Menggunakan Throwable untuk Hal-Hal Selain Pengecualian
Daniel R Hicks
Sudahkah Anda mengukur?
@ ThorbjørnRavnAndersen tentu saja saya sedang membaca buku dan tidak memiliki "test case" nyata untuk mengukur .. tetapi jika kita mengukurnya dalam kode yang dibuat maka jelas ada penalti kinerja kemudian
Pacerier

Jawaban:

18

Pengecualian biasanya hanya lebih lambat jika mereka benar-benar dilempar. Biasanya, pengecualian dalam situasi seperti ini jarang terjadi sehingga itu bukan sesuatu yang harus Anda khawatirkan. Anda hanya harus benar-benar khawatir tentang kinerja pengecualian jika itu terjadi sepanjang waktu dan dengan demikian merupakan faktor yang signifikan.

Kode kedua lebih baik karena lebih mudah untuk mengikuti logika. Penanganan kesalahan dilokalkan dengan baik. Satu-satunya keberatan adalah bahwa jika Anda menggunakan pengecualian, maka gunakan pengecualian. Jangan menangkap pengecualian dan lalu kembalikan kode kesalahan.

Winston Ewert
sumber
4
Setuju, saya benci melihat kode kesalahan dalam bahasa yang memiliki pengecualian. Sebagian besar waktu Anda harus menangkap, login, rethrow tanpa memecahkan tumpukan panggilan. Dan sebagian besar waktu, tidak masalah jika kasus "pengecualian" lebih lambat. Aplikasi memiliki masalah, luangkan waktu untuk menanganinya dengan benar. Selama pengecualian itu luar biasa, dan bukan bagian dari operasi normal, atau jalan bahagia, dari aplikasi, itu biasanya bukan bau kode.
CaffGeek
Btw saya bertanya-tanya bagaimana kita membenarkan persepsi EOFE yang digunakan DataStreams untuk mendeteksi suatu akhir baris (bukan akhir baris itu) seperti yang ditunjukkan dalam docs.oracle.com/javase/tutorial/essential/io/datastreams.html ?
Pacerier
@Pacerier, ini bisa jadi luar biasa. Kehabisan input di baris dapat menunjukkan data buruk atau sesuatu yang salah terjadi. Saya menduga penggunaan yang dimaksudkan adalah untuk mem-parsing beberapa bidang di luar garis.
Winston Ewert
@Downvoter, jika Anda memiliki masalah dengan jawaban, mohon jelaskan!
Winston Ewert
5

Yah, seperti yang mereka katakan, pengecualian harus menangani kasus luar biasa , dan karena ini adalah kasus luar biasa (yaitu sesuatu yang terjadi sangat sedikit di antaranya) Saya akan mengatakan bahwa ini berlaku juga di sini.

Juga, saya akan menyarankan agar hal-hal yang tidak mengoptimalkan mikro seperti ini. Lebih fokus pada keterbacaan kode , karena Anda akan menghabiskan lebih banyak waktu membaca kode daripada menulis kode baru.

Untuk benar-benar memastikan bahwa ini adalah hambatan kinerja, lakukan beberapa profil, dan analisis dan fokus pada tugas-tugas yang lebih kritis berdasarkan analisis itu.

Andreas Johansson
sumber
5

Saya bisa mengerti bahwa kita harus menyerah optimisasi sangat kecil untuk kode yang lebih rapi (setidaknya 99% dari waktu), namun dari apa yang saya tahu, menangkap pengecualian milik kelas kode yang memiliki keterlambatan nyata dalam runtime.

Dari mana Anda tahu itu? Bagaimana Anda mendefinisikan "keterlambatan nyata"?

Ini persis seperti mitos kinerja yang samar-samar (dan sepenuhnya salah) yang mengarah pada optimalisasi prematur yang tidak berguna.

Melempar dan menangkap pengecualian mungkin lambat dibandingkan dengan menambahkan dua angka, tetapi sama sekali tidak signifikan dibandingkan dengan membaca data dari soket. Anda mungkin bisa melempar dan menangkap ribuan pengecualian dalam waktu yang bersamaan Anda membaca satu paket jaringan. Dan kita tidak berbicara tentang ribuan pengecualian di sini, hanya satu pengecualian.

Michael Borgwardt
sumber
Saya berasal dari .NET, jadi maafkan pengetahuan saya tentang Java, tetapi saya secara pribadi telah mengalami pengecualian dalam menangkap .NET menyebabkan penundaan gila (besarnya detik) yang diperbaiki dengan menghapus "kode pengecualian-penangkapan" ..
Pacerier
@Pacerier, serius? besarnya detik? Saya kira mungkin itu menangkap banyak pengecualian? Lalu aku bisa melihat itu.
Winston Ewert
3
@Pacerier: Jika pengecualian menyebabkan penundaan gila, jelas apa yang menyebabkannya tidak luar biasa, dan karenanya harus ditangani dengan cara lain. Tidak ada sistem yang harus mengambil detik untuk memproses pengecualian, dan saya pikir Microsoft cukup kompeten untuk menghindarinya.
David Thornley
5

Saya setuju dengan jawaban yang Anda katakan bahwa ini adalah kasus luar biasa, jadi cukup masuk akal untuk menggunakan penanganan pengecualian.

Mengatasi masalah kinerja Anda secara lebih langsung, saya pikir Anda perlu menjaga skala tentang berbagai hal. Melempar / menangkap pengecualian dalam kondisi ini cenderung mengambil satu urutan mikrodetik. Saat kontrol aliran berjalan, itu lambat - berkali-kali lebih lambat dari ifpernyataan normal , tidak ada pertanyaan tentang itu.

Pada saat yang sama, ingatlah bahwa apa yang Anda lakukan di sini adalah membaca data dari jaringan. Dengan kecepatan 10 megabit per detik (lambat untuk jaringan lokal, tetapi cukup cepat saat koneksi Internet berjalan), itu berada pada urutan yang sama dengan waktu untuk membaca satu byte informasi.

Tentu saja, overhead itu hanya terjadi ketika Anda benar-benar melempar pengecualian - ketika itu tidak dilempar, biasanya ada sedikit atau tidak ada overhead sama sekali (setidaknya dari apa yang saya lihat, overhead yang paling signifikan bukan dari penanganan pengecualian itu sendiri, seperti dari menambahkan lebih banyak potensi aliran kontrol, membuat kode lebih sulit bagi kompiler untuk mengoptimalkan juga)

Dalam hal ini, ketika pengecualian dilemparkan, data tulisan Anda ke log. Mengingat sifat dari pencatatan, kemungkinan Anda cukup bagus untuk mengeluarkannya segera setelah Anda menulisnya (untuk memastikan tidak hilang). Menulis ke disk adalah (sekali lagi) operasi yang cukup lambat - Anda memerlukan SSD kelas enterprise yang cukup cepat bahkan untuk masuk ke kisaran puluhan ribu IOP / detik. Disk yang digunakan untuk logging mungkin dengan mudah dibatasi pada sesuatu seperti ratusan IOP / detik.

Intinya: ada kasus di mana pengecualian menangani overhead bisa menjadi signifikan, tetapi ini hampir pasti bukan salah satu dari mereka.

Jerry Coffin
sumber