Mengompilasi Ruby ke Bahasa Mesin
(patshaughnessy.net)- YJIT dan ZJIT adalah arsitektur compiler JIT yang mengubah kode Ruby menjadi bahasa mesin untuk meningkatkan kecepatan eksekusi di Ruby 3.x
- YJIT menghitung jumlah pemanggilan setiap fungsi atau blok dan ketika mencapai ambang tertentu, kode tersebut diubah menjadi bahasa mesin
- Kode yang telah dikonversi disimpan dalam blok YJIT, dan setiap blok mengubah beberapa instruksi YARV menjadi instruksi bahasa mesin ARM64 yang sesuai
- Menggunakan Branch Stub untuk mengamati tipe data aktual saat runtime dan secara selektif menghasilkan instruksi bahasa mesin yang sesuai
- Struktur ini adalah mekanisme inti untuk sekaligus mencapai peningkatan performa eksekusi Ruby dan efisiensi penanganan tipe dinamis
Chapter 4: Mengompilasi Ruby ke Bahasa Mesin
Interpreting vs. Compiling Ruby Code
- Tidak ada rincian lebih lanjut di teks asli
Counting Method and Block Calls
- YJIT melacak jumlah pemanggilan fungsi dan blok dalam program untuk mengidentifikasi kode hotspot
- Menyimpan nilai jit_entry dan jit_entry_calls di samping setiap urutan instruksi YARV untuk fungsi atau blok
jit_entrypada awalnya bernilai null, lalu nanti menyimpan pointer ke kode mesin yang dihasilkan YJITjit_entry_callsbertambah 1 setiap kali dipanggil
- Ketika jumlah pemanggilan mencapai ambang tertentu, YJIT mengompilasi kode tersebut ke bahasa mesin
- Ambang default Ruby 3.5 adalah 30 kali untuk program kecil dan 120 kali untuk aplikasi berskala besar
- Dapat diubah saat eksekusi dengan opsi
--yjit-call-threshold
- Dengan cara ini, YJIT hanya mengubah kode yang sering dijalankan menjadi bahasa mesin untuk menciptakan jalur eksekusi yang efisien
YJIT Blocks
- YJIT menyimpan instruksi bahasa mesin yang dihasilkan dalam blok YJIT
- Blok YJIT berbeda dari blok Ruby, dan merepresentasikan sebagian rentang instruksi YARV
- Setiap fungsi atau blok Ruby terdiri dari beberapa blok YJIT
- Dalam program contoh, YJIT mulai mengompilasi saat blok dijalankan untuk ke-30 kalinya
- Mengubah instruksi YARV pertama
getlocal_WC_1ke bahasa mesin dan membuat blok YJIT baru - Setelah itu mengompilasi instruksi
getlocal_WC_0tambahan dan memasukkannya ke blok yang sama
- Mengubah instruksi YARV pertama
- Menurut Figure 4-8, YJIT menghasilkan instruksi ARM64 untuk memuat nilai ke register x1 dan x9 pada prosesor M1
getlocal_WC_1menyimpan variabel lokal dari stack frame sebelumnya, sedangkangetlocal_WC_0menyimpan variabel dari stack saat ini ke stack- Instruksi bahasa mesin yang dihasilkan menjalankan perilaku yang sama
YJIT Branch Stubs
- Saat YJIT mengompilasi instruksi
opt_plus, muncul masalah karena tipe operand tidak diketahui- Instruksi bahasa mesin yang dibutuhkan berbeda tergantung pada tipe seperti integer, string, atau floating point
- Contoh: penjumlahan integer menggunakan instruksi
adds, sedangkan penjumlahan floating point memerlukan instruksi lain
- Untuk menyelesaikannya, YJIT menggunakan pendekatan observasi saat runtime alih-alih analisis awal
- Saat program berjalan, ia memeriksa tipe nilai yang benar-benar diteruskan lalu menghasilkan bahasa mesin yang sesuai
- Untuk perilaku ini digunakan Branch Stub
- Ketika cabang (branch) baru belum memiliki blok yang terhubung, ia sementara dihubungkan ke stub
- Setelah tipe aktual diketahui, stub tersebut diganti dengan blok yang sesuai
ZJIT (hanya disebutkan)
- Daftar isi mencakup bagian terkait ZJIT, tetapi tidak ada penjelasan rinci di isi utama
Ringkasan
- YJIT adalah compiler JIT untuk meningkatkan efisiensi eksekusi bahasa bertipe dinamis di Ruby 3.5
- Pemicu kompilasi berbasis jumlah pemanggilan, struktur blok YJIT, dan pemeriksaan tipe saat runtime melalui Branch Stub adalah inti utamanya
- Mengubah kode menjadi instruksi bahasa mesin nyata pada arsitektur ARM64 untuk meningkatkan kecepatan eksekusi kode Ruby
- ZJIT disebut sebagai JIT generasi berikutnya, tetapi tidak ada rincian lebih lanjut di isi utama
1 komentar
Komentar Hacker News
Dulu ada masa ketika MacRuby dikompilasi menjadi kode native di macOS dengan LLVM dan terintegrasi dengan framework Objective‑C
Itu ide yang cukup keren, tetapi pada akhirnya Apple tampaknya beralih arah ke Swift
Kalau versi barunya terbit, saya pasti akan membeli dan membaca buku Ruby Under a Microscope. Saya masih menyukai Ruby, tetapi tidak banyak kesempatan untuk benar-benar memakainya
Sekarang proyek itu dilanjutkan orang lain, tetapi saat ini kesannya fokusnya lebih besar ke DragonRuby (implementasi Ruby yang berfokus pada game)
Sebagai referensi, ada juga artikel wiki
Hanya saja, API lama mungkin sudah tidak lagi didukung
VB6 benar-benar sangat cepat untuk pengembangan, dan bisa dipakai sampai Direct3D maupun ASP Classic
Keanggunan dan kemudahan pengembangan Ruby mengingatkan saya pada masa itu
Jika Ruby punya alat GUI setara VB6, mungkin popularitasnya akan cukup berbeda
Senang sekali melihat Pat terus melanjutkan proyek ini
Buku pertamanya, Ruby Under a Microscope, dan tulisan blognya memberi saya inspirasi besar
Saya bahkan pernah bertemu langsung dengannya di konferensi Euruko, dan dia benar-benar orang yang luar biasa
Saat pertama kali membaca Ruby Under a Microscope, saya benar-benar menikmatinya
Berkat buku itu, saya juga pernah memakainya saat memecahkan soal CTF
Belakangan ini saya tidak lagi mengikuti implementasi internal Ruby, tetapi kalau versi baru keluar saya pasti akan membelinya
Melihat tulisan ini membuat saya ingin membaca versi baru bukunya lagi
Ngomong-ngomong soal kompilasi Ruby, saya penasaran apakah ada yang pernah mencoba Sorbet compiler buatan para developer Stripe
Tulisan pembukaan source Sorbet Compiler
Kompilasi AOT memang sangat sulit di Ruby
Pendekatan Sorbet menarik karena bisa membuat jalur cepat berdasarkan pemeriksaan tipe Ruby
Saya juga sedang membuat compiler Ruby sebagai proyek pribadi, sambil merujuk ke hokstad.com/compiler dan
writing-a-compiler-in-ruby
Saat ini saya fokus meloloskan RubySpec, dan nanti ingin mencoba optimasi berbasis tipe juga
Ini tidak berhubungan langsung dengan kompilasi Ruby, tetapi buku Enterprise Integration with Ruby memberi saya banyak wawasan tentang pemanfaatan Ruby di luar web
Sejak mengenal MRuby, saya jadi ketagihan mengubah proyek dan skrip saya menjadi executable mandiri
Senang melihat Ruby Under a Microscope masih terus diperbarui
Menurut saya, ini bacaan wajib bagi siapa pun yang ingin memahami cara kerja internal Ruby
Saya penasaran, ketika sebuah blok YJIT dijalankan berkali-kali, bagaimana ia melacak kompilasi untuk tiap tipe input
Saya ingin tahu bagaimana Ruby menangani berbagai tipe seperti int atau float
Ia memakai pendekatan “wait‑and‑see” dengan menunda kompilasi sampai tipe aktual diberikan
Versi blok untuk masing-masing tipe dikelola terpisah, lalu dipanggil sesuai kebutuhan
Algoritme ini disebut Basic Block Versioning
Maxime Chevalier‑Boisvert dari Shopify menjelaskannya dengan baik dalam video presentasi RubyConf 2021
Engine JIT baru, ZJIT, tampaknya memakai pendekatan yang berbeda
Membuat bahasa bertipe dinamis menjadi cepat dengan JIT biasanya dibayar dengan peningkatan penggunaan memori
Kalau bukan perusahaan besar seperti Shopify, ini mungkin justru jadi masalah yang lebih besar
Instance cloud modern memberi sekitar 4GiB memori per core, jadi kode JIT beberapa ratus MB masih sangat mungkin ditanggung
Cara YJIT menemukan hotspot dengan hanya menghitung jumlah pemanggilan fungsi terlihat sederhana
Saya jadi penasaran apakah tidak ada fitur untuk mendeteksi komputasi berat di dalam loop seperti pada JIT JavaScript
Struktur blok Ruby tampaknya juga bisa membantu optimasi seperti ini
Jadi JIT bisa memperlakukan blok sebagai fungsi terpisah dan secara alami mengoptimalkan perulangan
Bagian ini akan dibahas lebih dalam di bab berikutnya