Memahami JIT di PHP 8

Terjemahan artikel disiapkan pada awal kursus "Backend-developer dalam PHP"








TL; DR



Kompilator Just In Time di PHP 8 diimplementasikan sebagai bagian dari ekstensi Opcache dan dirancang untuk mengkompilasi kode operasi ke dalam instruksi prosesor saat runtime.



Ini berarti bahwa dengan JIT, beberapa kode operasi tidak harus ditafsirkan oleh Zend VM, instruksi tersebut akan dieksekusi langsung sebagai instruksi tingkat prosesor.



JIT dalam PHP 8



Salah satu fitur PHP 8 yang paling banyak dikomentari adalah kompiler Just In Time (JIT). Ini terdengar di banyak blog dan komunitas - ada banyak desas-desus di sekitarnya, tetapi sejauh ini saya belum menemukan banyak detail tentang cara kerja JIT secara detail.



Setelah banyak upaya dan frustrasi untuk menemukan informasi yang berguna, saya memutuskan untuk mempelajari kode sumber PHP. Menggabungkan sedikit pengetahuan saya tentang C dengan semua informasi yang tersebar yang dapat saya kumpulkan sejauh ini, saya telah berhasil mempersiapkan artikel ini dan berharap ini membantu Anda memahami PHP JIT dengan lebih baik.



Untuk mempermudahnya: Ketika JIT bekerja seperti yang diharapkan, kode Anda tidak akan dieksekusi melalui Zend VM, melainkan akan dieksekusi langsung sebagai seperangkat instruksi tingkat prosesor.



Ini adalah keseluruhan ide.



Tetapi untuk memahami ini lebih baik, kita perlu memikirkan bagaimana php bekerja secara internal. Ini tidak terlalu sulit, tetapi perlu beberapa pengenalan.



Saya sudah menulis artikel dengan gambaran singkat tentang cara kerja php . Jika Anda merasa artikel ini terlalu rumit, baca saja pendahulunya dan kembali. Ini seharusnya membuat segalanya sedikit lebih mudah.



Bagaimana kode PHP dieksekusi?



Kita semua tahu php adalah bahasa yang diartikan. Tapi apa sebenarnya ini artinya?



Setiap kali Anda ingin mengeksekusi kode PHP, baik itu potongan atau aplikasi web keseluruhan, Anda harus melalui penerjemah php. Yang paling umum digunakan adalah PHP FPM dan interpreter CLI. Pekerjaan mereka sangat sederhana: dapatkan kode php, interpretasikan, dan kembalikan hasilnya.



Ini adalah gambaran umum untuk setiap bahasa yang ditafsirkan. Beberapa langkah mungkin berbeda, tetapi gagasan umumnya sama. Dalam PHP kerjanya seperti ini:



  1. Kode PHP dibaca dan dikonversi menjadi serangkaian kata kunci yang dikenal sebagai Token. Proses ini memungkinkan penerjemah memahami bagian mana dari program, setiap bagian kode ditulis. Langkah pertama ini disebut Lexing atau Tokenizing .
  2. , PHP . (Abstract Syntax Tree — AST) , (parsing). AST , , . , «echo 1 + 1» « 1 + 1» , , « , — 1 + 1».
  3. AST, , . -, , (Intermediate Representation IR), PHP (Opcode). AST .
  4. Sekarang kita memiliki opcodes, muncul yang paling menarik: implementasi kode! PHP memiliki mesin bernama Zend VM yang mampu mendapatkan daftar opcode dan mengeksekusinya. Setelah semua opcode dijalankan, program berakhir.




Untuk membuatnya lebih jelas, saya membuat diagram:





Diagram sederhana dari proses interpretasi PHP.



Cukup mudah seperti yang Anda lihat. Tapi ada juga kemacetan di sini: apa gunanya lexing dan parsing kode Anda setiap kali Anda mengeksekusi jika kode php Anda mungkin tidak sering berubah?



Bagaimanapun, kami hanya tertarik pada opcodes, kan? Baik! Inilah sebabnya mengapa ekstensi Opcache ada .



Ekstensi opcache



