JIT vs. Kompiler Statis
Seperti yang sudah dikatakan di posting sebelumnya, JIT dapat mengkompilasi IL / bytecode menjadi kode native saat runtime. Biaya sebesar itu disebutkan, tetapi tidak sampai pada kesimpulannya:
JIT memiliki satu masalah besar yaitu ia tidak dapat mengkompilasi semuanya: kompilasi JIT membutuhkan waktu, sehingga JIT hanya akan mengkompilasi beberapa bagian kode, sedangkan kompiler statis akan menghasilkan biner asli yang lengkap: Untuk beberapa jenis program, statis kompiler akan dengan mudah mengungguli JIT.
Tentu saja, C # (atau Java, atau VB) biasanya lebih cepat menghasilkan solusi yang layak dan kuat daripada C ++ (jika hanya karena C ++ memiliki semantik yang kompleks, dan pustaka standar C ++, meskipun menarik dan kuat, cukup buruk jika dibandingkan dengan yang lengkap. cakupan pustaka standar dari .NET atau Java), jadi biasanya, perbedaan antara C ++ dan .NET atau Java JIT tidak akan terlihat oleh sebagian besar pengguna, dan untuk biner yang sangat penting, Anda masih dapat memanggil pemrosesan C ++ dari C # atau Java (meskipun panggilan asli semacam ini bisa sangat mahal harganya) ...
Pemrograman C ++
Perhatikan bahwa biasanya, Anda membandingkan kode runtime C ++ dengan padanannya di C # atau Java. Tetapi C ++ memiliki satu fitur yang dapat mengungguli Java / C # di luar kotak, yaitu template metaprograming: Pemrosesan kode akan dilakukan pada waktu kompilasi (dengan demikian, meningkatkan waktu kompilasi secara signifikan), menghasilkan runtime nol (atau hampir nol).
Saya belum begitu melihat efek kehidupan nyata pada ini (saya bermain hanya dengan konsep, tetapi pada saat itu, perbedaannya adalah detik eksekusi untuk JIT, dan nol untuk C ++), tetapi ini layak untuk disebutkan, di samping fakta template metaprograming tidak sepele...
Sunting 2011-06-10: Dalam C ++, bermain dengan tipe dilakukan pada waktu kompilasi, yang berarti menghasilkan kode generik yang memanggil kode non-generik (misalnya parser generik dari string ke tipe T, memanggil API perpustakaan standar untuk tipe T yang dikenali, dan membuat parser mudah diperluas oleh penggunanya) sangat mudah dan sangat efisien, sedangkan padanan di Java atau C # paling sulit untuk ditulis, dan akan selalu lebih lambat dan diselesaikan pada waktu proses bahkan ketika jenisnya diketahui pada waktu kompilasi, artinya satu-satunya harapan Anda adalah agar JIT menyejajarkan semuanya.
...
Sunting 2011-09-20: Tim di belakang Blitz ++ ( Homepage , Wikipedia ) pergi ke arah itu, dan tampaknya, tujuan mereka adalah untuk mencapai kinerja FORTRAN pada kalkulasi ilmiah dengan memindahkan sebanyak mungkin dari eksekusi runtime ke waktu kompilasi, melalui metaprogramming template C ++ . Jadi " Saya belum begitu melihat efek kehidupan nyata tentang hal ini " bagian saya tulis di atas tampaknya tidak eksis dalam kehidupan nyata.
Penggunaan Memori C ++ Asli
C ++ memiliki penggunaan memori yang berbeda dari Java / C #, dan karenanya, memiliki kelebihan / kekurangan yang berbeda.
Tidak peduli optimasi JIT, tidak ada yang akan berjalan secepat akses pointer langsung ke memori (mari kita abaikan sebentar cache prosesor, dll.). Jadi, jika Anda memiliki data yang berdekatan dalam memori, mengaksesnya melalui pointer C ++ (yaitu pointer C ... Mari beri Caesar haknya) akan berjalan kali lebih cepat daripada di Java / C #. Dan C ++ memiliki RAII, yang membuat banyak pemrosesan jauh lebih mudah daripada di C # atau bahkan di Java. C ++ tidak perlu using
mencakup keberadaan objeknya. Dan C ++ tidak memiliki finally
klausa. Ini bukan sebuah kesalahan.
:-)
Dan meskipun struct mirip C # primitif, objek C ++ "pada tumpukan" tidak akan dikenakan biaya pada alokasi dan penghancuran, dan tidak memerlukan GC untuk bekerja di utas independen untuk melakukan pembersihan.
Sedangkan untuk fragmentasi memori, pengalokasi memori pada tahun 2008 bukanlah pengalokasi memori lama dari tahun 1980 yang biasanya dibandingkan dengan GC: Alokasi C ++ tidak dapat dipindahkan ke memori, benar, tetapi kemudian, seperti pada sistem file Linux: Siapa yang membutuhkan hard disk defragmenting saat fragmentasi tidak terjadi? Menggunakan pengalokasi yang tepat untuk tugas yang tepat harus menjadi bagian dari perangkat pengembang C ++. Sekarang, menulis pengalokasi tidaklah mudah, dan kemudian, kebanyakan dari kita memiliki hal-hal yang lebih baik untuk dilakukan, dan untuk sebagian besar penggunaan, RAII atau GC lebih dari cukup.
Edit 2011-10-04: Untuk contoh tentang pengalokasi yang efisien: Pada platform Windows, sejak Vista, Heap Fragmentasi Rendah diaktifkan secara default. Untuk versi sebelumnya, LFH dapat diaktifkan dengan memanggil fungsi WinAPI HeapSetInformation ). Di OS lain, pengalokasi alternatif disediakan (lihathttps://secure.wikimedia.org/wikipedia/en/wiki/Malloc untuk daftar)
Sekarang, model memori menjadi lebih rumit dengan munculnya teknologi multicore dan multithreading. Di bidang ini, saya kira .NET memiliki keunggulan, dan Java, saya diberi tahu, memegang posisi teratas. Sangat mudah bagi beberapa hacker "on the bare metal" untuk memuji kode "near the machine" miliknya. Tetapi sekarang, lebih sulit untuk menghasilkan perakitan yang lebih baik dengan tangan daripada membiarkan kompilator menjalankan tugasnya. Untuk C ++, kompilator biasanya menjadi lebih baik daripada peretas sejak satu dekade. Untuk C # dan Java, ini lebih mudah.
Namun, C ++ 0x standar baru akan memberlakukan model memori sederhana ke kompiler C ++, yang akan menstandarisasi (dan dengan demikian menyederhanakan) kode multiprosesing / paralel / threading yang efektif dalam C ++, dan membuat pengoptimalan menjadi lebih mudah dan aman bagi penyusun. Tapi kemudian, kita akan melihat dalam beberapa tahun jika janjinya dipenuhi.
C ++ / CLI vs. C # / VB.NET
Catatan: Di bagian ini, saya berbicara tentang C ++ / CLI, yaitu C ++ yang dihosting oleh .NET, bukan C ++ asli.
Minggu lalu, saya mengikuti pelatihan tentang pengoptimalan .NET, dan menemukan bahwa kompiler statis sangat penting. Sama pentingnya dari JIT.
Kode yang sama yang dikompilasi di C ++ / CLI (atau leluhurnya, Managed C ++) bisa jadi kali lebih cepat daripada kode yang sama yang diproduksi di C # (atau VB.NET, yang kompilernya menghasilkan IL yang sama daripada C #).
Karena kompilator statis C ++ jauh lebih baik untuk menghasilkan kode yang sudah dioptimalkan daripada C #.
Misalnya, fungsi sebaris dalam .NET terbatas pada fungsi yang bytecode-nya kurang atau sama dengan 32 byte. Jadi, beberapa kode dalam C # akan menghasilkan pengakses 40 byte, yang tidak akan pernah di-inline oleh JIT. Kode yang sama di C ++ / CLI akan menghasilkan aksesor 20 byte, yang akan di-inline oleh JIT.
Contoh lain adalah variabel sementara, yang hanya dikompilasi oleh kompilator C ++ sementara masih disebutkan dalam IL yang dihasilkan oleh kompilator C #. Pengoptimalan kompilasi statis C ++ akan menghasilkan lebih sedikit kode, sehingga mengizinkan pengoptimalan JIT yang lebih agresif lagi.
Alasannya berspekulasi adalah fakta bahwa compiler C ++ / CLI mendapatkan keuntungan dari teknik pengoptimalan yang luas dari compiler asli C ++.
Kesimpulan
Saya suka C ++.
Tapi sejauh yang saya lihat, C # atau Java semuanya merupakan taruhan yang lebih baik. Bukan karena mereka lebih cepat dari C ++, tetapi karena ketika Anda menambahkan kualitasnya, mereka akhirnya menjadi lebih produktif, membutuhkan lebih sedikit pelatihan, dan memiliki pustaka standar yang lebih lengkap daripada C ++. Dan untuk sebagian besar program, perbedaan kecepatannya (dengan satu atau lain cara) akan dapat diabaikan ...
Sunting (2011-06-06)
Pengalaman saya di C # /. NET
Saya sekarang memiliki 5 bulan pengkodean C # profesional yang hampir eksklusif (yang menambahkan hingga CV saya sudah penuh dengan C ++ dan Java, dan sentuhan C ++ / CLI).
Saya bermain dengan WinForms (Ahem ...) dan WCF (keren!), Dan WPF (Keren !!!! Baik melalui XAML dan raw C #. WPF sangat mudah Saya percaya Swing tidak bisa dibandingkan dengannya), dan C # 4.0.
Kesimpulannya adalah bahwa meskipun lebih mudah / lebih cepat untuk menghasilkan kode yang berfungsi di C # / Java daripada di C ++, jauh lebih sulit untuk menghasilkan kode yang kuat, aman, dan tangguh di C # (dan bahkan lebih sulit di Java) daripada di C ++. Ada banyak alasan, tetapi dapat diringkas oleh:
- Generik tidak sekuat template ( coba tulis metode Parse generik yang efisien (dari string ke T), atau yang setara dengan boost :: lexical_cast di C # yang efisien untuk memahami masalahnya )
- RAII tetap tak tertandingi ( GC masih bisa bocor (ya, saya harus menangani masalah itu) dan hanya akan menangani memori. Bahkan C #
using
tidak semudah dan sekuat itu karena menulis implementasi Buang yang benar itu sulit )
- C #
readonly
dan Java final
sama sekali tidak berguna seperti C ++const
( Tidak ada cara Anda dapat mengekspos data kompleks yang hanya dapat dibaca (Pohon Node, misalnya) di C # tanpa kerja yang luar biasa, sementara itu adalah fitur bawaan C ++. Data yang tidak dapat diubah adalah solusi yang menarik , tetapi tidak semuanya bisa dibuat tidak berubah, jadi itu tidak cukup, sejauh ini ).
Jadi, C # tetap menjadi bahasa yang menyenangkan selama Anda menginginkan sesuatu yang berhasil, tetapi bahasa yang membuat frustrasi saat Anda menginginkan sesuatu yang selalu dan aman berfungsi.
Java bahkan lebih membuat frustasi, karena memiliki masalah yang sama daripada C #, dan banyak lagi: Kurangnya using
kata kunci yang setara dengan C # , seorang kolega saya yang sangat terampil menghabiskan terlalu banyak waktu untuk memastikan sumber dayanya dibebaskan dengan benar, sedangkan padanan di C ++ akan mudah (menggunakan destruktor dan petunjuk cerdas).
Jadi saya kira keuntungan produktivitas C # / Java terlihat untuk sebagian besar kode ... sampai Anda membutuhkan kode untuk sesempurna mungkin. Hari itu, Anda akan tahu rasa sakit. (Anda tidak akan percaya apa yang diminta dari server dan aplikasi GUI kami ...).
Tentang Java sisi server dan C ++
Saya terus berhubungan dengan tim server (saya bekerja 2 tahun di antara mereka, sebelum kembali ke tim GUI), di sisi lain gedung, dan saya belajar sesuatu yang menarik.
Beberapa tahun terakhir, trennya adalah aplikasi server Java ditakdirkan untuk menggantikan aplikasi server C ++ lama, karena Java memiliki banyak kerangka kerja / alat, dan mudah dipelihara, diterapkan, dll. Dll.
... Sampai masalah latensi rendah muncul beberapa bulan terakhir ini. Kemudian, aplikasi server Java, tidak peduli pengoptimalan yang dilakukan oleh tim Java kami yang terampil, kalah bersaing dengan yang lama, server C ++ yang tidak benar-benar dioptimalkan.
Saat ini, keputusannya adalah mempertahankan server Java untuk penggunaan umum di mana performa masih penting, tidak peduli dengan target latensi rendah, dan secara agresif mengoptimalkan aplikasi server C ++ yang sudah lebih cepat untuk kebutuhan latensi rendah dan latensi ultra-rendah.
Kesimpulan
Tidak ada yang sesederhana yang diharapkan.
Java, dan bahkan lebih banyak lagi C #, adalah bahasa yang keren, dengan pustaka dan kerangka kerja standar yang luas, tempat Anda dapat membuat kode dengan cepat, dan segera mendapatkan hasil.
Tetapi ketika Anda membutuhkan kekuatan mentah, pengoptimalan yang kuat dan sistematis, dukungan kompiler yang kuat, fitur bahasa yang kuat dan keamanan mutlak, Java dan C # mempersulit untuk memenangkan persentase kualitas terakhir yang hilang tetapi penting yang Anda butuhkan untuk tetap berada di atas persaingan.
Seolah-olah Anda membutuhkan lebih sedikit waktu dan pengembang yang kurang berpengalaman di C # / Java daripada di C ++ untuk menghasilkan kode kualitas rata-rata, tetapi di sisi lain, saat Anda membutuhkan kode kualitas yang sangat baik untuk menyempurnakan, tiba-tiba lebih mudah dan lebih cepat untuk mendapatkan hasil tepat di C ++.
Tentu saja, ini adalah persepsi saya sendiri, mungkin terbatas pada kebutuhan khusus kita.
Tapi tetap saja, itulah yang terjadi hari ini, baik di tim GUI dan tim sisi server.
Tentu saja, saya akan memperbarui posting ini jika sesuatu yang baru terjadi.
Sunting (2011-06-22)
"Kami menemukan bahwa dalam hal kinerja, C ++ menang dengan selisih yang besar. Namun, itu juga membutuhkan upaya penyetelan yang paling ekstensif, banyak di antaranya dilakukan pada tingkat kecanggihan yang tidak akan tersedia untuk pemrogram rata-rata.
[...] Versi Java mungkin yang paling sederhana untuk diterapkan, tetapi yang paling sulit untuk dianalisis performanya. Secara khusus, efek seputar pengumpulan sampah rumit dan sangat sulit disetel. "
Sumber:
Sunting (2011-09-20)
"Kata-kata di Facebook adalah bahwa ' kode C ++ yang ditulis dengan wajar hanya berjalan cepat, ' yang menggarisbawahi upaya besar yang dihabiskan untuk mengoptimalkan kode PHP dan Java. Paradoksnya, kode C ++ lebih sulit untuk ditulis daripada di bahasa lain, tetapi kode yang efisien adalah jauh lebih mudah [untuk menulis dalam C ++ daripada dalam bahasa lain]. "
- Herb Sutter di // build / , mengutip Andrei Alexandrescu
Sumber: