Seperti yang ditunjukkan orang lain, assert
adalah semacam benteng pertahanan terakhir Anda terhadap kesalahan programmer yang seharusnya tidak pernah terjadi. Itu adalah pemeriksaan kewarasan yang diharapkan tidak akan gagal ke kiri dan ke kanan pada saat Anda mengirim.
Itu juga dirancang untuk dihilangkan dari rilis rilis stabil, untuk alasan apa pun yang mungkin berguna bagi pengembang: estetika, kinerja, apa pun yang mereka inginkan. Ini adalah bagian dari apa yang memisahkan debug build dari build rilis, dan menurut definisi build rilis tidak memiliki pernyataan seperti itu. Jadi ada subversi desain jika Anda ingin merilis analog "rilis build dengan pernyataan di tempat" yang akan menjadi upaya rilis rilis dengan _DEBUG
definisi preprocessor dan tidak NDEBUG
terdefinisi; ini sebenarnya bukan rilis build lagi.
Desainnya bahkan meluas ke perpustakaan standar. Sebagai contoh yang sangat mendasar di antara banyak, banyak implementasi std::vector::operator[]
akan assert
pemeriksaan kewarasan untuk memastikan Anda tidak memeriksa vektor di luar batas. Dan perpustakaan standar akan mulai berkinerja jauh, jauh lebih buruk jika Anda mengaktifkan pemeriksaan seperti itu dalam rilis rilis. Patokan vector
menggunakanoperator[]
dan ctor pengisian dengan pernyataan seperti itu yang disertakan pada array dinamis lama yang biasa akan sering menunjukkan array dinamis menjadi jauh lebih cepat hingga Anda menonaktifkan pemeriksaan tersebut, sehingga mereka sering melakukan dampak kinerja jauh, jauh dari cara sepele. Pemeriksaan null pointer di sini dan pemeriksaan di luar batas sebenarnya dapat menjadi beban besar jika pemeriksaan seperti itu diterapkan jutaan kali pada setiap frame dalam loop kritis sebelum kode semudah mendereferensi pointer cerdas atau mengakses array.
Jadi, Anda kemungkinan besar menginginkan alat yang berbeda untuk pekerjaan itu dan yang tidak dirancang untuk dihilangkan dari rilis build jika Anda ingin rilis build yang melakukan pemeriksaan kewarasan seperti itu di bidang utama. Yang paling berguna yang saya temukan secara pribadi adalah logging. Dalam hal itu, ketika pengguna melaporkan bug, banyak hal menjadi lebih mudah jika mereka melampirkan log dan baris terakhir dari log memberi saya petunjuk besar tentang di mana bug itu terjadi dan apa yang mungkin terjadi. Kemudian, setelah mereproduksi langkah-langkah mereka dalam membangun debug, saya mungkin juga mendapatkan kegagalan pernyataan, dan bahwa kegagalan pernyataan lebih lanjut memberi saya petunjuk besar untuk merampingkan waktu saya. Namun karena logging relatif mahal, saya tidak menggunakannya untuk menerapkan pemeriksaan kewarasan tingkat sangat rendah seperti memastikan array tidak diakses di luar batas dalam struktur data generik.
Namun akhirnya, dan agak setuju dengan Anda, saya bisa melihat kasus yang masuk akal di mana Anda mungkin benar-benar ingin memberikan sesuatu kepada penguji seperti build debug selama pengujian alpha, misalnya, dengan sekelompok kecil penguji alfa yang, misalnya, menandatangani NDA . Di sana mungkin merampingkan pengujian alfa jika Anda memberikan sesuatu kepada penguji Anda selain versi rilis lengkap dengan beberapa info debug yang dilampirkan bersama dengan beberapa fitur debug / pengembangan seperti tes yang dapat dijalankan dan lebih banyak keluaran verbose saat mereka menjalankan perangkat lunak. Saya setidaknya melihat beberapa perusahaan game besar melakukan hal seperti itu untuk alpha. Tapi itu untuk sesuatu seperti pengujian alfa atau internal di mana Anda benar-benar mencoba untuk memberikan sesuatu kepada penguji selain dari rilis rilis. Jika Anda benar-benar mencoba mengirimkan rilis build, maka menurut definisi, seharusnya tidak_DEBUG
didefinisikan atau yang benar-benar membingungkan perbedaan antara "debug" dan "rilis" build.
Mengapa kode ini harus dihapus sebelum dirilis? Pemeriksaan tidak banyak menguras kinerja dan jika mereka gagal pasti ada masalah yang saya lebih suka pesan kesalahan yang lebih langsung tentang.
Seperti yang ditunjukkan di atas, cek tidak selalu sepele dari sudut pandang kinerja. Banyak yang mungkin sepele, tetapi sekali lagi, bahkan lib standar menggunakannya dan itu dapat mempengaruhi kinerja dengan cara yang tidak dapat diterima bagi banyak orang dalam banyak kasus jika, katakanlah, akses acak melintasi std::vector
memakan waktu 4 kali lebih lama dalam apa yang seharusnya menjadi rilis rilis yang dioptimalkan karena batas-batasnya memeriksa yang seharusnya tidak pernah gagal.
Dalam mantan tim kami benar-benar harus membuat matriks dan pustaka vektor kami mengecualikan beberapa pernyataan di jalur kritis tertentu hanya untuk membuat debug membangun berjalan lebih cepat, karena pernyataan itu memperlambat operasi matematika dengan lebih dari urutan besarnya ke titik di mana itu mulai mengharuskan kita untuk menunggu 15 menit sebelum kita bahkan dapat melacak kode yang diinginkan. Rekan kerja saya sebenarnya ingin menghapus sajaasserts
langsung karena mereka menemukan bahwa melakukan itu membuat perbedaan besar. Sebaliknya, kami memutuskan untuk membuat jalur debug kritis untuk menghindarinya. Ketika kami membuat jalur kritis tersebut menggunakan data vektor / matriks secara langsung tanpa melalui pemeriksaan batas, waktu yang diperlukan untuk melakukan operasi penuh (yang mencakup lebih dari sekadar vektor / matriks matematika) berkurang dari menit ke detik. Jadi itu adalah kasus yang ekstrem tetapi yang pasti penegasan tidak selalu diabaikan dari sudut pandang kinerja, bahkan tidak dekat.
Tapi juga hanya caranya saja asserts
yang dirancang. Jika mereka tidak memiliki dampak kinerja yang sangat besar di seluruh papan, maka saya mungkin lebih suka jika mereka dirancang sebagai lebih dari fitur membangun debug atau kita dapat menggunakan vector::at
yang mencakup batas memeriksa bahkan dalam rilis rilis membangun dan membuang di luar batas akses, misalnya (belum dengan hit kinerja besar). Tetapi saat ini saya menemukan desain mereka jauh lebih berguna, mengingat dampak kinerja besar mereka dalam kasus saya, sebagai fitur debug-build-only yang dihilangkan ketika NDEBUG
didefinisikan. Untuk kasus-kasus yang pernah saya tangani, itu membuat perbedaan besar untuk build rilis untuk mengecualikan cek kewarasan yang seharusnya tidak pernah benar-benar gagal sejak awal.
vector::at
vs. vector::operator[]
Saya pikir perbedaan dari dua metode ini menjadi inti dari ini juga sebagai alternatif: pengecualian. vector::operator[]
implementasi biasanya assert
untuk memastikan bahwa akses di luar batas akan memicu kesalahan yang mudah direproduksi ketika mencoba mengakses vektor di luar batas. Tetapi pelaksana perpustakaan melakukan ini dengan asumsi bahwa tidak akan ada biaya sepeser pun dalam rilis rilis yang dioptimalkan.
Sementara vector::at
itu disediakan yang selalu melakukan out of bounds memeriksa dan melempar bahkan dalam rilis rilis, tetapi memiliki penalti kinerja ke titik di mana saya sering melihat kode jauh lebih banyak menggunakan vector::operator[]
daripada vector::at
. Banyak desain C ++ menggemakan gagasan "membayar untuk apa yang Anda gunakan / butuhkan", dan banyak orang sering mendukung operator[]
, yang bahkan tidak repot-repot dengan batas-batas memeriksa membangun rilis, berdasarkan pada gagasan bahwa mereka tidak perlu batas memeriksa di rilis rilis dioptimalkan mereka. Tiba-tiba jika pernyataan diaktifkan dalam rilis build, kinerja keduanya akan identik, dan penggunaan vektor akan selalu lebih lambat dari array dinamis. Jadi sebagian besar dari desain dan manfaat pernyataan didasarkan pada gagasan bahwa mereka menjadi bebas dalam rilis rilis.
release_assert
Ini menarik setelah menemukan niat ini. Secara alami setiap kasus penggunaan akan berbeda, tetapi saya pikir saya akan menemukan beberapa kegunaan untuk release_assert
yang melakukan pengecekan dan akan merusak perangkat lunak yang menampilkan nomor baris dan pesan kesalahan bahkan dalam versi rilis.
Untuk beberapa kasus yang tidak jelas dalam kasus saya di mana saya tidak ingin perangkat lunak pulih dengan anggun seperti jika ada pengecualian. Saya ingin crash bahkan dalam rilis dalam kasus-kasus sehingga pengguna dapat diberi nomor baris untuk melaporkan ketika perangkat lunak menemukan sesuatu yang seharusnya tidak pernah terjadi, masih dalam ranah kewarasan memeriksa kesalahan programmer, bukan kesalahan input eksternal seperti pengecualian, tetapi cukup murah untuk dilakukan tanpa khawatir tentang biaya dalam rilis.
Sebenarnya ada beberapa kasus di mana saya akan menemukan crash keras dengan nomor baris dan pesan kesalahan lebih baik untuk pulih dari pengecualian dilemparkan yang mungkin cukup murah untuk disimpan dalam rilis. Dan ada beberapa kasus di mana tidak mungkin untuk pulih dari pengecualian, seperti kesalahan yang ditemui saat mencoba memulihkan dari yang sudah ada. Di sana saya akan menemukan yang sempurna untuk release_assert(!"This should never, ever happen! The software failed to fail!");
dan tentu saja itu akan menjadi murah karena cek akan dilakukan di dalam jalur yang luar biasa di tempat pertama dan tidak akan dikenakan biaya apa pun di jalur eksekusi normal.