Ekstensi Opcache dilengkapi dengan PHP dan biasanya tidak ada alasan khusus untuk menonaktifkannya. Jika Anda menggunakan PHP, Anda mungkin harus mengaktifkan Opcache.



Apa yang dilakukannya adalah menambahkan lapisan cache opcode bersama online. Tugasnya adalah mengambil opcodes yang baru-baru ini dihasilkan dari AST kami dan menyimpannya sehingga eksekusi selanjutnya dapat dengan mudah melewati fase lexing dan parsing.



Berikut adalah diagram dari proses yang sama dengan ekstensi Opcache dalam pikiran:





Aliran interpretasi PHP dengan Opcache. Jika file telah diuraikan, php mengekstrak opcode yang di-cache untuknya, daripada menguraikannya kembali.



Itu hanya memesona betapa indahnya langkah-langkah lexing, parsing dan kompilasi dilewati.

Catatan : Di sinilah fitur PHP 7.4 preload berguna ! Ini memungkinkan Anda memberi tahu PHP FPM untuk menguraikan basis kode Anda, mengonversinya menjadi opcode, dan menyimpannya bahkan sebelum Anda benar-benar melakukan sesuatu.


Anda mungkin mulai bertanya-tanya di mana Anda dapat menempel JIT di sini, kan ?! Setidaknya saya harap begitu, itulah sebabnya saya menulis artikel ini ...



Apa yang dilakukan oleh kompiler Just In Time?



Setelah mendengarkan penjelasan Ziva di episode podcast PHP dan JIT dari PHP Internals News , saya bisa mendapatkan beberapa gagasan tentang apa yang sebenarnya harus dilakukan JIT ...



Jika Opcache memungkinkan kode operasi yang lebih cepat sehingga dapat langsung menuju ke Zend VM, JIT dimaksudkan untuk membuatnya bekerja tanpa Zend VM sama sekali.



Zend VM adalah program C yang bertindak sebagai lapisan antara kode operasi dan prosesor itu sendiri. JIT menghasilkan kode yang dikompilasi pada saat runtime, sehingga php dapat melewati Zend VM dan melompat langsung ke prosesor . Secara teori, kita harus mendapat manfaat dari ini dalam hal kinerja.



Awalnya terdengar aneh, karena untuk mengkompilasi kode mesin, Anda harus menulis implementasi yang sangat spesifik untuk setiap jenis arsitektur. Namun sebenarnya itu cukup nyata.



Implementasi JIT dalam PHP menggunakan pustaka DynASM (Dynamic Assembler) , yang memetakan sekumpulan instruksi CPU dalam format tertentu untuk merakit kode untuk berbagai jenis CPU. Dengan demikian, kompiler Just In Time mengubah kode operasi menjadi kode mesin khusus arsitektur menggunakan DynASM.



Meskipun satu pikiran masih menghantuiku ...



Jika preloading mampu mem-parsing kode php ke operasional sebelum eksekusi, dan DynASM dapat mengkompilasi kode operasional ke kode mesin (kompilasi Just In Time), mengapa kita tidak mengkompilasi PHP di tempat menggunakan kompilasi Ahead of Time ?!



Salah satu pemikiran yang saya dapatkan dari episode podcast adalah bahwa PHP diketik dengan lemah, artinya PHP sering tidak tahu apa jenis variabel sampai Zend VM mencoba untuk mengeksekusi opcode tertentu.



Anda dapat memahami ini dengan melihat tipe union zend_value , yang memiliki banyak petunjuk untuk representasi tipe yang berbeda untuk suatu variabel. Kapan pun Zend VM mencoba mengambil nilai dari zend_value, ia menggunakan makro seperti ZSTR_VALyang mencoba mengakses penunjuk string dari penggabungan nilai.



Misalnya, pengendali Zend VM ini harus menangani ekspresi kurang dari atau sama dengan (<=). Lihat bagaimana ia bercabang ke banyak jalur kode berbeda untuk menebak jenis operan.



Menduplikasi logika inferensi tipe ini dengan kode mesin tidak layak dan berpotensi membuat segalanya lebih lambat.



