Diterjemahkan vs Dikompilasi: Perbedaan yang bermanfaat?

29

Banyak pertanyaan yang diajukan di sini tentang penerapan bahasa yang ditafsirkan vs dikompilasi. Saya bertanya-tanya apakah perbedaan itu benar-benar masuk akal. (Sebenarnya pertanyaannya biasanya tentang bahasa, tetapi mereka benar-benar memikirkan implementasi paling populer dari bahasa-bahasa itu).

Saat ini hampir tidak ada implementasi yang ditafsirkan secara ketat. yaitu tidak ada yang mem-parsing dan menjalankan kode satu baris pada suatu waktu. Selain itu, implementasi yang dikompilasi ke kode mesin juga menjadi kurang umum. Semakin, kompiler menargetkan semacam mesin virtual.

Bahkan, sebagian besar implementasi konvergen pada strategi dasar yang sama. Kompiler menghasilkan bytecode yang ditafsirkan atau dikompilasi ke kode asli melalui JIT. Ini benar-benar campuran dari ide-ide tradisional kompilasi dan interpretasi.

Jadi saya bertanya: Apakah ada perbedaan yang berguna antara implementasi yang ditafsirkan dan implementasi yang disusun akhir-akhir ini?

Winston Ewert
sumber
7
@DeadMG Tidak se-baru yang Anda kira: Sejarah singkat just-in-time ...
yannis
4
@DeadMG Mengingat bahwa mayoritas bahasa baru yang diperkenalkan dalam 10 tahun terakhir atau lebih terutama berjalan pada beberapa jenis VM, saya akan mengatakan dia ada benarnya. Tentu saja masih ada (dan akan ada beberapa dekade ke depan) bahasa dikompilasi ke kode asli, dan JIT akan tetap mewah (atau tidak, jika orang-orang PyPy memiliki cara mereka sendiri). Jadi ya, mungkin pernyataan yang berlebihan, tapi saya setuju bahwa arus utama (untuk saat ini dan masa depan yang dapat dilihat) tampaknya adalah bytecode compiler + mungkin JIT.
4
@DeadMG, Anda harus memiliki jenggot putih panjang, jika model VM "baru" untuk Anda. P-codetelah diperkenalkan pada tahun 1966 dulu. IBM Aix sudah ada sejak 1986.
SK-logic
6
Hal-hal seperti cangkang unix, Tcl dan yang sama akan selalu ditafsirkan secara murni, sehingga perbedaannya masuk akal, setidaknya dalam CS akademik. Tetapi memang benar bahwa ketika coders bergumam tentang penerjemah vs kompiler mereka tidak masuk akal dalam sebagian besar kasus.
SK-logic
3
@ SK-logic, saya pikir komentar Anda adalah jawaban yang lebih baik daripada jawaban yang benar-benar diposting
Winston Ewert

Jawaban:

23

Penting untuk diingat bahwa menafsirkan dan menyusun bukan hanya alternatif satu sama lain. Pada akhirnya, program apa pun yang Anda tulis (termasuk yang dikompilasi ke kode mesin) ditafsirkan. Menafsirkan kode berarti mengambil satu set instruksi dan mengembalikan jawaban.

Kompilasi, di sisi lain, berarti mengubah program dalam satu bahasa ke bahasa lain. Biasanya diasumsikan bahwa ketika kompilasi berlangsung, kode dikompilasi ke bahasa "level bawah" (mis. Kode mesin, semacam bytecode VM, dll.). Kode yang dikompilasi ini masih ditafsirkan kemudian.

Sehubungan dengan pertanyaan Anda tentang apakah ada perbedaan yang berguna antara bahasa yang ditafsirkan dan dikompilasi, pendapat pribadi saya adalah bahwa setiap orang harus memiliki pemahaman dasar tentang apa yang terjadi pada kode yang mereka tulis selama interpretasi. Jadi, jika kode mereka sedang dikompilasi JIT, atau bytecode-cache, dll, programmer setidaknya harus memiliki pemahaman dasar tentang apa artinya itu.

Zach Smith
sumber
3
Ya programmer harus memiliki pemahaman dasar. Tetapi saya bertanya-tanya apakah terminologi yang dikompilasi / ditafsirkan tidak menghalangi itu.
Winston Ewert
2
Terima kasih!! Diartikan hanyalah sinonim untuk "dieksekusi", dan begitulah semua program dijalankan.
Gardenhead
9

Perbedaan ini sangat bermakna karena bahasa yang dikompilasi membatasi semantik dengan cara yang tidak perlu ditafsirkan bahasa. Beberapa teknik interpretif sangat sulit (praktis tidak mungkin) untuk dikompilasi.

