Melihat halaman web Julia , Anda dapat melihat beberapa tolok ukur dari beberapa bahasa di beberapa algoritma (timing ditunjukkan di bawah). Bagaimana bahasa dengan kompiler yang awalnya ditulis dalam C, mengungguli kode C?
Gambar: waktu benchmark relatif terhadap C (lebih kecil lebih baik, kinerja C = 1.0).
programming-languages
compilers
efficiency
Program Perjuangan
sumber
sumber
Jawaban:
Tidak ada hubungan yang diperlukan antara implementasi compiler dan output dari compiler. Anda bisa menulis kompiler dalam bahasa seperti Python atau Ruby, yang implementasi paling umum sangat lambat, dan kompiler itu dapat menghasilkan kode mesin yang sangat dioptimalkan mampu mengungguli C. Kompiler itu sendiri akan membutuhkan waktu lama untuk dijalankan, karena itukode ditulis dalam bahasa yang lambat. (Lebih tepatnya, ditulis dalam bahasa dengan implementasi lambat. Bahasa tidak benar-benar inheren cepat atau lambat, seperti yang ditunjukkan oleh Raphael dalam komentar. Saya memperluas ide ini di bawah.) Program yang dikompilasi akan secepatnya implementasi sendiri diperbolehkan — kami dapat menulis kompiler dengan Python yang menghasilkan kode mesin yang sama dengan kompiler Fortran, dan program kompilasi kami akan secepat Fortran, meskipun mereka membutuhkan waktu lama untuk dikompilasi.
Ini cerita yang berbeda jika kita berbicara tentang seorang penerjemah. Penerjemah harus dijalankan saat program yang ditafsirkan sedang berjalan, sehingga ada hubungan antara bahasa tempat penerjemah diimplementasikan dan kinerja kode yang ditafsirkan. Dibutuhkan beberapa optimasi runtime yang pintar untuk membuat bahasa yang ditafsirkan yang berjalan lebih cepat dari bahasa di mana penerjemah diimplementasikan, dan kinerja akhir dapat bergantung pada seberapa dapat diterima sepotong kode untuk optimasi semacam ini. Banyak bahasa, seperti Java dan C #, menggunakan runtimes dengan model hybrid yang menggabungkan beberapa manfaat dari interpreter dengan beberapa manfaat dari kompiler.
Sebagai contoh nyata, mari kita lihat lebih dekat pada Python. Python memiliki beberapa implementasi. Yang paling umum adalah CPython, penerjemah bytecode yang ditulis dalam C. Ada juga PyPy, yang ditulis dalam dialek khusus Python yang disebut RPython, dan yang menggunakan model kompilasi hibrida agak mirip JVM. PyPy jauh lebih cepat daripada CPython di sebagian besar tolok ukur; ia menggunakan segala macam trik luar biasa untuk mengoptimalkan kode saat runtime. Namun, bahasa Python yang dijalankan PyPy adalah bahasa Python yang persis sama dengan yang dijalankan CPython, kecuali beberapa perbedaan yang tidak memengaruhi kinerja.
Misalkan kita menulis kompiler dalam bahasa Python untuk Fortran. Kompiler kami menghasilkan kode mesin yang sama dengan GFortran. Sekarang kami menyusun program Fortran. Kita dapat menjalankan kompiler kita di atas CPython, atau kita dapat menjalankannya di PyPy, karena ditulis dalam Python dan kedua implementasi ini menjalankan bahasa Python yang sama. Apa yang akan kita temukan adalah bahwa jika kita menjalankan kompiler kita di CPython, kemudian jalankan di PyPy, lalu kompilasi sumber Fortran yang sama dengan GFortran, kita akan mendapatkan kode mesin yang sama persis tiga kali, sehingga program yang dikompilasi akan selalu berjalan di sekitar kecepatan yang sama. Namun, waktu yang diperlukan untuk menghasilkan program yang dikompilasi akan berbeda. CPython kemungkinan besar akan memakan waktu lebih lama dari PyPy, dan PyPy kemungkinan besar akan lebih lama dari GFortran, meskipun semuanya akan menampilkan kode mesin yang sama di akhir.
Dari pemindaian tabel tolok ukur situs web Julia, sepertinya tidak ada bahasa yang berjalan pada penerjemah (Python, R, Matlab / Oktaf, Javascript) memiliki tolok ukur di mana mereka mengalahkan C. Ini umumnya konsisten dengan apa yang saya harapkan untuk dilihat, walaupun saya dapat membayangkan kode yang ditulis dengan library Numpy Python yang sangat dioptimalkan (ditulis dalam C dan Fortran) mengalahkan beberapa kemungkinan implementasi C dari kode yang sama. Bahasa yang sama atau lebih baik daripada C sedang dikompilasi (Fortran, Julia ) atau menggunakan model hibrida dengan kompilasi parsial (Jawa, dan mungkin LuaJIT). PyPy juga menggunakan model hybrid, jadi sangat mungkin jika kita menjalankan kode Python yang sama pada PyPy dan bukan CPython, kita akan melihatnya mengalahkan C pada beberapa tolok ukur.
sumber
Bagaimana sebuah mesin yang dibangun oleh seorang pria bisa lebih kuat dari seorang pria? Ini pertanyaan yang persis sama.
Jawabannya adalah bahwa output dari kompiler tergantung pada algoritma yang diimplementasikan oleh kompiler itu, bukan pada langauge yang digunakan untuk mengimplementasikannya. Anda dapat menulis kompiler yang sangat lambat dan tidak efisien yang menghasilkan kode yang sangat efisien. Tidak ada yang spesial dari kompiler: hanya sebuah program yang mengambil beberapa input dan menghasilkan beberapa output.
sumber
Saya ingin membuat satu poin terhadap asumsi umum yang, menurut pendapat saya, salah sampai berbahaya ketika memilih alat untuk pekerjaan.
Tidak ada bahasa lambat atau cepat. ¹
Dalam perjalanan kami ke CPU benar-benar melakukan sesuatu, ada banyak langkah².
Setiap item berkontribusi pada runtime aktual yang dapat Anda ukur, terkadang berat. "Bahasa" yang berbeda fokus pada hal yang berbeda³.
Sekedar memberi contoh.
1 vs 2-4 : programmer C rata-rata cenderung menghasilkan kode yang jauh lebih buruk daripada programmer Java rata-rata, baik dalam hal kebenaran dan efisiensi. Itu karena programmer memiliki lebih banyak tanggung jawab dalam C.
1/4 vs 7 : dalam bahasa tingkat rendah seperti C, Anda mungkin dapat mengeksploitasi fitur CPU tertentu sebagai programmer . Dalam bahasa tingkat yang lebih tinggi, hanya compiler / interpreter yang dapat melakukannya, dan hanya jika mereka mengetahui CPU target.
1/4 vs 5 : apakah Anda ingin atau harus mengontrol tata letak memori untuk menggunakan arsitektur memori yang terbaik? Beberapa bahasa memberi Anda kendali atas itu, beberapa tidak.
2/4 vs 3 : Python yang diinterpretasikan sendiri sangat lambat, tetapi ada ikatan populer untuk perpustakaan yang sangat dioptimalkan dan disusun secara alami untuk komputasi ilmiah. Jadi melakukan hal-hal tertentu dengan Python cepat pada akhirnya, jika sebagian besar pekerjaan dilakukan oleh perpustakaan ini.
2 vs 4 : Penerjemah Ruby standar cukup lambat. JRuby, di sisi lain, bisa sangat cepat. Itu bahasa yang sama cepat menggunakan kompiler / juru bahasa lain.
1/2 vs 4 : Menggunakan optimisasi kompiler, kode sederhana dapat diterjemahkan ke dalam kode mesin yang sangat efisien.
Intinya adalah, patokan yang Anda temukan tidak masuk akal, setidaknya tidak ketika direbus ke meja yang Anda sertakan. Bahkan jika semua yang Anda minati adalah waktu yang berjalan, Anda perlu menentukan seluruh rantai dari pemrogram ke CPU; menukar elemen apa pun dapat mengubah hasilnya secara dramatis.
Agar lebih jelas, ini menjawab pertanyaan karena itu menunjukkan bahwa bahasa yang dikompilasi (langkah 4) hanyalah sepotong teka-teki, dan mungkin tidak relevan sama sekali (lihat jawaban lain).
Saya sengaja tidak masuk ke metrik kesuksesan yang berbeda di sini: menjalankan efisiensi waktu, efisiensi memori, waktu pengembang, keamanan, keselamatan, kebenaran (terbukti?), Dukungan alat, kemandirian platform, ...
Membandingkan bahasa dengan satu metrik meskipun telah dirancang untuk tujuan yang sepenuhnya berbeda adalah kesalahan besar.
sumber
Ada satu hal yang terlupakan tentang pengoptimalan di sini.
Ada perdebatan panjang tentang fortran yang mengungguli C. Menyingkirkan debat yang salah: kode yang sama ditulis dalam C dan fortran (seperti yang diperkirakan penguji) dan kinerja diuji berdasarkan data yang sama. Masalahnya adalah, bahasa-bahasa ini berbeda, C memungkinkan pointer alias, sedangkan fortran tidak.
Jadi kodenya tidak sama, tidak ada batasan pada file yang diuji C, yang memberikan perbedaan, setelah menulis ulang file untuk memberitahu kompiler bahwa ia dapat mengoptimalkan pointer, runtime menjadi serupa.
Intinya di sini adalah, bahwa beberapa teknik optimasi lebih mudah (atau mulai legal) dalam bahasa yang baru dibuat.
Kedua VM dapat melakukan tes tekanan saat berjalan, sehingga dapat mengambil kode yang ditekan dan mengoptimalkannya atau bahkan menghitungnya selama runtime. Di muka disusun program C tidak mengharapkan di mana tekanan atau (sebagian besar waktu) ada versi generik dari executable untuk keluarga umum mesin.
Dalam tes ini ada juga JS, juga ada VM lebih cepat dari V8, dan juga melakukan lebih cepat daripada C dalam beberapa tes.
Saya telah memeriksanya, dan ada teknik optimisasi unik yang belum tersedia di kompiler C.
C compiler harus melakukan analisis statis seluruh kode sekaligus, berbaris di atas platform yang diberikan dan berkeliling masalah penyelarasan memori.
VM hanya mentransliterasikan bagian kode untuk perakitan yang dioptimalkan dan menjalankannya.
Tentang Julia - ketika saya memeriksanya beroperasi pada AST kode, misalnya GCC melewatkan langkah ini baru-baru ini mulai mengambil beberapa info dari sana. Ini ditambah kendala lain dan teknik VM mungkin menjelaskan sedikit.
Contoh: mari kita ambil loop sederhana, yang membutuhkan titik akhir akhir mulai dari variabel dan memuat bagian variabel ke dalam perhitungan yang diketahui saat runtime.
Kompiler C menghasilkan variabel pemuatan dari register.
Tetapi pada saat runtime variabel-variabel ini dikenal dan diperlakukan sebagai konstanta melalui eksekusi.
Jadi alih-alih memuat variabel dari register (dan tidak melakukan caching karena dapat berubah, dan dari analisis statis tidak jelas) mereka diperlakukan sepenuhnya seperti konstanta dan dilipat, diperbanyak.
sumber
Jawaban sebelumnya memberikan cukup banyak penjelasan, meskipun sebagian besar dari sudut pragmatis, karena sebanyak pertanyaan masuk akal , sebagaimana dijelaskan dengan sangat baik oleh jawaban Raphael .
Menambah jawaban ini, kita harus mencatat bahwa, saat ini, kompiler C ditulis dalam C. Tentu saja, sebagaimana dicatat oleh Raphael, output dan kinerjanya mungkin tergantung, antara lain, pada CPU yang sedang berjalan. Tetapi itu juga tergantung pada jumlah optimasi yang dilakukan oleh kompiler. Jika Anda menulis dalam C kompilator pengoptimalan yang lebih baik untuk C (yang kemudian Anda kompilasi dengan yang lama untuk dapat menjalankannya), Anda mendapatkan kompiler baru yang membuat bahasa C lebih cepat daripada sebelumnya. Jadi, berapa kecepatan C? Perhatikan bahwa Anda bahkan dapat mengkompilasi kompiler baru dengan dirinya sendiri, sebagai pass kedua, sehingga kompilasi lebih efisien, meskipun masih memberikan kode objek yang sama. Dan teorema ketenagakerjaan penuh menunjukkan bahwa mereka tidak ada habisnya untuk perbaikan seperti itu (terima kasih kepada Raphael untuk penunjuknya).
Tapi saya pikir mungkin ada gunanya mencoba memformalkan masalah, karena menggambarkan dengan sangat baik beberapa konsep dasar, dan khususnya pandangan denotasional versus operasional.
Apa itu kompiler?
Menyempurnakan argumen, kami mungkin ingin kompiler memiliki efisiensi yang baik, sehingga terjemahan dapat dilakukan dalam waktu yang wajar. Jadi kinerja program kompiler penting bagi pengguna, tetapi tidak berdampak pada semantik. Saya mengatakan kinerja, karena kompleksitas teoretis dari beberapa penyusun dapat jauh lebih tinggi dari yang diharapkan.
Tentang bootstrap
Ini akan menggambarkan perbedaan, dan menunjukkan aplikasi praktis.
sumber
Dengan teorema speedup Blum ada program yang ditulis dan dijalankan pada kombinasi komputer / kompiler yang paling cepat akan berjalan lebih lambat daripada program untuk hal yang sama pada PC pertama Anda yang menjalankan BASIC yang diartikan. Tidak ada "bahasa tercepat". Yang bisa Anda katakan adalah bahwa jika Anda menulis algoritma yang sama dalam beberapa bahasa (implementasi; seperti yang disebutkan, ada banyak kompiler C yang berbeda di sekitar, dan saya bahkan menemukan penerjemah C yang cukup mampu), itu akan berjalan lebih cepat atau lebih lambat di masing-masing .
Tidak mungkin ada hierarki "selalu lebih lambat". Ini adalah fenomena yang semua orang fasih dalam beberapa bahasa menyadari: Setiap bahasa pemrograman dirancang untuk jenis aplikasi tertentu, dan implementasi yang lebih banyak digunakan telah dioptimalkan dengan penuh kasih untuk jenis program tersebut. Saya cukup yakin bahwa misalnya program untuk bermain-main dengan string yang ditulis dalam Perl mungkin akan mengalahkan algoritma yang sama yang ditulis dalam C, sementara program yang mengunyah array besar bilangan bulat di C akan lebih cepat daripada Perl.
sumber
Mari kita kembali ke baris asli: "Bagaimana bahasa yang kompilernya ditulis dalam C bisa lebih cepat daripada C?" Saya pikir ini benar-benar dimaksudkan untuk mengatakan: bagaimana sebuah program yang ditulis dalam Julia, yang intinya ditulis dalam C, akan lebih cepat daripada sebuah program yang ditulis dalam C? Secara khusus, bagaimana mungkin program "mandel" seperti yang ditulis dalam Julia berjalan di 87% dari waktu pelaksanaan program "mandel" yang setara ditulis dalam C?
Risalah Babou adalah satu-satunya jawaban yang benar untuk pertanyaan ini sejauh ini. Semua tanggapan lain sejauh ini kurang lebih menjawab pertanyaan lain. Masalah dengan teks babou adalah bahwa deskripsi teoretis panjang-paragraf-panjang "Apa itu kompiler" ditulis dalam istilah bahwa poster asli mungkin akan mengalami kesulitan memahami. Siapa pun yang memahami konsep yang dirujuk oleh kata "semantik", "denotasional", "realisasi", "dapat dihitung", dan seterusnya akan sudah tahu jawaban atas pertanyaan itu.
Jawaban yang lebih sederhana adalah bahwa baik kode C, maupun kode Julia, tidak langsung dieksekusi oleh mesin. Keduanya harus diterjemahkan, dan proses terjemahan itu memperkenalkan banyak cara agar kode mesin yang dapat dieksekusi bisa lebih lambat atau lebih cepat, tetapi tetap menghasilkan hasil akhir yang sama. Baik C dan Julia melakukan kompilasi, yang berarti serangkaian terjemahan ke bentuk lain. Biasanya, file teks yang dapat dibaca manusia diterjemahkan ke beberapa representasi internal, dan kemudian dituliskan sebagai urutan instruksi yang dapat dipahami komputer secara langsung. Dengan beberapa bahasa, ada lebih dari itu, dan Julia adalah salah satunya - ia memiliki kompiler "JIT", yang berarti seluruh proses penerjemahan tidak harus terjadi sekaligus untuk seluruh program. Tetapi hasil akhirnya untuk bahasa apa pun adalah kode mesin yang tidak memerlukan terjemahan lebih lanjut, kode yang dapat dikirim langsung ke CPU untuk membuatnya melakukan sesuatu. Pada akhirnya, INI adalah "perhitungan", dan ada lebih dari satu cara untuk memberi tahu CPU cara mendapatkan jawaban yang Anda inginkan.
Orang bisa membayangkan bahasa pemrograman yang memiliki operator "plus" dan "multiply", dan bahasa lain yang hanya memiliki "plus". Jika perhitungan Anda membutuhkan perkalian, satu bahasa akan "lebih lambat" karena tentu saja CPU dapat melakukan keduanya secara langsung, tetapi jika Anda tidak memiliki cara untuk menyatakan perlunya mengalikan 5 * 5, Anda harus menulis "5 + 5 + 5 + 5 + 5 ". Yang terakhir akan membutuhkan lebih banyak waktu untuk sampai pada jawaban yang sama. Agaknya, ada beberapa hal yang terjadi dengan Julia; mungkin bahasanya memungkinkan programmer untuk menyatakan tujuan yang diinginkan dari komputasi Mandelbrot diatur dengan cara yang tidak mungkin untuk secara langsung diekspresikan dalam C.
Prosesor yang digunakan untuk benchmark disebutkan sebagai Xeon E7-8850 2.00GHz CPU. Tolok ukur C menggunakan kompilator gcc 4.8.2 untuk menghasilkan instruksi untuk CPU itu, sementara Julia menggunakan kerangka kerja kompiler LLVM. Mungkin saja backend gcc (bagian yang menghasilkan kode mesin untuk arsitektur CPU tertentu) tidak sebagus backend LLVM. Itu bisa membuat perbedaan dalam kinerja. Ada juga banyak hal lain yang terjadi - kompiler dapat "mengoptimalkan" dengan mungkin mengeluarkan instruksi dalam urutan yang berbeda dari yang ditentukan oleh programmer, atau bahkan tidak melakukan beberapa hal sama sekali jika dapat menganalisis kode dan menentukan mereka tidak dituntut untuk mendapatkan jawaban yang benar. Dan programmer mungkin telah menulis bagian dari program C dengan cara yang membuatnya lambat, tetapi tidak
Semua ini adalah cara untuk mengatakan: ada banyak cara untuk menulis kode mesin untuk menghitung set Mandelbrot, dan bahasa yang Anda gunakan memiliki efek besar pada bagaimana kode mesin itu ditulis. Semakin banyak Anda memahami tentang kompilasi, set instruksi, cache, dan sebagainya, semakin lengkap Anda untuk mendapatkan hasil yang Anda inginkan. Hasil utama dari hasil benchmark yang dikutip untuk Julia adalah bahwa tidak ada satu bahasa atau alat yang terbaik dalam segala hal. Faktanya faktor kecepatan terbaik di seluruh bagan adalah untuk Java!
sumber
Kecepatan program yang dikompilasi tergantung pada dua hal:
Bahasa yang ditulis oleh kompiler tidak relevan dengan (1). Sebagai contoh, kompiler Java dapat ditulis dalam C atau Java atau Python, tetapi dalam semua kasus "mesin" yang menjalankan program adalah JVM.
Bahasa yang ditulis oleh kompiler tidak relevan dengan (2). Sebagai contoh, tidak ada alasan mengapa kompiler C yang ditulis dengan Python tidak dapat menampilkan file yang dapat dieksekusi sama persis dengan kompiler C yang ditulis dalam C atau Java.
sumber
Saya akan mencoba menawarkan jawaban yang lebih pendek.
Inti dari pertanyaan terletak pada definisi "kecepatan" suatu bahasa .
Kebanyakan jika tidak semua tes perbandingan kecepatan tidak menguji berapa kecepatan maksimum yang dimungkinkan. Sebagai gantinya mereka menulis sebuah program kecil dalam bahasa yang ingin mereka uji, untuk memecahkan masalah. Saat menulis program, programmer menggunakan apa yang mereka anggap * sebagai praktik terbaik dan konvensi bahasa, pada saat ujian. Kemudian mereka mengukur kecepatan program dijalankan.
* Asumsinya terkadang salah.
sumber
Kode yang ditulis dalam bahasa X yang Compilernya ditulis dalam C, dapat mengungguli kode yang ditulis dalam C, asalkan, kompiler C melakukan optimasi yang buruk dibandingkan dengan bahasa X. Jika kita menjaga optimasi dari diskusi maka jika kompiler X dapat menghasilkan lebih baik kode objek selain yang dihasilkan oleh kompiler C, maka juga kode yang ditulis dalam X dapat memenangkan perlombaan.
Tetapi jika bahasa X adalah bahasa yang ditafsirkan, dan penerjemah ditulis dalam C, dan jika kita mengasumsikan bahwa penerjemah bahasa X dan kode yang ditulis dalam C dikompilasi oleh kompiler C yang sama, maka sama sekali tidak ada kode yang ditulis dalam X akan mengungguli kode ditulis dalam C, asalkan kedua implementasi mengikuti algoritma yang sama dan menggunakan struktur data yang setara.
sumber