Mengapa VM harus “mesin stack” atau “mesin register” dll.?

48

(Ini adalah pertanyaan yang sangat pemula-ish).

Saya telah mempelajari sedikit tentang Mesin Virtual.

Ternyata banyak dari mereka dirancang sangat mirip dengan komputer fisik atau teoretis.

Saya membaca bahwa JVM misalnya, adalah 'mesin tumpukan'. Apa itu artinya (dan koreksi saya jika saya salah) adalah bahwa ia menyimpan semua 'memori sementara' di stack, dan melakukan operasi pada stack ini untuk semua opcode-nya.

Misalnya, kode sumber 2 + 3akan diterjemahkan ke bytecode mirip dengan:

push 2
push 3
add

Pertanyaan saya adalah ini:

JVM mungkin ditulis menggunakan C / C ++ dan semacamnya. Jika demikian, mengapa JVM tidak menjalankan kode C berikut: 2 + 3..? Maksud saya, mengapa perlu tumpukan, atau 'register' VM lainnya - seperti di komputer fisik?

CPU fisik yang mendasarinya mengurus semua ini. Mengapa penulis VM tidak hanya menjalankan bytecode yang ditafsirkan dengan instruksi 'biasa' dalam bahasa yang diprogram dengan VM?

Mengapa VM perlu meniru perangkat keras, ketika perangkat keras yang sebenarnya sudah melakukan ini untuk kita?

Sekali lagi, pertanyaan yang sangat pemula. Terima kasih atas bantuan Anda

Aviv Cohn
sumber
5
Sudahkah Anda mempertimbangkan mesin apa yang bukan virtual?
5
@MichaelT Maksudmu mesin fisik?
Aviv Cohn
Tentu saja, kebanyakan Javascript VM bukan mesin stack atau mesin register - V8 / IonMonkey / Chakra / dll. Adalah VM yang mengimplementasikan Javascript. "VM" hanyalah penerjemah atau kompiler JIT yang dapat mengimplementasikan bahasa apa pun yang diinginkan oleh perancang.
Billy ONeal
@BillyONeal Jadi misalnya jika saya menulis VM untuk beberapa bahasa dan saya menulisnya di C: VM mem-parsing baris bytcode 'print "hi"', dan jalankan printf("hi");: apakah ini dianggap sebagai VM? Ia tidak memiliki 'tumpukan' atau 'register' dan atau apa pun.
Aviv Cohn
@ Kat: Ya, itu benar.
Billy ONeal

Jawaban:

51

Mesin, virtual atau tidak, membutuhkan model komputasi yang menggambarkan bagaimana komputasi dilakukan di dalamnya. Menurut definisi, segera setelah dihitung, ia mengimplementasikan beberapa model perhitungan. Pertanyaannya kemudian adalah: Model apa yang harus kita pilih untuk VM kita? Mesin fisik dibatasi oleh apa yang dapat dilakukan secara efektif dan efisien dalam perangkat keras. Tetapi, seperti yang Anda perhatikan, mesin virtual tidak memiliki kendala seperti itu, mereka didefinisikan dalam perangkat lunak menggunakan bahasa tingkat tinggi yang sewenang-wenang.

Sebenarnya, ada mesin virtual yang tingkat tinggi seperti yang Anda gambarkan. Mereka disebut bahasa pemrograman . Standar C misalnya mendedikasikan sebagian besar halamannya untuk mendefinisikan model untuk apa yang disebut "mesin abstrak C" yang menggambarkan bagaimana program C berperilaku, dan dengan ekstensi (as-jika aturan) bagaimana kompiler C yang sesuai (atau penerjemah) harus berperilaku.

Tentu saja, kita biasanya tidak menyebut itu mesin virtual. VM biasanya berarti tingkat yang lebih rendah, lebih dekat dengan perangkat keras, tidak dimaksudkan untuk diprogram secara langsung, dirancang untuk dieksekusi secara efisien. Bias pemilihan ini berarti bahwa sesuatu yang menerima kode komposer tingkat tinggi (seperti apa yang Anda jelaskan) tidak akan dianggap sebagai VM karena dieksekusi kode tingkat tinggi.