Kode yang diinterpretasikan dapat melakukan hal-hal seperti menghasilkan kode pada waktu berjalan, dan memberikan visibilitas kode tersebut ke binding leksikal dari ruang lingkup yang ada. Itu salah satu contohnya. Lain adalah bahwa penerjemah dapat diperluas dengan kode yang ditafsirkan yang dapat mengontrol bagaimana kode dievaluasi. Ini adalah dasar untuk "fexprs" Lisp kuno: fungsi yang dipanggil dengan argumen yang tidak dievaluasi dan memutuskan apa yang harus dilakukan dengannya (memiliki akses penuh ke lingkungan yang diperlukan untuk menjalankan kode dan mengevaluasi variabel, dll). Dalam bahasa yang dikompilasi, Anda tidak dapat benar-benar menggunakan teknik itu; Anda menggunakan makro sebagai gantinya: fungsi yang dipanggil pada waktu kompilasi dengan argumen yang tidak dievaluasi, dan menerjemahkan kode daripada menafsirkan.

Beberapa implementasi bahasa dibangun di sekitar teknik ini; penulisnya menolak kompilasi sebagai tujuan penting, dan lebih memilih merangkul jenis fleksibilitas ini.

Menafsirkan akan selalu berguna sebagai teknik untuk bootstrap kompiler. Untuk contoh konkret, lihat CLISP (implementasi populer Common Lisp). CLISP memiliki kompiler yang ditulis dengan sendirinya. Ketika Anda membangun CLISP, kompiler itu sedang ditafsirkan selama langkah-langkah awal membangun. Ini digunakan untuk mengkompilasi sendiri, dan kemudian setelah dikompilasi, kompilasi kemudian dilakukan dengan menggunakan kompiler yang dikompilasi.

Tanpa kernel interpreter, Anda perlu melakukan bootstrap dengan beberapa Lisp yang ada, seperti yang dilakukan SBCL.

Dengan interpretasi, Anda dapat mengembangkan bahasa dari awal mutlak, dimulai dengan bahasa assembly. Kembangkan I / O dasar dan rutinitas inti, lalu tulis eval, bahasa mesin. Setelah Anda memiliki eval, tulis dalam bahasa tingkat tinggi; kernel kode mesin melakukan evaluasi. Gunakan fasilitas ini untuk memperluas perpustakaan dengan lebih banyak rutinitas dan menulis kompiler juga. Gunakan kompiler untuk mengkompilasi rutinitas tersebut dan kompilator itu sendiri.

Interpretasi: batu loncatan penting di jalan menuju kompilasi!

Kaz
sumber
1
IMO, ini adalah jawaban terbaik. Saya sedang mengerjakan bahasa mainan saya sendiri dan paragraf terakhir menjelaskan cara saya mengembangkannya. Ini benar-benar membuat bekerja pada ide-ide baru menjadi lebih mudah. Juga memberi +1 untuk menyebutkan proses bootstrap CLISP.
sinan
Secara teori, setiap bahasa "ditafsirkan" dapat dibuat menjadi bahasa "dikompilasi" dengan menghasilkan file EXE yang terdiri dari juru bahasa ditambah kode sumber atau bytecode untuk program yang ditafsirkan. Mungkin tidak terlalu efisien.
dan04
Baca bagaimana Wirth et al menciptakan P-code untuk menyederhanakan porting PASCAL ke arsitektur mesin baru. Itu pada awal 1970-an.
John R. Strohm
1
Saya menduga paragraf pembuka Anda membingungkan kompilasi dan interpretasi dengan perilaku statis dan dinamis, tetapi saya akan memberi Anda manfaat dari keraguan dan hanya meminta contoh bahasa dengan semantik yang "praktis mustahil" untuk dikompilasi. Mengenai bootstrap kompiler, memang benar bahwa implementasi pertama harus ditulis dalam sesuatu selain bahasa yang Anda implementasikan, tetapi tidak harus menjadi penerjemah, bisa juga kompiler yang ditulis dalam bahasa lain.
8bittree
1

Sebenarnya banyak implementasi bahasa masih ditafsirkan secara ketat, Anda mungkin tidak menyadarinya. Untuk beberapa nama: bahasa shell UNIX, Windows cmd dan shell PowerScript, Perl, awk, sed, MATLAB, Mathematica dan sebagainya.

