Saya akan mulai dengan tidak setuju dengan bagian dari jawaban yang diterima (dan diputuskan dengan baik) untuk pertanyaan ini dengan menyatakan:
Sebenarnya ada banyak alasan mengapa kode JITted akan berjalan lebih lambat daripada program C ++ yang dioptimalkan dengan benar (atau bahasa lain tanpa overhead runtime) termasuk:
menghitung siklus yang dihabiskan pada kode JITting pada saat runtime menurut definisi tidak tersedia untuk digunakan dalam eksekusi program.
setiap jalur panas di JITter akan bersaing dengan kode Anda untuk instruksi dan cache data di CPU. Kita tahu bahwa cache mendominasi dalam hal kinerja dan bahasa asli seperti C ++ tidak memiliki jenis pertikaian ini, menurut definisi.
anggaran waktu pengoptimal waktu berjalan tentu jauh lebih terbatas daripada pengoptimal waktu kompilasi (seperti yang ditunjukkan komentator lain)
Intinya: Pada akhirnya, Anda akan hampir pasti dapat membuat implementasi lebih cepat di C ++ dari yang Anda bisa dalam C # .
Sekarang, dengan mengatakan, seberapa cepat sebenarnya tidak dapat diukur, karena ada terlalu banyak variabel: tugas, domain masalah, perangkat keras, kualitas implementasi, dan banyak faktor lainnya. Anda akan menjalankan tes pada skenario Anda untuk menentukan perbedaan dalam kinerja, dan kemudian memutuskan apakah itu sepadan dengan upaya tambahan dan kompleksitas.
Ini adalah topik yang sangat panjang dan kompleks, tetapi saya merasa perlu menyebutkan demi kelengkapan bahwa optimizer runtime C # sangat baik, dan mampu melakukan optimasi dinamis tertentu pada saat runtime yang tidak tersedia untuk C ++ dengan waktu kompilasi ( statis) pengoptimal. Bahkan dengan ini, keuntungannya masih sangat dalam di pengadilan aplikasi asli, tetapi pengoptimal dinamis adalah alasan untuk "kualifikasi hampir pasti" yang diberikan di atas.
-
Dalam hal kinerja relatif, saya juga terganggu oleh angka-angka dan diskusi yang saya lihat di beberapa jawaban lain, jadi saya pikir saya akan berpadu dan pada saat yang sama, memberikan beberapa dukungan untuk pernyataan yang saya buat di atas.
Sebagian besar masalah dengan tolok ukur tersebut adalah Anda tidak dapat menulis kode C ++ seolah-olah Anda sedang menulis C # dan berharap mendapatkan hasil yang representatif (mis. Melakukan ribuan alokasi memori di C ++ akan memberi Anda angka yang mengerikan.)
Alih-alih, saya menulis kode C ++ yang idiomatis dan membandingkannya dengan kode C # @Wiory yang disediakan. Dua perubahan besar yang saya buat pada kode C ++ adalah:
1) menggunakan vektor :: cadangan ()
2) meratakan array 2d menjadi 1d untuk mencapai lokalitas cache yang lebih baik (blok yang berdekatan)
C # (.NET 4.6.1)
private static void TestArray()
{
const int rows = 5000;
const int columns = 9000;
DateTime t1 = System.DateTime.Now;
double[][] arr = new double[rows][];
for (int i = 0; i < rows; i++)
arr[i] = new double[columns];
DateTime t2 = System.DateTime.Now;
Console.WriteLine(t2 - t1);
t1 = System.DateTime.Now;
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
arr[i][j] = i;
t2 = System.DateTime.Now;
Console.WriteLine(t2 - t1);
}
Run time (Release): Init: 124ms, Isi: 165ms
C ++ 14 (Dentang v3.8 / C2)
#include <iostream>
#include <vector>
auto TestSuite::ColMajorArray()
{
constexpr size_t ROWS = 5000;
constexpr size_t COLS = 9000;
auto initStart = std::chrono::steady_clock::now();
auto arr = std::vector<double>();
arr.reserve(ROWS * COLS);
auto initFinish = std::chrono::steady_clock::now();
auto initTime = std::chrono::duration_cast<std::chrono::microseconds>(initFinish - initStart);
auto fillStart = std::chrono::steady_clock::now();
for(auto i = 0, r = 0; r < ROWS; ++r)
{
for (auto c = 0; c < COLS; ++c)
{
arr[i++] = static_cast<double>(r * c);
}
}
auto fillFinish = std::chrono::steady_clock::now();
auto fillTime = std::chrono::duration_cast<std::chrono::milliseconds>(fillFinish - fillStart);
return std::make_pair(initTime, fillTime);
}
Jalankan waktu (Rilis): Init: 398µs (ya, itu mikrodetik), Isi: 152ms
Total waktu Jalankan: C #: 289ms, C ++ 152ms (kira-kira 90% lebih cepat)
Pengamatan
Mengubah implementasi C # ke implementasi array 1d yang sama menghasilkan Init: 40ms, Isi: 171ms, Total: 211ms ( C ++ masih hampir 40% lebih cepat ).
Jauh lebih sulit untuk mendesain dan menulis kode "cepat" dalam C ++ daripada menulis kode "biasa" dalam kedua bahasa.
Sangat mudah untuk mendapatkan kinerja yang buruk di C ++; kami melihat bahwa dengan kinerja vektor tanpa pagu harga. Dan ada banyak jebakan seperti ini.
Kinerja C # agak luar biasa ketika Anda mempertimbangkan semua yang terjadi saat runtime. Dan kinerja itu relatif mudah diakses.
Lebih banyak data anekdotal yang membandingkan kinerja C ++ dan C #: https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore
Intinya adalah bahwa C ++ memberi Anda lebih banyak kontrol atas kinerja. Apakah Anda ingin menggunakan pointer? Referensi? Stack memory? Tumpukan? Polimorfisme dinamis atau menghilangkan overhead runtime dari suatu vtable dengan polimorfisme statis (via templat / CRTP)? Dalam C ++ Anda harus ... er, bisa membuat semua pilihan ini (dan lebih) sendiri, idealnya sehingga solusi terbaik alamat masalah Anda penanggulangan sedang.
Tanyakan pada diri Anda apakah Anda benar-benar ingin atau memerlukan kontrol itu, karena bahkan untuk contoh sepele di atas, Anda dapat melihat bahwa meskipun ada peningkatan kinerja yang signifikan, itu memerlukan investasi yang lebih dalam untuk mengakses.