Menulis dalam C untuk Kinerja? [Tutup]

32

Saya tahu saya cukup sering mendengar bahwa C biasanya memiliki keunggulan kinerja lebih dari C ++. Saya tidak benar-benar memikirkan hal lain sampai saya menyadari bahwa MSVC tampaknya tidak mendukung standar terbaru C, tetapi yang terbaru mendukungnya C99 (sejauh yang saya tahu).

Saya berencana menulis perpustakaan dengan beberapa kode untuk di-render di OpenGL sehingga saya bisa menggunakannya kembali. Saya berencana untuk menulis perpustakaan di C karena setiap peningkatan kinerja diterima ketika datang ke grafis.

Tetapi apakah itu benar-benar layak? Kode yang menggunakan perpustakaan kemungkinan akan ditulis dalam C ++ dan saya lebih suka kode dalam C ++ secara umum.

Namun, jika itu akan menghasilkan bahkan perbedaan kecil dalam kinerja, saya mungkin akan pergi dengan C.

Dapat juga dicatat bahwa perpustakaan ini akan menjadi sesuatu yang akan saya buat untuk bekerja di Windows / OS X / Linux, dan saya mungkin akan mengkompilasi semuanya secara asli (MSVC untuk Windows, Dentang atau GCC untuk OS X, dan GCC untuk Linux .. .atau mungkin kompiler Intel untuk semuanya).

Saya telah melihat sekeliling dan saya telah menemukan beberapa tolok ukur dan semacamnya, tetapi semua yang saya lihat lebih berkaitan dengan GCC daripada MSVC dan Dentang. Juga, tolok ukur tidak menyebutkan standar bahasa yang digunakan. Adakah yang punya pemikiran tentang ini?

EDIT:Saya hanya ingin membagikan sudut pandang saya tentang pertanyaan ini setelah beberapa tahun pengalaman lebih. Saya akhirnya menulis proyek yang saya ajukan pertanyaan ini di C ++. Saya memulai proyek lain sekitar waktu yang sama di C ketika kami mencari untuk keluar dalam jumlah kecil kinerja yang kami bisa dan membutuhkan proyek yang dapat dihubungkan dalam C. Beberapa bulan yang lalu, saya mencapai titik di mana saya benar-benar membutuhkan peta dan maju manipulasi string. Saya tahu kemampuan untuk ini di pustaka standar C ++ dan akhirnya sampai pada kesimpulan bahwa struktur-struktur di pustaka standar kemungkinan akan mengungguli dan lebih stabil daripada peta dan string yang bisa saya implementasikan dalam C dalam jumlah waktu yang wajar. Persyaratan untuk dapat dihubungkan dalam C mudah dipenuhi dengan menulis antarmuka C ke kode C ++, yang dilakukan dengan cepat dengan jenis buram. Menulis ulang pustaka dalam C ++ tampaknya jauh lebih cepat daripada saat menulisnya di C dan kurang rentan terhadap bug, terutama kebocoran memori. Saya juga bisa menggunakan pustaka threading library standar, yang jauh lebih mudah daripada menggunakan implementasi platform-spesifik. Pada akhirnya, saya percaya menulis perpustakaan di C ++ membawa manfaat besar dengan kemungkinan biaya kinerja yang kecil. Saya belum membandingkan versi C ++, tetapi saya percaya bahwa mungkin saja saya telah mendapatkan beberapa kinerja dengan menggunakan struktur data perpustakaan standar daripada yang saya tulis. Saya percaya menulis perpustakaan di C ++ membawa manfaat besar dengan kemungkinan biaya kinerja yang kecil. Saya belum membandingkan versi C ++, tetapi saya percaya bahwa mungkin saja saya telah mendapatkan beberapa kinerja dengan menggunakan struktur data perpustakaan standar daripada yang saya tulis. Saya percaya menulis perpustakaan di C ++ membawa manfaat besar dengan kemungkinan biaya kinerja yang kecil. Saya belum membandingkan versi C ++, tetapi saya percaya bahwa mungkin saja saya telah mendapatkan beberapa kinerja dengan menggunakan struktur data perpustakaan standar daripada yang saya tulis.