Kompilasi terakhir setelah tipe dievaluasi juga bukan pilihan yang baik karena mengkompilasi ke kode mesin adalah tugas yang intensif CPU. Jadi, mengkompilasi SEMUA saat runtime adalah ide yang buruk.



Bagaimana perilaku kompiler Just In Time?



Kita sekarang tahu bahwa kita tidak dapat menyimpulkan tipe untuk menghasilkan pra-kompilasi yang cukup baik. Kita juga tahu bahwa kompilasi saat runtime itu mahal. Bagaimana JIT berguna untuk PHP?



Untuk menyeimbangkan persamaan ini, PHP JIT mencoba untuk mengkompilasi hanya beberapa opcode yang dianggapnya layak. Untuk melakukan ini, itu profil opcodes dijalankan oleh mesin virtual Zend dan memeriksa yang masuk akal untuk dikompilasi. (tergantung pada konfigurasi Anda) .



Ketika opcode tertentu dikompilasi, ia kemudian mendelegasikan eksekusi ke kode yang dikompilasi itu daripada mendelegasikan ke Zend VM. Ini terlihat seperti diagram di bawah ini:





Aliran interpretasi PHP dengan JIT. Jika sudah dikompilasi, opcodes tidak dieksekusi melalui Zend VM.



Dengan demikian, ada beberapa instruksi dalam ekstensi Opcache yang menentukan apakah kode operasi tertentu harus dikompilasi atau tidak. Jika demikian, kompilator mengubahnya menjadi kode mesin menggunakan DynASM dan mengeksekusi kode mesin yang baru dibuat ini.



Menariknya, karena implementasi saat ini memiliki batas megabyte untuk kode yang dikompilasi (juga dapat dikonfigurasi), eksekusi kode harus dapat dengan mulus beralih antara JIT dan kode yang ditafsirkan.



Ngomong-ngomong, pembicaraan oleh Benoit Jacquemont tentang JIT dari php ini sangat membantu saya.



Saya masih tidak yakin dalam kasus-kasus spesifik apa kompilasi berlangsung, tetapi saya pikir saya belum benar-benar ingin mengetahui hal ini.



Jadi peningkatan produktivitas Anda mungkin tidak akan kolosal



Saya harap ini jauh lebih jelas sekarang MENGAPA semua orang mengatakan bahwa sebagian besar aplikasi php tidak akan mendapatkan banyak manfaat kinerja dari menggunakan kompiler Just In Time. Dan mengapa rekomendasi Ziv untuk membuat profil dan bereksperimen dengan berbagai konfigurasi JIT untuk aplikasi Anda adalah cara terbaik.



Opcode yang dikompilasi biasanya akan tersebar di beberapa permintaan jika Anda menggunakan PHP FPM, tetapi ini masih bukan pengubah permainan.



Ini karena JIT mengoptimalkan operasi CPU, dan saat ini sebagian besar aplikasi php lebih terikat I / O daripada yang lainnya. Tidak masalah jika operasi pemrosesan dikompilasi jika Anda harus mengakses disk atau jaringan. Pengaturan waktunya akan sangat mirip.



Jika hanya...



Anda melakukan sesuatu yang non-I / O, seperti pemrosesan gambar atau pembelajaran mesin. Apa pun selain I / O akan mendapat manfaat dari kompiler Just In Time. Ini juga alasan orang sekarang mengatakan mereka lebih condong ke arah menulis fungsi PHP asli yang ditulis dalam PHP daripada C. Biaya overhead tidak akan secara dramatis berbeda jika fungsi tersebut dikompilasi pula.



Waktu yang menarik untuk menjadi programmer PHP ...



Saya harap artikel ini bermanfaat bagi Anda dan Anda mendapatkan pemahaman yang lebih baik tentang apa itu JIT dalam PHP 8. Jangan ragu untuk men-tweet saya jika Anda ingin menambahkan sesuatu yang saya mungkin sudah lupa di sini, dan jangan lupa untuk membagikan ini dengan sesama pengembang Anda, itu pasti akan menambah sedikit nilai untuk percakapan Anda!-- @nawarian






PHP:







All Articles