Saya menemukan perbedaan kinerja yang sangat besar antara kode serupa di C anc C #.
Kode C adalah:
#include <stdio.h>
#include <time.h>
#include <math.h>
main()
{
int i;
double root;
clock_t start = clock();
for (i = 0 ; i <= 100000000; i++){
root = sqrt(i);
}
printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);
}
Dan C # (aplikasi konsol) adalah:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
DateTime startTime = DateTime.Now;
double root;
for (int i = 0; i <= 100000000; i++)
{
root = Math.Sqrt(i);
}
TimeSpan runTime = DateTime.Now - startTime;
Console.WriteLine("Time elapsed: " + Convert.ToString(runTime.TotalMilliseconds/1000));
}
}
}
Dengan kode di atas, C # selesai dalam 0,328125 detik (versi rilis) dan C membutuhkan waktu 11,14 detik untuk berjalan.
C sedang dikompilasi ke windows yang dapat dieksekusi menggunakan mingw.
Saya selalu beranggapan bahwa C / C ++ lebih cepat atau setidaknya sebanding dengan C # .net. Apa sebenarnya yang menyebabkan C bekerja lebih dari 30 kali lebih lambat?
EDIT: Tampaknya pengoptimal C # menghapus root karena tidak digunakan. Saya mengubah tugas root menjadi root + = dan mencetak totalnya di akhir. Saya juga telah mengkompilasi C menggunakan cl.exe dengan flag / O2 yang disetel untuk kecepatan maksimal.
Hasilnya sekarang: 3,75 detik untuk C 2,61 detik untuk C #
C masih membutuhkan waktu lebih lama, tetapi ini bisa diterima
sumber
Jawaban:
Karena Anda tidak pernah menggunakan 'root', kompilator mungkin telah menghapus panggilan untuk mengoptimalkan metode Anda.
Anda dapat mencoba mengakumulasi nilai akar kuadrat menjadi akumulator, mencetaknya di akhir metode, dan melihat apa yang terjadi.
Edit: simak jawaban Jalf di bawah ini
sumber
Anda harus membandingkan build debug. Saya baru saja menyusun kode C Anda, dan mendapatkannya
Jika Anda tidak mengaktifkan pengoptimalan, tolok ukur apa pun yang Anda lakukan sama sekali tidak berguna. (Dan jika Anda mengaktifkan pengoptimalan, loop akan dioptimalkan. Jadi kode pembandingan Anda juga cacat. Anda perlu memaksanya untuk menjalankan loop, biasanya dengan menjumlahkan hasil atau yang serupa, dan mencetaknya di akhir)
Tampaknya apa yang Anda ukur pada dasarnya adalah "kompiler mana yang menyisipkan overhead debugging paling banyak". Dan ternyata jawabannya adalah C. Tapi itu tidak memberi tahu kita program mana yang paling cepat. Karena bila Anda menginginkan kecepatan, Anda mengaktifkan pengoptimalan.
Ngomong-ngomong, Anda akan terhindar dari banyak sakit kepala dalam jangka panjang jika Anda mengabaikan gagasan tentang bahasa yang "lebih cepat" daripada satu sama lain. C # tidak lebih memiliki kecepatan daripada bahasa Inggris.
Ada beberapa hal dalam bahasa C yang akan efisien bahkan dalam kompiler non-pengoptimalan yang naif, dan ada hal lain yang sangat bergantung pada kompiler untuk mengoptimalkan semuanya. Dan tentu saja, hal yang sama berlaku untuk C # atau bahasa lainnya.
Kecepatan eksekusi ditentukan oleh:
Kompiler C # yang baik akan menghasilkan kode yang efisien. Kompiler C yang buruk akan menghasilkan kode yang lambat. Bagaimana dengan kompilator C yang menghasilkan kode C #, yang kemudian dapat dijalankan melalui kompilator C #? Seberapa cepat lari itu? Bahasa tidak memiliki kecepatan. Kode Anda melakukannya.
sumber
i
, dansqrt
itulah yang diukur.Biar singkat saja, sudah ditandai jawab. C # memiliki keuntungan besar karena memiliki model floating point yang terdefinisi dengan baik. Itu kebetulan cocok dengan mode operasi asli dari set instruksi FPU dan SSE pada prosesor x86 dan x64. Tidak ada kebetulan di sana. JITter mengkompilasi Math.Sqrt () menjadi beberapa instruksi sebaris.
C / C ++ asli dibebani dengan kompatibilitas mundur bertahun-tahun. Opsi / fp: tepat, / fp: cepat dan / fp: kompilasi ketat adalah yang paling terlihat. Karenanya, ia harus memanggil fungsi CRT yang mengimplementasikan sqrt () dan memeriksa opsi floating point yang dipilih untuk menyesuaikan hasilnya. Itu lambat.
sumber
Saya seorang pengembang C ++ dan C #. Saya telah mengembangkan aplikasi C # sejak beta pertama kerangka .NET dan saya memiliki lebih dari 20 tahun pengalaman dalam mengembangkan aplikasi C ++. Pertama, kode C # TIDAK AKAN PERNAH lebih cepat daripada aplikasi C ++, tetapi saya tidak akan membahas panjang lebar tentang kode yang dikelola, cara kerjanya, lapisan inter-op, internal manajemen memori, sistem tipe dinamis, dan pengumpul sampah. Namun demikian, izinkan saya melanjutkan dengan mengatakan bahwa tolok ukur yang tercantum di sini semuanya menghasilkan hasil yang TIDAK BENAR.
Izinkan saya menjelaskan: Hal pertama yang perlu kita pertimbangkan adalah compiler JIT untuk C # (.NET Framework 4). Sekarang JIT menghasilkan kode native untuk CPU menggunakan berbagai algoritme pengoptimalan (yang cenderung lebih agresif daripada pengoptimal C ++ default yang disertakan dengan Visual Studio) dan set instruksi yang digunakan oleh compiler .NET JIT adalah cerminan yang lebih dekat dari CPU yang sebenarnya. pada mesin sehingga substitusi tertentu dalam kode mesin dapat dilakukan untuk mengurangi siklus jam dan meningkatkan laju klik di cache pipeline CPU dan menghasilkan pengoptimalan hyper-threading lebih lanjut seperti pengurutan ulang instruksi dan peningkatan yang berkaitan dengan prediksi cabang.
Artinya, kecuali Anda mengompilasi aplikasi C ++ menggunakan parameter yang benar untuk build RELEASE (bukan build DEBUG), aplikasi C ++ Anda mungkin bekerja lebih lambat daripada aplikasi berbasis C # atau .NET yang sesuai. Saat menentukan properti proyek pada aplikasi C ++ Anda, pastikan Anda mengaktifkan "pengoptimalan penuh" dan "memilih kode cepat". Jika Anda memiliki mesin 64 bit, Anda HARUS menentukan untuk menghasilkan x64 sebagai platform target, jika tidak kode Anda akan dieksekusi melalui sub-lapisan konversi (WOW64) yang secara substansial akan mengurangi kinerja.
Setelah Anda melakukan pengoptimalan yang benar dalam kompiler, saya mendapatkan 0,72 detik untuk aplikasi C ++ dan 1,16 detik untuk aplikasi C # (keduanya dalam rilis build). Karena aplikasi C # sangat mendasar dan mengalokasikan memori yang digunakan dalam loop pada stack dan bukan pada heap, sebenarnya ia bekerja jauh lebih baik daripada aplikasi nyata yang terlibat dalam objek, komputasi berat, dan dengan kumpulan data yang lebih besar. Jadi angka yang diberikan adalah angka optimis yang bias terhadap C # dan kerangka .NET. Bahkan dengan bias ini, aplikasi C ++ selesai lebih dari separuh waktu dibandingkan aplikasi C # yang setara. Perlu diingat bahwa kompiler Microsoft C ++ yang saya gunakan tidak memiliki pengoptimalan pipeline dan hyperthreading yang benar (menggunakan WinDBG untuk melihat petunjuk perakitan).
Sekarang jika kita menggunakan kompiler Intel (yang merupakan rahasia industri untuk menghasilkan aplikasi berkinerja tinggi pada prosesor AMD / Intel), kode yang sama dijalankan dalam .54 detik untuk eksekusi C ++ vs .72 detik menggunakan Microsoft Visual Studio 2010 Jadi pada akhirnya, hasil akhirnya adalah 0,54 detik untuk C ++ dan 1,16 detik untuk C #. Jadi kode yang dihasilkan oleh compiler JIT .NET membutuhkan waktu 214% lebih lama daripada C ++ yang dapat dieksekusi. Sebagian besar waktu yang dihabiskan dalam .54 detik adalah untuk mendapatkan waktu dari sistem dan bukan dalam loop itu sendiri!
Hal yang juga hilang dalam statistik adalah waktu mulai dan pembersihan yang tidak termasuk dalam pengaturan waktu. Aplikasi C # cenderung menghabiskan lebih banyak waktu untuk start-up dan penghentian daripada aplikasi C ++. Alasan di balik ini rumit dan berkaitan dengan rutinitas validasi kode runtime .NET dan subsistem manajemen memori yang melakukan banyak pekerjaan di awal (dan akibatnya, akhir) program untuk mengoptimalkan alokasi memori dan sampah pengumpul.
Saat mengukur kinerja C ++ dan .NET IL, penting untuk melihat kode rakitan untuk memastikan bahwa SEMUA perhitungan ada. Apa yang saya temukan adalah bahwa tanpa meletakkan beberapa kode tambahan di C #, sebagian besar kode dalam contoh di atas sebenarnya telah dihapus dari biner. Ini juga terjadi pada C ++ ketika Anda menggunakan pengoptimal yang lebih agresif seperti yang disertakan dengan kompiler Intel C ++. Hasil yang saya berikan di atas 100% benar dan divalidasi di tingkat perakitan.
Masalah utama dengan banyaknya forum di internet yaitu banyak newbie yang mendengarkan propaganda marketing Microsoft tanpa memahami teknologinya dan membuat klaim palsu bahwa C # lebih cepat dari C ++. Klaimnya adalah bahwa secara teori, C # lebih cepat daripada C ++ karena kompiler JIT dapat mengoptimalkan kode untuk CPU. Masalah dengan teori ini adalah bahwa ada banyak pipa ledeng yang ada di kerangka .NET yang memperlambat kinerja; pipa ledeng yang tidak ada di aplikasi C ++. Selain itu, pengembang berpengalaman akan mengetahui kompiler yang tepat untuk digunakan pada platform tertentu dan menggunakan tanda yang sesuai saat menyusun aplikasi. Di Linux atau platform open source, ini tidak menjadi masalah karena Anda dapat mendistribusikan sumber Anda dan membuat skrip instalasi yang mengkompilasi kode menggunakan pengoptimalan yang sesuai. Di windows atau platform sumber tertutup, Anda harus mendistribusikan beberapa file yang dapat dieksekusi, masing-masing dengan pengoptimalan khusus. Biner windows yang akan disebarkan didasarkan pada CPU yang terdeteksi oleh penginstal msi (menggunakan tindakan kustom).
sumber
tebakan pertama saya adalah optimasi kompiler karena Anda tidak pernah menggunakan root. Anda cukup menetapkannya, lalu menimpanya lagi dan lagi.
Sunting: sial, kalahkan 9 detik!
sumber
Untuk melihat apakah loop sedang dioptimalkan, coba ubah kode Anda menjadi
ans serupa dalam kode C, dan kemudian mencetak nilai root di luar loop.
sumber
Mungkin kompilator c # memperhatikan Anda tidak menggunakan root di mana pun, jadi ia melewatkan keseluruhan for loop. :)
Itu mungkin bukan masalahnya, tapi saya curiga apapun penyebabnya, itu bergantung pada implementasi compiler. Coba kompilasi program C Anda dengan kompiler Microsoft (cl.exe, tersedia sebagai bagian dari win32 sdk) dengan pengoptimalan dan mode Rilis. Saya yakin Anda akan melihat peningkatan kinerja dibandingkan kompiler lainnya.
EDIT: Saya tidak berpikir kompiler dapat mengoptimalkan pengulangan for, karena harus tahu bahwa Math.Sqrt () tidak memiliki efek samping.
sumber
Apapun waktunya berbeda. Mungkin, "waktu yang berlalu" itu tidak valid. Ini hanya akan menjadi valid jika Anda dapat menjamin bahwa kedua program berjalan di bawah kondisi yang sama persis.
Mungkin Anda harus mencoba menang. setara dengan $ / usr / bin / time my_cprog; / usr / bin / time my_csprog
sumber
Saya mengumpulkan (berdasarkan kode Anda) dua tes yang lebih sebanding di C dan C #. Keduanya menulis array yang lebih kecil menggunakan operator modulus untuk pengindeksan (ini menambahkan sedikit overhead, tapi hei, kami mencoba membandingkan kinerja [pada tingkat kasar]).
Kode C:
Di C #:
Tes ini menulis data ke sebuah array (sehingga runtime .NET tidak boleh diizinkan untuk memilih operasi sqrt) meskipun array secara signifikan lebih kecil (tidak ingin menggunakan memori yang berlebihan). Saya menyusun ini dalam konfigurasi rilis dan menjalankannya dari dalam jendela konsol (alih-alih memulai melalui VS).
Di komputer saya, program C # bervariasi antara 6,2 dan 6,9 detik, sedangkan versi C bervariasi antara 6,9 dan 7.1.
sumber
Jika Anda hanya satu langkah kode di tingkat perakitan, termasuk melalui rutin akar kuadrat, Anda mungkin akan mendapatkan jawaban atas pertanyaan Anda.
Tidak perlu menebak-nebak.
sumber
Faktor lain yang mungkin menjadi masalah di sini adalah bahwa kompilator C mengkompilasi ke kode asli generik untuk keluarga prosesor yang Anda targetkan, sedangkan MSIL yang dihasilkan ketika Anda mengkompilasi kode C # kemudian JIT dikompilasi untuk menargetkan prosesor yang sama persis dengan yang Anda miliki. optimisasi yang dimungkinkan. Jadi kode asli yang dihasilkan dari C # mungkin jauh lebih cepat daripada C.
sumber
Tampaknya bagi saya ini tidak ada hubungannya dengan bahasa itu sendiri, melainkan berkaitan dengan implementasi yang berbeda dari fungsi akar kuadrat.
sumber
Sebenarnya teman-teman, loop TIDAK dioptimalkan. Saya mengumpulkan kode John dan memeriksa .exe yang dihasilkan. Isi loop adalah sebagai berikut:
Kecuali jika runtime cukup pintar untuk menyadari loop tidak melakukan apa-apa dan melewatinya?
Edit: Mengubah C # menjadi:
Hasil dalam waktu yang telah berlalu (di mesin saya) berubah dari 0,047 menjadi 2,17. Tetapi apakah itu hanya biaya tambahan untuk menambah 100 juta operator tambahan?
sumber