danielunderwood
sumber
9
Dukungan MSVC terbaru sebenarnya adalah C89.
detly
4
@detly Dalam Visual Studio 2013, sebagian besar fitur C99 didukung . Ini bukan dukungan penuh, tetapi saya berani bertaruh pada praktiknya tidak masalah untuk menggunakannya untuk menulis C99.
congusbongus
4
@ danielu13 - Di mana tepatnya Anda mendengar bahwa C memiliki keunggulan kinerja dibandingkan C ++?
Ramhound
1
@ Sebastian-LaurenţiuPlesciuc Saya pikir tautan-tautan itu sebenarnya tidak membantu. Yang pertama bisa dilawan dengan pertanyaan yang hampir sama oleh programmer.stackexchange.com/q/113295/76444 tetapi lebih memilih c ++ daripada c seperti di tautan Anda. Untuk tautan kedua Anda, itu hanya kata-kata kasar dari linus torvalds. Saya harap semua orang tahu sekarang bahwa dia benar-benar suka membenci c ++ dan tidak akan menyentuhnya dengan tongkat tetapi kata-katanya tentang c ++ hampir tidak obyektif, mereka penuh dengan pendapat dan bias pribadi dan tidak benar-benar mencerminkan realitas bahasa . Setidaknya itulah saya pendapat .
user1942027
1
@RaphaelMiedl Juga saya sebutkan ini ditulis pada tahun 2007 yang cukup lama, kompiler C ++ dan bahasa C ++ telah berkembang sejak saat itu. Apapun, itu tergantung pada programmer untuk memilih bahasa apa yang digunakan.
Sebastian-Laurenţiu Plesciuc

Jawaban:

89

Saya kira orang sering mengklaim bahwa C lebih cepat daripada C ++ karena lebih mudah untuk alasan tentang kinerja dalam C. C ++ secara inheren lebih lambat atau lebih cepat, tetapi kode C ++ tertentu mungkin mengaburkan hukuman kinerja tersembunyi. Misalnya, mungkin ada salinan dan konversi tersirat yang tidak segera terlihat ketika melihat beberapa bagian dari kode C ++.

Mari kita ambil pernyataan berikut:

foo->doSomething(a + 5, *c);

Mari kita asumsikan lebih lanjut yang doSomethingmemiliki tanda tangan berikut:

void doSomething(int a, long b);

Sekarang, mari kita coba menganalisis kemungkinan dampak kinerja pernyataan khusus ini.

Dalam C, implikasinya cukup jelas. foohanya dapat menjadi pointer ke struct, dan doSomethingharus menjadi pointer ke suatu fungsi. *creferensi panjang, dan a + 5penambahan bilangan bulat. Satu-satunya ketidakpastian berasal dari jenis a: Jika bukan int, akan ada beberapa konversi. tetapi terlepas dari itu, mudah untuk mengukur dampak kinerja dari pernyataan tunggal ini.

Sekarang mari kita beralih ke C ++. Pernyataan yang sama sekarang dapat memiliki karakteristik kinerja yang sangat berbeda:

  1. doSomethingbisa berupa fungsi anggota non-virtual (murah), fungsi anggota virtual (sedikit lebih mahal) std::function,, lambda ... dll. Yang lebih buruk, foobisa jadi tipe kelas kelebihan beban operator->dengan beberapa operasi dengan kompleksitas yang tidak diketahui. Jadi, untuk menghitung biaya panggilan doSomething, sekarang perlu mengetahui sifat pasti foodan doSomething.
  2. abisa berupa integer, atau referensi ke integer (tipuan tambahan), atau tipe kelas yang mengimplementasikan operator+(int). Operator bahkan dapat mengembalikan tipe kelas lain yang secara implisit dapat dikonversi int. Sekali lagi, biaya kinerja tidak terlihat dari pernyataan itu sendiri.
  3. cbisa menjadi tipe implementasi kelas operator*(). Bisa juga referensi ke yang long*lain.

Anda mendapatkan fotonya. Karena fitur bahasa C ++, jauh lebih sulit untuk mengukur biaya kinerja pernyataan tunggal daripada di C. Sekarang juga, abstraksi seperti std::vector, std::stringumumnya digunakan dalam C ++, yang memiliki karakteristik kinerja sendiri, dan menyembunyikan alokasi memori dinamis ( juga lihat jawaban @ Ian).

Jadi, intinya adalah: Secara umum, tidak ada perbedaan dalam kinerja yang mungkin dicapai dengan menggunakan C atau C ++. Tapi untuk kode benar-benar kinerja-kritis, orang biasanya lebih memilih menggunakan C karena ada cara yang kurang mungkin tersembunyi hukuman kinerja.