Charles E. Grant
sumber
3
Saya percaya Perl secara internal dikompilasi ke bytecode, dan setidaknya Mathematica dapat dikompilasi. Dan tidak ada yang menentukan implementasi awk dan sed (saya percaya beberapa coreutil GNU mengkompilasi ekspresi reguler ke finite automata sebelum eksekusi, yang bisa dibilang menempatkan mereka ke dalam kategori "kompilasi ke perantara, menafsirkan kategori itu").
1
Sebenarnya saya cukup yakin bahwa Perl, MATLAB, Mathematica semuanya dikompilasi menjadi bytecode. Saya tidak terbiasa dengan PowerScript, maksud Anda Powershell? Jika demikian, itu menggunakan CLR dan begitu juga menggunakan bytecode.
Winston Ewert
@ WinstonEwert, maaf saya maksudkan PowerShell. Pemahaman saya adalah bahwa terjemahan ke dalam bentuk peralihan tidak berarti bahwa ada sesuatu yang tidak ditafsirkan. Heck, penerjemah Dartmouth BASIC yang asli menerjemahkan sumber ke dalam token sebelum menerjemahkan. Setiap alat yang saya sebutkan memiliki mode di mana 1) membaca baris sumber, 2) menerjemahkan garis itu ke dalam bentuk yang dapat dieksekusi (mungkin beberapa kode perantara daripada kode asli), 3) mengeksekusi kode untuk baris itu, 4) kembali ke 1). Itu sesuai dengan pemahaman saya tentang seorang penerjemah.
Charles E. Grant
2
Bytecode menyiratkan dikompilasi. Compiler bytecode hanyalah sebuah program yang mengambil sumber dan mengubahnya menjadi bytecode. Karenanya semua penggunaan bytecode harus melibatkan compiler bytecode. Tetapi bytecode juga harus ditafsirkan (atau JITted). Jadi apapun yang menggunakan bytecode adalah interpreter / compiler hybrid.
Winston Ewert
4
Sungguh, hal saya adalah bahwa orang membuang pernyataan seperti "python diinterpretasikan" dan "Java dikompilasi" tanpa pemahaman tentang implementasi. Saya mencari tahu apakah ini berguna untuk menggambarkan implementasi dalam istilah tersebut. Kebenaran biasanya lebih rumit, dan mencoba merebusnya untuk ditafsirkan / dikompilasi tidak berguna.
Winston Ewert
1

Saya pikir: Benar-benar Ya .

Bahkan, sebagian besar implementasi konvergen pada strategi dasar yang sama

Sungguh, C ++ bertujuan untuk port ke kompiler domain beberapa konsep tingkat tinggi yang biasanya diserahkan kepada juru bahasa, tetapi tetap di sisi minoritas ...

CapelliC
sumber
2
Tunggu hingga Dentang + LLVM menjadi toolchain kompiler paling populer.
SK-logic
@ SK-logic, terlepas dari namanya, saya percaya bahwa Dentang + LLVM menghasilkan kode asli.
Winston Ewert
1
@ Winston Ewert, hanya jika Anda mau. Anda dapat berhenti pada tingkat LLVM IR dan melakukan apa pun yang Anda inginkan dengannya - menafsirkannya, mengkompilasi JIT itu, instrumen itu dengan cara apa pun yang Anda suka. Anda bahkan dapat menerjemahkannya ke Javascript dan kemudian melewati penerjemah: github.com/kripken/emscripten/wiki
SK-logic
@ SK-logika, hal-hal yang rapi! Tidak tahu LLVM bisa melakukan itu.
Winston Ewert
1
Keindahan llvm adalah pemisahan yang disengaja antara ujung depan dan ujung belakang. Dan alat untuk memanipulasi tengah sebelum menargetkan set instruksi. Anda dapat menggabungkan seluruh proyek Anda menjadi bytecode dan kemudian mengoptimalkan seluruh hal, dengan kompiler lain Anda harus memiliki satu sumber file atau setidaknya satu yang menyertakan jalan turun melalui pohon sumber sehingga kompiler bertindak pada satu sumber gabungan. Juga satu set alat di bawah llvm adalah generik untuk semua target, Anda tidak harus membangun untuk setiap target, satu kompiler cocok untuk semua (setidaknya sampai asm dari target).
old_timer
-1

Perbedaan yang berguna: program yang ditafsirkan dapat memodifikasi diri mereka sendiri dengan menambahkan atau mengubah fungsi saat runtime.

Diego Pacheco
sumber
8
Omong kosong. Kode modifikasi sendiri (mesin) adalah trik tertua dalam buku ini. Kemudian lagi, beberapa orang berpendapat bahwa kode asli pada akhirnya ditafsirkan oleh seorang juru bahasa menjadi silikon (CPU). Tetapi jika kita mengasumsikan itu, semua kode ditafsirkan dan tidak ada perbedaan untuk dibuat.
2
@nannan benar. Saya hanya akan menambahkan bahwa bahasa modern dapat memodifikasi diri mereka sendiri dengan membuat kelas baru secara dinamis dan memuat / menurunkan pustaka (atau "assemblies" dalam. NET misalnya)
Jalayn
5
Lisp umum dikompilasi, tetapi Anda masih dapat mengganti definisi fungsi dalam runtime dengan mudah.
SK-logic
Itu fitur interpretasi yang sangat menarik dan perlu (misalnya dalam Prolog).
CapelliC