Tetapi untuk sampai pada intinya, berikut adalah beberapa alasan untuk membuat VM (seperti, sesuatu yang ditargetkan oleh kompiler bytecode) berbasis register atau sejenisnya. Stack dan mesin register sangat sederhana. Ada urutan instruksi, beberapa status, dan semantik untuk setiap instruksi (fungsi State -> State). Tidak ada pengurangan pohon yang rumit, tidak ada prioritas operator. Memilah, menganalisis, dan mengeksekusinya sangat sederhana, karena ini adalah bahasa minimal (gula sintaksis dikompilasi) dan dirancang untuk dibaca mesin daripada dibaca manusia.

Sebaliknya, mem-parsing bahasa C-seperti paling sederhana pun cukup sulit, dan mengeksekusinya memerlukan analisis non-lokal seperti memeriksa dan menyebarkan jenis, menyelesaikan kelebihan beban, mempertahankan tabel simbol, menyelesaikan pengidentifikasi string , mengubah teks linier menjadi AST yang digerakkan oleh prioritas. , dan seterusnya. Itu dibangun di atas konsep yang menjadi alami bagi manusia tetapi harus dengan susah payah direkayasa secara terbalik oleh mesin.

Bytecode JVM, misalnya, dipancarkan oleh javac. Ini hampir tidak pernah perlu dibaca atau ditulis oleh manusia, jadi itu wajar untuk membawanya ke konsumsi oleh mesin. Jika Anda dioptimalkan untuk manusia, JVM akan hanya pada setiap startup membaca kode, mengurai, menganalisis, dan kemudian mengubahnya menjadi representasi menengah menyerupai disederhanakan model mesin seperti pula . Mungkin juga memotong perantara.


sumber
Jadi apa yang Anda katakan adalah bahwa mengkompilasi semuanya ke instruksi pada stack (yaitu System.out.println("hi");dikompilasi ke beberapa instruksi pada stack, int a = 7dikompilasi dengan instruksi pada stack, dll.) Membuat menjalankan program sederhana dan lebih efisien?
Aviv Cohn
2
@Prog Pada dasarnya, ya. Tapi bukan hanya eksekusi, juga analisis. Segala sesuatu yang dilakukan secara terprogram.
Namun, saya tidak mengerti mengapa 2 + 3dikompilasi push 2 push 3 add. The addlangkah pada akhirnya dieksekusi oleh JVM pula dengan menjalankan kode C 2 + 3. Tidak ada cara lain bagi programmer JVM untuk melakukan ini. Mengapa tidak mengkompilasinya 2 + 3, dan minta JVM mengeksekusi kode C 2 + 3(dengan asumsi kode itu ditulis dalam C) segera?
Aviv Cohn
@Prog Penulis JVM tidak bisa hanya menulis 2 + 3dalam kode sumber JVM karena JVM harus bekerja dengan program apa pun yang melakukan operasi dalam urutan apa pun. Membangun kode sumber C dan menunda implementasi C hanya mendorong masalah yang sama ke dalam implementasi C (dan tidak dapat dilakukan dengan mudah, apalagi efisien). Harus ada beberapa struktur data yang menggambarkan program, sehingga dapat ditafsirkan dan JIT dikompilasi, dan "kode sumber yang dapat dibaca manusia" adalah pilihan struktur data yang mengerikan karena alasan yang diuraikan di atas.
7
@Prog Anda sepertinya terlalu fokus pada kasus spesifik 2 + 3. Bagaimana a + b? Maka nilai yang ditambahkan tidak berasal i.argument{1,2}, mereka diambil dari variabel lokal. Bagaimana dengan frobnicate(x[i]) + (Foo.bar() * 2)? Menggunakan desain ini, hanya ada satu addoperasi (untuk int) dan bekerja secara independen dari bagaimana penambahan ditambahkan. Plus, instruksi yang hanya menambahkan literal bilangan bulat tidak ada gunanya: Hasilnya bisa juga sudah dihitung sebelumnya (yaitu bukannya add(2,3)seharusnya push(5)).
20

Jawaban ini berfokus pada JVM, tetapi pada kenyataannya itu berlaku untuk VM.

Mengapa VM perlu meniru perangkat keras, ketika perangkat keras yang sebenarnya sudah melakukan ini untuk kita?