gitar mematikan
sumber
1
Jawaban luar biasa. Inilah yang saya singgung dalam jawaban saya, tetapi Anda telah menjelaskannya dengan lebih baik.
Ian Goldby
4
Ini benar-benar jawaban yang diterima. Ini menjelaskan mengapa pernyataan seperti "C lebih cepat daripada C ++" ada. C bisa lebih cepat atau lebih lambat daripada C ++, tetapi biasanya jauh lebih mudah untuk mengetahui mengapa bagian tertentu dari kode C cepat / lambat, yang biasanya juga membuatnya lebih mudah untuk dioptimalkan.
Leo
Bukan untuk mengambil apa pun dari jawaban yang sangat bagus ini (untuk yang +1 dari saya), tetapi kompiler dapat menjadi apel dan jeruk dalam perbandingan ini. Itu / mereka dapat menghasilkan kode identik untuk C vs C ++, atau tidak. Tentu saja hal yang sama dapat dikatakan untuk dua kompiler atau opsi kompiler, bahkan ketika mengkompilasi program yang sama secara fisik dengan asumsi bahasa sumber yang sama.
JRobert
4
Saya akan menambahkan bahwa sebagian besar runtime C ++ besar-besaran dibandingkan dengan runtime C yang setara, yang akan menjadi masalah jika memori Anda terbatas.
James Anderson
@JamesAnderson Jika Anda benar-benar bahwa memori dibatasi, Anda mungkin tidak perlu runtime sama sekali :)
Navin
30

Kode yang ditulis dalam C ++ bisa lebih cepat daripada di C, untuk jenis tugas tertentu.

Jika Anda lebih suka C ++, gunakan C ++. Masalah kinerja apa pun akan menjadi tidak signifikan dibandingkan dengan keputusan algoritmik perangkat lunak Anda.

Apa namanya
sumber
6
Ini bisa lebih cepat tetapi mungkin tidak lebih cepat karena alasan yang sama.
Rob
Bisakah Anda memberikan beberapa contoh kode dioptimalkan yang ditulis dalam C ++ yang lebih cepat daripada C yang dioptimalkan?
1
@ TomDworzanski: salah satu contoh adalah bahwa dengan menggunakan templat, keputusan tentang jalur kode dapat ditentukan pada waktu kompilasi dan berakhir dengan hardcode dalam biner akhir, daripada persyaratan dan percabangan seperti yang diperlukan jika ditulis dalam c, serta kemampuan untuk menghindari panggilan fungsi melalui inlining.
whatsisname
23

Salah satu prinsip desain C ++ adalah Anda tidak membayar fitur yang tidak Anda gunakan. Jadi, jika Anda menulis kode dalam C ++ dan menghindari fitur yang tidak ada dalam C, maka kode yang dikompilasi yang dihasilkan harus setara dalam kinerja (meskipun Anda harus mengukur ini).

Ada biaya yang dapat diabaikan untuk menggunakan kelas, misalnya, dibandingkan dengan struct dan banyak fungsi terkait. Fungsi virtual akan sedikit lebih mahal, dan Anda harus mengukur kinerja untuk melihat apakah itu penting untuk aplikasi Anda. Hal yang sama berlaku untuk fitur bahasa C ++ lainnya.

Greg Hewgill
sumber
3
Fungsi pengiriman overhead virtual hampir dapat diabaikan, kecuali jika Anda sudah jalan WAY pada dekomposisi dan membuat hal-hal virtual. Vtables akan kecil dibandingkan dengan sisa kode dan data Anda, dan cabang yang diindeks melalui vtable menambahkan beberapa jam untuk setiap doa rutin. Karena doa rutin, semuanya, panggilan untuk kembali, akan berada di mana saja dari beberapa ratus hingga beberapa juta jam, cabang vtable akan dimakamkan di lantai kebisingan.
John R. Strohm
6
Struct adalah kelas dalam C ++.
sayap kanan
2
@ rightfold: Tentu, tetapi Anda masih dapat menulis kode C ++ yang meneruskan pointer ke struktur tanpa menggunakan thisfitur bahasa pointer. Itu saja yang saya katakan.
Greg Hewgill
4
@ John Biaya sebenarnya bukan tipuan (meskipun saya cukup yakin ini juga akan agak mengacaukan dengan beberapa prosesor prefetching), tetapi fakta bahwa Anda tidak dapat inline fungsi virtual (setidaknya dalam C ++) yang melarang banyak kemungkinan sebaliknya optimisasi. Dan ya itu bisa memiliki pengaruh besar pada kinerja.
Voo
2
@ Voo Agar adil, hal yang sama dapat dikatakan tentang kode C yang setara (kode yang secara manual mengemulasi polimorfisme runtime). Perbedaan terbesar adalah bahwa saya percaya akan lebih mudah bagi kompiler untuk menentukan apakah fungsi tersebut dapat diuraikan dalam C ++.
Thomas Eding
14

Salah satu alasan mengapa bahasa tingkat yang lebih tinggi kadang-kadang lebih lambat adalah karena mereka dapat bersembunyi di balik layar lebih banyak manajemen memori daripada bahasa tingkat yang lebih rendah.

