Mengkompilasi file C ++ membutuhkan waktu yang sangat lama bila dibandingkan dengan C # dan Java. Diperlukan waktu lebih lama untuk mengkompilasi file C ++ daripada menjalankan skrip Python ukuran normal. Saat ini saya menggunakan VC ++ tetapi sama dengan kompiler apa pun. Kenapa ini?
Dua alasan yang bisa saya pikirkan adalah memuat file header dan menjalankan preprocessor, tetapi sepertinya itu tidak menjelaskan mengapa itu membutuhkan waktu begitu lama.
c++
performance
compilation
Dan Goldstein
sumber
sumber
It takes significantly longer to compile a C++ file
- maksud Anda 2 detik dibandingkan dengan 1 detik? Tentu saja itu dua kali lebih panjang, tetapi hampir tidak signifikan. Atau maksud Anda 10 menit dibandingkan dengan 5 detik? Tolong hitung.Jawaban:
Beberapa alasan
File tajuk
Setiap unit kompilasi tunggal membutuhkan ratusan atau bahkan ribuan header untuk (1) dimuat dan (2) dikompilasi. Masing-masing dari mereka biasanya harus dikompilasi ulang untuk setiap unit kompilasi, karena preprocessor memastikan bahwa hasil kompilasi header mungkin bervariasi antara setiap unit kompilasi. (Makro dapat didefinisikan dalam satu unit kompilasi yang mengubah konten header).
Ini mungkin yang alasan utama, karena membutuhkan jumlah besar kode untuk dikompilasi untuk setiap unit kompilasi, dan tambahan, setiap kepala harus dikompilasi beberapa kali (sekali untuk setiap unit kompilasi yang mencakup itu).
Menautkan
Setelah dikompilasi, semua file objek harus dihubungkan bersama. Ini pada dasarnya adalah proses monolitik yang tidak bisa diparalelkan dengan baik, dan harus memproses seluruh proyek Anda.
Parsing
Sintaksnya sangat rumit untuk diuraikan, sangat bergantung pada konteks, dan sangat sulit untuk disatukan. Ini membutuhkan banyak waktu.
Templat
Di C #,
List<T>
adalah satu-satunya jenis yang dikompilasi, tidak peduli berapa banyak instantiasi Daftar yang Anda miliki di program Anda. Dalam C ++,vector<int>
adalah tipe yang sepenuhnya terpisah darivector<float>
, dan masing-masing harus dikompilasi secara terpisah.Tambahkan ke ini bahwa templat membuat "sub-bahasa" Turing-lengkap lengkap yang harus ditafsirkan oleh kompiler, dan ini bisa menjadi sangat rumit. Bahkan kode metaprogramming templat yang relatif sederhana dapat menentukan templat rekursif yang membuat puluhan dan puluhan instantiasi templat. Template juga dapat menghasilkan tipe yang sangat kompleks, dengan nama yang sangat panjang, menambahkan banyak pekerjaan tambahan ke linker. (Itu harus membandingkan banyak nama simbol, dan jika nama-nama ini dapat tumbuh menjadi ribuan karakter, itu bisa menjadi cukup mahal).
Dan tentu saja, mereka memperburuk masalah dengan file header, karena template umumnya harus didefinisikan dalam header, yang berarti jauh lebih banyak kode harus diurai dan dikompilasi untuk setiap unit kompilasi. Dalam kode C sederhana, header biasanya hanya berisi deklarasi maju, tetapi kode aktual sangat sedikit. Di C ++, tidak jarang hampir semua kode berada di file header.
Optimasi
C ++ memungkinkan untuk beberapa optimasi yang sangat dramatis. C # atau Java tidak mengizinkan kelas untuk dihilangkan sama sekali (mereka harus ada di sana untuk tujuan refleksi), tetapi bahkan metaprogram template C ++ sederhana dapat dengan mudah menghasilkan puluhan atau ratusan kelas, yang semuanya digarisbawahi dan dihilangkan lagi dalam optimasi tahap.
Selain itu, program C ++ harus dioptimalkan sepenuhnya oleh kompiler. Program AC # dapat mengandalkan kompiler JIT untuk melakukan optimasi tambahan pada waktu buka, C ++ tidak mendapatkan "peluang kedua" semacam itu. Apa yang dihasilkan kompiler sama optimalnya dengan yang didapat.
Mesin
C ++ dikompilasi ke kode mesin yang mungkin agak lebih rumit daripada penggunaan bytecode Java atau .NET (terutama dalam kasus x86). (Ini disebutkan karena kelengkapan hanya karena disebutkan dalam komentar dan semacamnya. Dalam praktiknya, langkah ini tidak mungkin mengambil lebih dari sebagian kecil dari total waktu kompilasi).
Kesimpulan
Sebagian besar faktor ini dimiliki oleh kode C, yang sebenarnya mengkompilasi dengan cukup efisien. Langkah parsing jauh lebih rumit di C ++, dan dapat mengambil lebih banyak waktu secara signifikan, tetapi pelaku utama mungkin template. Mereka berguna, dan membuat C ++ bahasa yang jauh lebih kuat, tetapi mereka juga mengambil korban dalam hal kecepatan kompilasi.
sumber
Perlambatan belum tentu sama dengan kompiler apa pun.
Saya belum pernah menggunakan Delphi atau Kylix tetapi pada masa MS-DOS, program Turbo Pascal akan dikompilasi hampir secara instan, sedangkan program setara Turbo C ++ hanya akan merangkak.
Dua perbedaan utama adalah sistem modul yang sangat kuat dan sintaks yang memungkinkan kompilasi single-pass.
Sangat mungkin bahwa kecepatan kompilasi belum menjadi prioritas bagi pengembang kompiler C ++, tetapi ada juga beberapa komplikasi inheren dalam sintaks C / C ++ yang membuatnya lebih sulit untuk diproses. (Saya bukan ahli C, tetapi Walter Bright, dan setelah membangun berbagai kompiler C / C ++ komersial, ia menciptakan bahasa D. Salah satu perubahannya adalah untuk menegakkan tata bahasa bebas konteks untuk membuat bahasa lebih mudah diurai .)
Juga, Anda akan melihat bahwa umumnya Makefile diatur sehingga setiap file dikompilasi secara terpisah dalam C, jadi jika 10 file sumber semua menggunakan file yang sama, file yang termasuk diproses 10 kali.
sumber
Parsing dan pembuatan kode sebenarnya agak cepat. Masalah sebenarnya adalah membuka dan menutup file. Ingat, bahkan dengan menyertakan penjaga, kompilator masih harus membuka file .H, dan membaca setiap baris (dan kemudian mengabaikannya).
Seorang teman sekali (sementara bosan di tempat kerja), mengambil aplikasi perusahaannya dan memasukkan semuanya - semua file sumber dan header - ke dalam satu file besar. Waktu kompilasi turun dari 3 jam menjadi 7 menit.
sumber
Alasan lain adalah penggunaan prosesor pra-C untuk menemukan deklarasi. Bahkan dengan penjaga tajuk, .h masih harus diurai berulang kali, setiap kali mereka dimasukkan. Beberapa kompiler mendukung header yang dikompilasi sebelumnya yang dapat membantu dengan ini, tetapi mereka tidak selalu digunakan.
Lihat juga: C ++ Jawaban yang Sering Diajukan
sumber
C ++ dikompilasi ke dalam kode mesin. Jadi Anda memiliki pra-prosesor, kompiler, pengoptimal, dan akhirnya assembler, yang semuanya harus dijalankan.
Java dan C # dikompilasi ke dalam byte-code / IL, dan Java virtual machine / .NET Framework mengeksekusi (atau JIT mengkompilasi ke dalam kode mesin) sebelum dieksekusi.
Python adalah bahasa yang ditafsirkan yang juga dikompilasi ke dalam byte-code.
Saya yakin ada alasan lain untuk ini juga, tetapi secara umum, tidak harus mengkompilasi ke bahasa mesin asli menghemat waktu.
sumber
Masalah terbesar adalah:
1) Header infinite memar. Sudah disebutkan. Mitigasi (seperti #pragma sekali) biasanya hanya bekerja per unit kompilasi, bukan per build.
2) Fakta bahwa toolchain sering dipisahkan menjadi beberapa binari (make, preprocessor, compiler, assembler, archiver, impdef, linker, dan dlltool dalam kasus ekstrem) yang semuanya harus menginisialisasi ulang dan memuat ulang semua status sepanjang waktu untuk setiap pemanggilan ( kompiler, assembler) atau setiap pasangan file (pengarsipan, tautan, dan dlltool).
Lihat juga diskusi ini tentang comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078 khususnya yang ini:
http://compilers.iecc.com/comparch/article/02-07-128
Perhatikan bahwa John, moderator dari comp.compiler tampaknya setuju, dan ini berarti harus mungkin untuk mencapai kecepatan yang sama untuk C juga, jika seseorang mengintegrasikan toolchain sepenuhnya dan mengimplementasikan header yang telah dikompilasi. Banyak kompiler C komersial melakukan ini sampai taraf tertentu.
Perhatikan bahwa model Unix memfaktorkan semuanya ke biner terpisah adalah semacam model kasus terburuk untuk Windows (dengan proses pembuatannya yang lambat). Sangat terlihat ketika membandingkan waktu pembuatan GCC antara Windows dan * nix, terutama jika sistem make / configure juga memanggil beberapa program hanya untuk mendapatkan informasi.
sumber
Membangun C / C ++: apa yang sebenarnya terjadi dan mengapa perlu waktu lama
Sebagian besar waktu pengembangan perangkat lunak tidak dihabiskan untuk menulis, menjalankan, men-debug atau bahkan merancang kode, tetapi menunggu sampai selesai dikompilasi. Untuk mempercepat, pertama-tama kita harus memahami apa yang terjadi ketika perangkat lunak C / C ++ dikompilasi. Langkah-langkahnya kira-kira sebagai berikut:
Kita sekarang akan melihat setiap langkah lebih terinci dengan fokus pada bagaimana mereka dapat dibuat lebih cepat.
Konfigurasi
Ini adalah langkah pertama ketika mulai membangun. Biasanya berarti menjalankan skrip konfigurasi atau CMake, Gyp, SCons, atau alat lain. Ini bisa memakan waktu mulai dari satu detik hingga beberapa menit untuk skrip konfigurasi berbasis Autotools yang sangat besar.
Langkah ini jarang terjadi. Itu hanya perlu dijalankan ketika mengubah konfigurasi atau mengubah konfigurasi build. Pendek perubahan sistem membangun, tidak ada banyak yang harus dilakukan untuk membuat langkah ini lebih cepat.
Bangun alat startup
Inilah yang terjadi ketika Anda menjalankan make atau klik pada ikon build pada IDE (yang biasanya merupakan alias untuk make). Biner alat bangun memulai dan membaca file konfigurasinya serta konfigurasi build, yang biasanya merupakan hal yang sama.
Bergantung pada kompleksitas dan ukuran build, ini bisa memakan waktu mulai dari sepersekian detik hingga beberapa detik. Dengan sendirinya ini tidak akan terlalu buruk. Sayangnya sebagian besar sistem build berbasis make menyebabkan make untuk dipasok puluhan hingga ratusan kali untuk setiap build. Biasanya ini disebabkan oleh penggunaan rekursif (yang buruk).
Perlu dicatat bahwa alasan Make sangat lambat bukanlah bug implementasi. Sintaks Makefiles memiliki beberapa kebiasaan yang membuat implementasi sangat cepat tetapi mustahil. Masalah ini bahkan lebih terlihat ketika dikombinasikan dengan langkah selanjutnya.
Pemeriksaan ketergantungan
Setelah alat build membaca konfigurasinya, ia harus menentukan file apa yang telah berubah dan mana yang perlu dikompilasi ulang. File konfigurasi berisi grafik asiklik langsung yang menjelaskan dependensi build. Grafik ini biasanya dibangun selama langkah konfigurasi. Waktu startup alat bangun dan pemindai ketergantungan dijalankan di setiap bangunan. Runtime gabungan mereka menentukan batas bawah pada siklus edit-kompilasi-debug. Untuk proyek-proyek kecil kali ini biasanya beberapa detik atau lebih. Ini lumayan. Ada alternatif untuk Membuat. Yang tercepat dari mereka adalah Ninja, yang dibangun oleh insinyur Google untuk Chromium. Jika Anda menggunakan CMake atau Gyp untuk membangun, cukup beralih ke backend Ninja mereka. Anda tidak perlu mengubah apa pun di file build sendiri, cukup nikmati peningkatan kecepatan. Ninja tidak dikemas di sebagian besar distribusi,
Kompilasi
Pada titik ini kita akhirnya memanggil kompiler. Memotong beberapa sudut, berikut adalah langkah-langkah perkiraan yang diambil.
Berlawanan dengan kepercayaan umum, kompilasi C ++ sebenarnya tidak terlalu lambat. STL lambat dan sebagian besar alat pembangunan yang digunakan untuk mengkompilasi C ++ lambat. Namun ada alat yang lebih cepat dan cara untuk mengurangi bagian bahasa yang lambat.
Menggunakannya membutuhkan sedikit minyak siku, tetapi manfaatnya tidak bisa dipungkiri. Waktu pengembangan yang lebih cepat mengarah pada pengembang yang lebih bahagia, lebih gesit dan, akhirnya, kode yang lebih baik.
sumber
Bahasa yang dikompilasi selalu membutuhkan overhead awal yang lebih besar daripada bahasa yang ditafsirkan. Selain itu, mungkin Anda tidak menyusun kode C ++ dengan sangat baik. Sebagai contoh:
Kompilasi jauh lebih lambat daripada:
sumber
Cara mudah untuk mengurangi waktu kompilasi dalam proyek C ++ yang lebih besar adalah dengan membuat file * .cpp yang menyertakan semua file cpp di proyek Anda dan kompilasi. Ini mengurangi masalah ledakan tajuk menjadi satu kali. Keuntungan dari ini adalah bahwa kesalahan kompilasi masih akan mereferensikan file yang benar.
Misalnya, anggap Anda memiliki a.cpp, b.cpp dan c.cpp .. buat file: everything.cpp:
Kemudian kompilasi proyek dengan hanya membuat segalanya.cpp
sumber
Beberapa alasannya adalah:
1) Tata bahasa C ++ lebih kompleks daripada C # atau Java dan membutuhkan lebih banyak waktu untuk menguraikan.
2) (Lebih penting) compiler C ++ menghasilkan kode mesin dan melakukan semua optimasi selama kompilasi. C # dan Java berjalan setengah jalan dan membiarkan langkah-langkah ini ke JIT.
sumber
Pertukaran yang Anda dapatkan adalah bahwa program berjalan sedikit lebih cepat. Itu mungkin menjadi kenyamanan dingin bagi Anda selama pengembangan, tetapi bisa jadi masalah besar setelah pengembangan selesai, dan program ini hanya dijalankan oleh pengguna.
sumber
Sebagian besar jawaban agak tidak jelas dalam menyebutkan bahwa C # akan selalu berjalan lebih lambat karena biaya melakukan tindakan yang dalam C ++ dilakukan hanya sekali pada waktu kompilasi, biaya kinerja ini juga dipengaruhi oleh dependensi runtime (lebih banyak hal untuk dimuat agar dapat dilakukan untuk menjalankan), belum lagi bahwa program C # akan selalu memiliki jejak memori yang lebih tinggi, semuanya menghasilkan kinerja yang lebih erat terkait dengan kemampuan perangkat keras yang tersedia. Hal yang sama berlaku untuk bahasa lain yang ditafsirkan atau bergantung pada VM.
sumber
Ada dua masalah yang dapat saya pikirkan yang mungkin mempengaruhi kecepatan di mana program Anda di C ++ mengkompilasi.
MASALAH YANG MUNGKIN # 1 - MEMBUAT HEADER: (Ini mungkin atau mungkin belum pernah ditangani oleh jawaban atau komentar lain.) Microsoft Visual C ++ (AKA VC ++) mendukung header yang telah dikompilasi, yang sangat saya rekomendasikan. Saat Anda membuat proyek baru dan memilih jenis program yang Anda buat, jendela setup wizard akan muncul di layar Anda. Jika Anda menekan tombol "Next>" di bagian bawahnya, jendela akan membawa Anda ke halaman yang memiliki beberapa daftar fitur; pastikan bahwa kotak di sebelah opsi "Precompiled header" dicentang. (CATATAN: Ini adalah pengalaman saya dengan aplikasi konsol Win32 di C ++, tetapi ini mungkin tidak demikian dengan semua jenis program di C ++.)
MUNGKIN MASALAH # 2 - LOKASI YANG DITAWARKAN: Musim panas ini, saya mengambil kursus pemrograman, dan kami harus menyimpan semua proyek kami pada 8GB flash drive, karena komputer di lab yang kami gunakan dihapus setiap malam pada tengah malam, yang akan menghapus semua pekerjaan kami. Jika Anda mengkompilasi ke perangkat penyimpanan eksternal demi portabilitas / keamanan / dll., Itu bisa memakan waktu yang sangat lamawaktu (bahkan dengan header yang dikompilasi sebelumnya yang saya sebutkan di atas) untuk dikompilasi oleh program Anda, terutama jika itu adalah program yang cukup besar. Saran saya untuk Anda dalam hal ini adalah membuat dan mengkompilasi program pada hard drive komputer yang Anda gunakan, dan kapan pun Anda ingin / perlu berhenti mengerjakan proyek Anda karena alasan apa pun, pindahkan ke eksternal Anda. penyimpanan perangkat, dan kemudian klik ikon "Safely Remove Hardware and Eject Media", yang akan muncul sebagai flash drive kecil di belakang lingkaran hijau kecil dengan tanda centang putih di atasnya, untuk memutuskan sambungan.
Saya harap ini membantu Anda; beri tahu saya jika ada! :)
sumber