Harus saya akui, bahwa biasanya saya tidak pernah repot untuk beralih antara konfigurasi Debug dan Release dalam program saya, dan saya biasanya memilih untuk pergi untuk konfigurasi Debug , bahkan ketika program-program tersebut sebenarnya digunakan di tempat pelanggan.
Sejauh yang saya tahu, satu-satunya perbedaan antara konfigurasi ini jika Anda tidak mengubahnya secara manual adalah bahwa Debug memiliki DEBUG
konstanta yang didefinisikan, dan Release telah memeriksa kode Optimize .
Jadi pertanyaan saya sebenarnya ada dua:
Apakah ada banyak perbedaan kinerja antara kedua konfigurasi ini. Apakah ada jenis kode tertentu yang akan menyebabkan perbedaan besar dalam kinerja di sini, atau sebenarnya tidak terlalu penting?
Apakah ada jenis kode yang akan berjalan dengan baik di bawah konfigurasi Debug yang mungkin gagal di bawah konfigurasi Release , atau dapatkah Anda yakin bahwa kode yang diuji dan bekerja dengan baik di bawah konfigurasi Debug juga akan bekerja dengan baik di bawah konfigurasi Release.
sumber
Jawaban:
Compiler C # itu sendiri tidak banyak mengubah IL yang dipancarkan dalam rilis build. Yang perlu dicatat adalah bahwa itu tidak lagi memancarkan opcode NOP yang memungkinkan Anda untuk mengatur breakpoint pada kurung kurawal. Yang besar adalah pengoptimal yang dibangun ke dalam kompiler JIT. Saya tahu itu membuat optimasi berikut:
Metode inlining. Panggilan metode diganti dengan menyuntikkan kode metode. Ini yang besar, itu membuat aksesor properti pada dasarnya gratis.
Alokasi register CPU. Variabel lokal dan argumen metode dapat tetap disimpan dalam register CPU tanpa pernah (atau kurang sering) disimpan kembali ke frame stack. Ini adalah yang besar, penting untuk menyulitkan debugging kode yang dioptimalkan. Dan memberi arti pada kata kunci yang mudah menguap .
Array eliminasi indeks memeriksa. Optimasi penting ketika bekerja dengan array (semua kelas koleksi .NET menggunakan array secara internal). Ketika kompiler JIT dapat memverifikasi bahwa loop tidak pernah mengindeks array dari batas maka itu akan menghilangkan pemeriksaan indeks. Yang besar.
Ulangi membuka gulungan. Loop dengan tubuh kecil ditingkatkan dengan mengulangi kode hingga 4 kali dalam tubuh dan looping lebih sedikit. Mengurangi biaya cabang dan meningkatkan opsi eksekusi skalar super-prosesor.
Penghapusan kode mati. Pernyataan seperti jika (false) {/ ... /} akan sepenuhnya dihilangkan. Ini dapat terjadi karena pelipatan dan pelurusan yang konstan. Kasus lain adalah ketika kompiler JIT dapat menentukan bahwa kode tidak memiliki efek samping yang mungkin. Optimalisasi inilah yang membuat kode profil sangat rumit.
Mengangkat kode. Kode di dalam loop yang tidak terpengaruh oleh loop dapat dipindahkan keluar dari loop. Pengoptimal dari kompiler C akan menghabiskan lebih banyak waktu untuk mencari peluang untuk diangkat. Namun ini adalah optimasi mahal karena analisis aliran data yang diperlukan dan jitter tidak mampu membayar waktu sehingga hanya mengangkat kasus yang jelas. Memaksa .NET programmer untuk menulis kode sumber yang lebih baik dan mengangkat sendiri.
Penghapusan sub-ekspresi umum. x = y + 4; z = y + 4; menjadi z = x; Cukup umum dalam pernyataan seperti dest [ix + 1] = src [ix + 1]; ditulis untuk keterbacaan tanpa memperkenalkan variabel pembantu. Tidak perlu kompromi keterbacaan.
Lipat konstan. x = 1 + 2; menjadi x = 3; Contoh sederhana ini ditangkap lebih awal oleh kompiler, tetapi terjadi pada waktu JIT ketika optimasi lain memungkinkan ini.
Salin propagasi. x = a; y = x; menjadi y = a; Ini membantu pengalokasi register membuat keputusan yang lebih baik. Ini adalah masalah besar dalam x86 jitter karena memiliki beberapa register yang dapat digunakan. Setelah memilih yang tepat sangat penting untuk kinerja.
Ini adalah optimasi yang sangat penting yang dapat membuat besar kesepakatan perbedaan ketika, misalnya, Anda profil Debug membangun aplikasi Anda dan bandingkan dengan Release membangun. Itu hanya benar-benar penting meskipun ketika kode berada di jalur kritis Anda, 5 hingga 10% dari kode yang Anda tulis yang benar - benar mempengaruhi kinerja program Anda. Pengoptimal JIT tidak cukup pintar untuk mengetahui apa yang penting di muka, itu hanya dapat menerapkan tombol "putar ke sebelas" untuk semua kode.
Hasil efektif dari optimasi ini pada waktu eksekusi program Anda sering dipengaruhi oleh kode yang berjalan di tempat lain. Membaca file, mengeksekusi query dbase, dll. Membuat pekerjaan pengoptimal JIT benar-benar tidak terlihat. Itu tidak masalah :)
Pengoptimal JIT adalah kode yang cukup andal, sebagian besar karena telah diuji jutaan kali. Sangat jarang memiliki masalah dalam versi rilis program Anda. Namun itu terjadi. Kegelisahan x64 dan x86 memiliki masalah dengan struct. Jitter x86 memiliki masalah dengan konsistensi titik mengambang, menghasilkan hasil yang sedikit berbeda ketika perantara perhitungan titik mengambang disimpan dalam register FPU pada presisi 80-bit alih-alih terpotong saat disiram ke memori.
sumber
LinkedList<T>
tidak, meskipun itu tidak sering digunakan.volatile
kunci tidak berlaku untuk variabel lokal yang disimpan dalam bingkai tumpukan. Dari dokumentasi di msdn.microsoft.com/en-us/library/x13ttww7.aspx : "Kata kunci yang mudah menguap hanya dapat diterapkan ke bidang kelas atau struct. Variabel lokal tidak dapat dinyatakan volatil."Debug
danRelease
membangun dalam hal ini adalah kotak centang "optimalkan kode" yang biasanya aktifRelease
tetapi mati untukDebug
. Hanya untuk memastikan pembaca tidak mulai berpikir bahwa ada "keajaiban", perbedaan tak terlihat antara dua konfigurasi build yang melampaui apa yang ditemukan di halaman properti proyek di Visual Studio.Ya, ada banyak perbedaan kinerja dan ini benar-benar berlaku di seluruh kode Anda. Debug melakukan sangat sedikit optimasi kinerja, dan mode rilis sangat banyak;
Hanya kode yang bergantung pada
DEBUG
konstanta yang dapat bekerja secara berbeda dengan rilis build. Selain itu, Anda seharusnya tidak melihat masalah.Contoh kode kerangka kerja yang bergantung pada
DEBUG
konstanta adalahDebug.Assert()
metode, yang atributnya[Conditional("DEBUG)"]
didefinisikan. Ini berarti bahwa itu juga tergantung padaDEBUG
konstanta dan ini tidak termasuk dalam rilis rilis.sumber
DEBUG
:AppDomain.CurrentDomain.GetAssemblies().Sum(p => p.GetTypes().Sum(p1 => p1.GetProperties().Length))
.asp.net
menggunakan dan menggunakan debug alih-alih melepaskan beberapa skrip mungkin ditambahkan pada halaman Anda, seperti:MicrosoftAjax.debug.js
yang memiliki sekitar 7k baris.Ini sangat tergantung pada sifat aplikasi Anda. Jika aplikasi Anda berat-UI, Anda mungkin tidak akan melihat perbedaan karena komponen paling lambat yang terhubung ke komputer modern adalah pengguna. Jika Anda menggunakan beberapa animasi UI, Anda mungkin ingin menguji apakah Anda dapat melihat kelambatan yang terlihat saat berjalan di DEBUG build.
Namun, jika Anda memiliki banyak perhitungan yang berat perhitungan, maka Anda akan melihat perbedaan (bisa setinggi 40% seperti yang disebutkan @Pieter, meskipun itu akan tergantung pada sifat perhitungannya).
Ini pada dasarnya adalah tradeoff desain. Jika Anda merilis di bawah DEBUG build, maka jika pengguna mengalami masalah, Anda bisa mendapatkan traceback yang lebih bermakna dan Anda dapat melakukan diagnostik yang jauh lebih fleksibel. Dengan melepaskan DEBUG build, Anda juga menghindari pengoptimal yang menghasilkan Heisenbugs yang tidak jelas .
sumber
Pengalaman saya adalah bahwa aplikasi berukuran sedang atau lebih besar terasa lebih responsif dalam rilis Rilis. Cobalah dengan aplikasi Anda dan lihat bagaimana rasanya.
Satu hal yang dapat menggigit Anda dengan rilis build adalah bahwa kode build Debug kadang-kadang dapat menekan kondisi ras dan bug lain yang terkait dengan threading. Kode yang dioptimalkan dapat menghasilkan penyusunan ulang instruksi dan eksekusi yang lebih cepat dapat memperburuk kondisi balapan tertentu.
sumber
Anda seharusnya tidak pernah merilis .NET Debug build ke dalam produksi. Mungkin berisi kode jelek untuk mendukung Edit-dan-Lanjutkan atau siapa yang tahu apa lagi. Sejauh yang saya tahu, ini hanya terjadi di VB bukan C # (catatan: posting asli ditandai C #) , tetapi harus tetap memberikan alasan untuk berhenti sejenak tentang apa yang menurut Microsoft mereka boleh lakukan dengan membangun Debug. Bahkan, sebelum .NET 4.0, kode VB bocor memori sebanding dengan jumlah instance objek dengan peristiwa yang Anda buat dalam mendukung Edit-dan-Lanjutkan. (Meskipun ini dilaporkan diperbaiki per https://connect.microsoft.com/VisualStudio/feedback/details/481671/vb-classes-with-events-are-not-garbage-collected-when-debugging , kode yang dihasilkan terlihat jahat, membuat
WeakReference
objek dan menambahkannya ke daftar statis sementaramemegang kunci) Saya tentu tidak menginginkan dukungan debugging semacam ini di lingkungan produksi!sumber
Dalam pengalaman saya, hal terburuk yang telah keluar dari mode Release adalah "bug rilis" yang tidak jelas. Karena IL (bahasa perantara) dioptimalkan dalam mode Rilis, ada kemungkinan bug yang tidak akan terwujud dalam mode Debug. Ada pertanyaan SO lainnya yang mencakup masalah ini: Alasan umum untuk bug dalam versi rilis tidak hadir dalam mode debug
Ini terjadi pada saya sekali atau dua kali di mana aplikasi konsol sederhana akan berjalan dengan baik dalam mode Debug, tetapi diberi input yang sama persis, akan keluar dalam mode rilis. Bug ini sangat sulit di-debug (dengan definisi mode Release, ironisnya).
sumber
Saya akan mengatakan bahwa 1) sangat tergantung pada implementasi Anda. Biasanya, perbedaannya tidak terlalu besar. Saya melakukan banyak pengukuran dan seringkali saya tidak melihat perbedaan. Jika Anda menggunakan kode yang tidak dikelola, banyak array besar dan hal-hal seperti itu, perbedaan kinerja sedikit lebih besar, tetapi bukan dunia yang berbeda (seperti di C ++). 2) Biasanya dalam kode rilis lebih sedikit kesalahan ditampilkan (toleransi yang lebih tinggi), maka switch harus berfungsi dengan baik.
sumber
sumber