Bahasa apa pun (atau pustaka, API, dll) yang mengabstraksi detail tingkat rendah berpotensi menyembunyikan operasi yang mahal. Sebagai contoh, dalam beberapa bahasa hanya memotong spasi spasi dari string menghasilkan alokasi memori dan salinan string. Alokasi memori dan penyalinan khususnya bisa menjadi mahal jika terjadi berulang kali dalam satu lingkaran yang ketat.

Jika Anda menulis kode semacam ini dalam C, itu akan menjadi sangat jelas. Dalam C ++ mungkin kurang begitu, karena alokasi dan penyalinan dapat diabstraksi menjadi suatu kelas di suatu tempat. Mereka bahkan mungkin disembunyikan di belakang operator yang kelihatan berlebihan atau copy constructor.

Jadi gunakan C ++ jika Anda mau. Tapi jangan tertipu oleh kenyamanan yang tampak dari abstraksi ketika Anda tidak tahu apa yang ada di bawahnya.

Tentu saja, gunakan profiler untuk mengetahui apa yang sebenarnya memperlambat kode Anda.

Ian Goldby
sumber
5

Untuk apa nilainya, saya cenderung menulis perpustakaan saya di C ++ 11 untuk set fitur yang ditingkatkan. Saya suka bisa mengambil keuntungan dari hal-hal seperti pointer bersama, pengecualian, pemrograman generik, dan fitur C ++ lainnya. Saya suka C ++ 11 karena saya menemukan sedikit dukungan pada semua platform yang saya pedulikan. Visual Studio 2013 memiliki banyak fitur bahasa inti dan implementasi perpustakaan yang siap digunakan dan konon sedang bekerja untuk menambahkan sisanya. Seperti yang Anda ketahui, Dentang dan GCC keduanya mendukung seluruh rangkaian fitur juga.

Dengan itu, saya baru-baru ini membaca tentang strategi yang sangat bagus tentang pengembangan perpustakaan yang saya pikir secara langsung relevan dengan permintaan Anda. Artikel tersebut berjudul "Gaya penanganan kesalahan AC yang cocok dengan pengecualian C ++" Stefanu Du Toit menyebut strategi ini sebagai pola "jam pasir". Paragraf pertama artikel:

Saya telah menulis banyak kode perpustakaan menggunakan apa yang saya sebut pola "jam pasir": Saya mengimplementasikan perpustakaan (dalam kasus saya, biasanya, menggunakan C ++), membungkusnya dalam API C yang menjadi satu-satunya titik masuk ke perpustakaan, kemudian bungkus C API itu dalam C ++ atau bahasa lain untuk menyediakan abstraksi yang kaya dan sintaksis yang mudah digunakan. Dalam hal kode lintas platform asli, API C memberikan stabilitas ABI yang tak tertandingi, dan portabilitas ke bahasa lain melalui FFI. Saya bahkan membatasi API untuk subset C yang saya tahu portabel untuk berbagai FFI dan mengisolasi perpustakaan dari kebocoran perubahan dalam struktur data internal - berharap lebih banyak tentang itu di posting blog masa depan.


Sekarang untuk mengatasi masalah utama Anda: kinerja.

Saya akan menyarankan, seperti banyak jawaban lain di sini, bahwa menulis kode dalam kedua bahasa akan bekerja dengan baik pada sudut pandang kinerja. Dari sudut pandang pribadi, saya menemukan menulis kode yang benar dalam C ++ menjadi lebih mudah karena fitur bahasa, tapi saya pikir itu adalah pilihan pribadi. Apa pun itu, kompiler benar-benar pintar dan cenderung menulis kode yang lebih baik daripada Anda. Itu mengatakan bahwa kompiler kemungkinan akan mengoptimalkan kode Anda lebih baik daripada yang Anda bisa.

Saya tahu banyak programmer mengatakan ini, tetapi hal pertama yang harus Anda lakukan adalah menulis kode Anda, lalu buat profil dan optimalkan di mana profiler Anda menyarankan Anda melakukannya. Waktu Anda akan jauh lebih baik dihabiskan untuk menghasilkan fitur dan kemudian mengoptimalkannya setelah Anda dapat melihat di mana kemacetan Anda.


Sekarang untuk beberapa bacaan menyenangkan tentang bagaimana fitur dan optimisasi bahasa dapat benar-benar bekerja sesuai keinginan Anda:

std :: unique_ptr tidak memiliki overhead

constexp memungkinkan untuk perhitungan waktu kompilasi

memindahkan semantik mencegah benda sementara yang tidak perlu