Mereka tidak, tetapi itu membuat VM jauh lebih sederhana dan portabel: VM yang mengemulasi perangkat keras dapat menggunakan model komputasi yang sama daripada CPU perangkat keras apa pun.

JVM khususnya dibangun dengan portabilitas dalam pikiran, pada kenyataannya itu dibangun sehingga bahkan dapat diimplementasikan dalam perangkat keras (mungkin sulit dipercaya hari ini, tetapi asal Jawa ada di dunia tertanam - khususnya, pengontrol untuk televisi interaktif ).

Jika Anda memiliki tujuan seperti ini, diharapkan VM beroperasi sedekat mungkin dengan mesin fisik, karena menerjemahkan ke kode mesin aktual menjadi lebih mudah dan dengan demikian lebih cepat. Setelah Anda memiliki opcodes dari VM, secara teori, yang harus Anda lakukan hanyalah menerjemahkan ke opcodes dari CPU yang sebenarnya dijalankan oleh program. Dalam praktiknya tidak sesederhana itu.

Maksud saya, mengapa perlu tumpukan, atau 'register' VM lainnya - seperti di komputer fisik?

Menggunakan model mesin virtual berbasis stack memiliki keuntungan bahwa ia dapat dengan mudah ditransfer ke mesin register dan stack, sedangkan yang sebaliknya tidak selalu benar. VM berbasis register perlu membuat asumsi tentang jumlah register, ukuran register, dll. Dengan mesin stack, tidak ada asumsi seperti itu yang diperlukan.

CPU fisik yang mendasarinya mengurus semua ini. Mengapa penulis VM tidak hanya menjalankan bytecode yang ditafsirkan dengan instruksi 'biasa' dalam bahasa yang diprogram dengan VM?

Nah, itulah yang dilakukan VM seperti itu, mereka mengartikan bytecode. Bahkan JVM benar-benar melakukan itu, setidaknya sebelum JIT (just-in-time) mulai: ia mengartikan kode byte dan mengeksekusi pernyataan dalam bahasa yang digunakan JVM (biasanya C atau C ++, tetapi bahkan ada satu yang ditulis dalam JavaScript, Doppio ). Namun, perlu diketahui bahwa pernyataan seperti itu pun diterjemahkan ke kode mesin oleh kompiler dan benar-benar terlihat sangat mirip dengan apa yang dihasilkan oleh kompiler Java - yaitu, mereka menggunakan register dan stack untuk melakukan pekerjaan mereka. Perhatikan penggunaan bahasa "ditafsirkan" vs "dikompilasi" menjadi agak kabur pada saat ini.

miraculixx
sumber
Tentu saja, apa pun yang dapat diimplementasikan dalam perangkat lunak dapat diimplementasikan dalam perangkat keras. Selain itu, JVM saat ini (hotspot) adalah kompiler JIT - ia tidak menjalankan pernyataan dalam bahasa yang dituliskan JVM. Jika ya, Java akan berkinerja sangat buruk dan tidak akan menjadi platform yang layak seperti sekarang ini. . (Sial, kebanyakan implementasi Javascript akan lebih cepat)
Billy ONeal
2
@BillyONeal "Daripada mengkompilasi metode demi metode, tepat pada waktunya, Java HotSpot VM segera menjalankan program menggunakan penerjemah, dan menganalisis kode saat dijalankan untuk mendeteksi titik-titik kritis yang penting dalam program. Kemudian memfokuskan perhatian dari pengoptimal kode asli global di hot spot. "dikutip dari oracle.com/technetwork/java/whitepaper-135217.html#2 , bagian" Deteksi Hot Spot "
miraculixx
Iya. "Pengoptimal kode asli" == kompilasi JIT. Ada fase juru bahasa untuk kode yang tampaknya tidak "panas" untuk menghindari JITing hal-hal yang jarang digunakan. Tetapi itu tidak berarti tidak ada JIT yang dilakukan sama sekali.
Billy ONeal
Terimakasih telah menjawab. Apa yang saya kumpulkan dari jawaban Anda adalah bahwa alasan untuk meniru perangkat keras dalam VM (alias dengan 'tumpukan' atau 'register' dll.) Adalah karena nantinya mudah untuk mengkompilasi bytecode atau kode sumber ke kode mesin aktual dari sebuah mesin. CPU fisik. Namun terlepas dari itu - adakah yang bisa didapat dari meniru perangkat keras dalam VM? Saya masih tidak mengerti mengapa seseorang merancang VM akan berpikir dalam hal 'mesin stack' atau 'mesin register' dll ketika sebenarnya kita sedang berbicara tentang perangkat lunak. Apakah saya melewatkan sesuatu?
Aviv Cohn
@ Kat Ok, Anda memiliki bahasa pemrograman, misalnya X. Bagaimana Anda menjalankan programnya? Anda dapat menginterpretasikan sumber atau mengkompilasinya ke kode mesin, atau mengompilasinya menjadi beberapa kode perantara. Sekarang Anda memiliki bahasa pemrograman lain, Y, dan ingin mengimplementasikannya menggunakan X. Jika kedua implementasi adalah interpreter, Anda akan memiliki interpreter Y yang berjalan pada interpreter X, dan ini akan sangat lambat.
18446744073709551615
11

Mengapa VM harus “mesin stack” atau “mesin register” dll.?

Mereka tidak. Jika Anda membutuhkan mesin virtual, itu bisa apa saja.

Mesin virtual yang ada telah muncul sebagai solusi untuk situasi seperti: Sebuah ide yang sangat brilian telah datang ke kepala saya, saya telah menemukan bahasa pemrograman baru! Tetapi saya harus membuat kode. (Tugas yang sangat membosankan!) Tetapi saya tidak ingin membuat kode i8086 karena jelek, dan saya tidak ingin menghasilkan kode 68k karena semua orang menggunakan Intel. Ada juga VAX, tapi saya tidak punya VAX, baik komputer maupun buku VAX. Oleh karena itu saya akan menghasilkan kode untuk beberapa prosesor yang tidak ada secara fisik dan mengimplementasikan prosesor itu dalam perangkat lunak. Spesifikasi VM itu akan membuat bab dalam tesis saya. Secara teori, adalah mungkin untuk mengkompilasinya ke kode asli dari prosesor apa pun, tetapi itu bukan saya.

Di sisi lain, notasi seperti "2 + 3" mungkin tidak akan digunakan oleh VM di masa mendatang karena itu menyiratkan melakukan banyak transformasi sebelum sesuatu dapat dieksekusi.

18446744073709551615
sumber
Terimakasih telah menjawab. Jadi apa yang saya kumpulkan dari jawaban Anda, adalah bahwa motivasi untuk merancang VM yang mengemulasi CPU fisik adalah karena membuatnya mudah untuk mengimplementasikan kompiler yang nantinya dikompilasi dengan kode mesin aktual. Tapi selain itu - apakah ada keuntungan untuk merancang VM dalam hal 'mesin stack' atau 'mesin register' dll?
Aviv Cohn
1
Register membutuhkan algoritma alokasi register, yang membutuhkan teori dan debugging. Mesin stack (khususnya yang nol-operan) hanya dapat menempatkan data pada stack. OTOH, perangkat keras biasanya mengimplementasikan jumlah register yang terbatas daripada tumpukan ukuran variabel. Jadi tumpukan lebih mudah untuk perangkat lunak, register lebih mudah untuk perangkat keras dan mungkin karena itu sedikit lebih cepat.
18446744073709551615
-2

Untuk menjawab pertanyaan aktual yang ditanyakan. Istilah "MESIN virtual" berarti bahwa SEMUA perangkat lunak / perangkat keras disimulasikan / ditiru. Jika Anda menggunakan perangkat lunak / perangkat keras yang mendasari untuk menjalankan instruksi maka Anda tidak memiliki VM, Anda memiliki kompiler / juru bahasa.

Kyrelel
sumber
Apakah ini hanya pendapat Anda atau Anda dapat mendukungnya?
Agak
@ Kirrelel itu tidak benar. Perangkat keras "ALL" ditiru dalam VM "sistem" atau "penuh". Tidak semua VM penuh. Sebagai contoh, lapisan BSD VM dinamai "mesin virtual", meskipun perangkat keras tidak ditiru di sana.
Netch
Saya tidak berpikir pertanyaannya tentu tentang terminologi, melainkan mengapa mesin virtual menerapkan fungsionalitas yang tampaknya sudah ditangani oleh perangkat keras yang sebenarnya
Ryan