vmrob
sumber
std::unique_ptr has zero overheadIni tidak mungkin benar (secara teknis) karena harus memiliki konstruktornya dipanggil jika stack terurai karena pengecualian. Pointer mentah tidak memiliki overhead ini dan masih benar jika kode Anda mungkin tidak akan melempar. Kompiler tidak akan dapat membuktikan hal ini dalam kasus umum.
Thomas Eding
2
@ Thomas Saya mengacu pada ukuran dan overhead runtime sehubungan dengan kode bebas pengecualian. Perbaiki saya jika saya salah, tetapi ada model eksekusi yang menyebabkan overhead nol runtime saat pengecualian tidak dilemparkan yang masih memungkinkan pengecualian diperbanyak bila perlu. Meski begitu, kapan pengecualian bisa dilemparkan ke konstruktor unique_ptr? Ini dinyatakan noexcept, dan paling tidak menangani semua pengecualian, tapi saya tidak bisa membayangkan jenis pengecualian apa yang bisa dilemparkan.
vmrob
vmrob: Maafkan saya ... saya bermaksud menulis "destructor" bukan "constructor". Saya juga bermaksud menulis "terbukti tidak akan melempar" juga. Eeek!
Thomas Eding
2
@ThomasEding. Anda tahu, saya tidak berpikir itu akan menjadi masalah jika destruktor melempar pengecualian. Selama destructor tidak memperkenalkan pengecualian baru, itu masih nol kehancuran overhead. Selain itu, saya percaya seluruh destructor akan diuraikan ke satu panggilan delete / free dengan optimisasi.
vmrob
4

Perbedaan kinerja antara C ++ dan C bukan karena apa pun dalam bahasa, secara tegas, tetapi dalam apa yang menggoda Anda untuk melakukannya. Ini seperti kartu kredit vs. uang tunai. Itu tidak membuat Anda menghabiskan lebih banyak, tetapi Anda tetap melakukannya, kecuali jika Anda sangat disiplin.

Berikut adalah contoh dari program yang ditulis dalam C ++, yang kemudian diselaraskan secara agresif. Anda perlu tahu cara melakukan penyetelan kinerja yang agresif, terlepas dari bahasa. Metode yang saya gunakan adalah jeda acak, seperti yang ditunjukkan dalam video ini .

Jenis-jenis hal mahal yang membuat Anda tergoda untuk melakukan C ++ adalah manajemen memori yang berlebihan, pemrograman gaya notifikasi, mempercayai penghitung program Anda ke pustaka abstraksi multi-layer (seperti kata @Ian), bersembunyi lambat, dll.

Mike Dunlavey
sumber
2

C tidak memiliki keunggulan kinerja apa pun dibandingkan dengan C ++ jika Anda melakukan hal yang sama dalam kedua bahasa. Anda dapat mengambil kode C lama yang ditulis oleh programmer C yang layak dan mengubahnya menjadi kode C ++ yang valid dan setara, yang akan berjalan sama cepatnya (kecuali Anda dan kompiler Anda tahu apa yang dilakukan kata kunci "batasan" dan Anda menggunakannya secara efektif, tetapi kebanyakan orang tidak).

C ++ dapat memiliki kinerja yang sangat berbeda, baik lebih lambat atau lebih cepat, jika (1) Anda menggunakan pustaka C ++ standar untuk melakukan hal-hal yang dapat dilakukan lebih cepat dan lebih mudah tanpa menggunakan pustaka, atau (2) jika Anda menggunakan pustaka C ++ standar untuk melakukan hal-hal yang jauh lebih mudah dan lebih cepat daripada dengan mengimplementasikan kembali perpustakaan di C. buruk

gnasher729
sumber
1
ini tampaknya tidak menawarkan sesuatu yang substansial atas apa yang dijelaskan dalam 6 jawaban sebelumnya
nyamuk
Saya pikir jawaban ini menyebutkan poin penting yang tidak disebutkan orang lain. Pada pandangan pertama tampaknya C ++ adalah superset dari C, jadi jika Anda dapat menulis implementasi C cepat maka Anda harus dapat menulis implementasi C ++ yang setara. Namun, C99 mendukung kata kunci pembatas yang memungkinkan penghindaran dari penunjuk alias yang tidak diinginkan. C ++ tidak memiliki dukungan seperti itu. Kemampuan untuk menghindari aliasing pointer adalah fitur penting dari Fortran yang membuatnya berguna untuk aplikasi berkinerja tinggi. Saya berharap juga memungkinkan untuk memeras kinerja yang lebih baik dari C99 daripada C ++ di domain yang sama.
user27539