PHP GR8: Akankah JIT Meningkatkan Kinerja PHP 8



PHP adalah salah satu bahasa pengembangan utama di Badoo. Di pusat data kami, ribuan inti prosesor sibuk mengeksekusi jutaan baris kode PHP. Kami terus mengikuti berita dan secara aktif mencari cara untuk meningkatkan produktivitas, karena bahkan sedikit optimasi pada volume kami mengarah pada penghematan sumber daya yang signifikan. Salah satu berita kinerja PHP utama adalah penampilan JIT dalam versi bahasa yang kedelapan. Ini, tentu saja, tidak dapat tetap tanpa perhatian kami, dan kami menerjemahkan sebuah artikel tentang apa itu JIT, bagaimana itu akan diterapkan dalam PHP, mengapa diputuskan untuk melakukannya dan apa yang diharapkan dari itu.

Jika Anda tidak meninggalkan gua atau tidak datang dari masa lalu (dalam hal ini, selamat datang), Anda sudah tahu bahwa PHP 8 akan memiliki JIT: hari lain, suara dengan tenang dan damai, dan sebagian besar peserta memberikan suara mendukung implementasi, jadi semuanya diputuskan .

Dalam kegembiraan, Anda bahkan dapat menggambarkan beberapa gerakan gila seperti di foto (ini, omong-omong, disebut "Detroit JIT":



Sekarang, duduk dan baca artikel mitos-sanggahan ini. Saya ingin mengklarifikasi kesalahpahaman terkait dengan apa JIT itu dan bagaimana itu berguna, dan berbicara tentang cara kerjanya (tetapi tidak terlalu banyak detail sehingga Anda tidak bosan).

Karena saya tidak tahu siapa yang akan membaca artikel itu, saya akan beralih dari pertanyaan sederhana ke rumit. Jika Anda sudah tahu jawaban pertanyaan dalam judul, Anda dapat dengan aman melewati bab yang sesuai.

Apa itu JIT?


PHP diimplementasikan berdasarkan mesin virtual (kami menyebutnya Zend VM). Bahasa mengkompilasi kode sumber PHP menjadi instruksi yang dimengerti mesin virtual (ini disebut tahap kompilasi). Instruksi mesin virtual yang diperoleh pada tahap kompilasi disebut opcodes. Pada tahap runtime, Zend VM mengeksekusi opcodes, sehingga melakukan pekerjaan yang diperlukan.

Sirkuit ini berfungsi dengan baik. Selain itu, alat-alat seperti APC (sebelum) dan OpCache (hari ini) men-cache hasil dari tahap kompilasi, jadi tahap ini hanya dilakukan jika perlu.

Singkatnya, JIT adalah strategi kompilasi just-in-time (pada waktu yang tepat), di mana kode pertama-tama diterjemahkan ke dalam representasi perantara, yang kemudian berubah menjadi kode mesin yang bergantung pada arsitektur selama eksekusi.

Dalam PHP, ini berarti bahwa JIT akan mempertimbangkan instruksi untuk mesin virtual yang diterima pada tahap kompilasi sebagai representasi perantara dan mengeluarkan kode mesin yang tidak lagi dijalankan oleh Zend VM, tetapi langsung oleh prosesor.

Mengapa PHP membutuhkan JIT?


Sesaat sebelum munculnya PHP 7.0, fokus utama tim PHP adalah kinerja bahasa. Sebagian besar perubahan utama dalam PHP 7.0 ada di patch PHPNG, yang sangat meningkatkan cara PHP menggunakan memori dan prosesor. Sejak itu, kita masing-masing harus melirik kinerja bahasa.

Setelah rilis PHP 7.0, peningkatan kinerja berlanjut: tabel hash (struktur data utama dalam PHP) dioptimalkan, spesialisasi opcode tertentu dalam Zend VM dan spesialisasi urutan tertentu dalam kompiler dilaksanakan, Pengoptimal (komponen OpCache) terus ditingkatkan, dan banyak perubahan lainnya diterapkan.

Kebenaran yang keras adalah bahwa sebagai hasil dari semua optimisasi ini, kami dengan cepat mendekati batas peluang peningkatan produktivitas.

Harap dicatat: dengan "batas peluang peningkatan" yang saya maksudkan adalah fakta bahwa pertukaran yang harus dilakukan untuk perbaikan lebih lanjut tidak lagi terlihat menarik. Ketika datang untuk mengoptimalkan kinerja, kami selalu berbicara tentang pertukaran. Seringkali, demi produktivitas, kita harus mengorbankan kesederhanaan. Semua orang ingin berpikir bahwa kode paling sederhana juga yang tercepat, tetapi di dunia modern pemrograman C ini tidak demikian. Yang tercepat paling sering adalah kode yang disiapkan untuk mengambil keuntungan dari struktur internal arsitektur atau struktur yang dibangun ke dalam platform / kompiler. Kesederhanaan saja tidak menjamin kinerja yang lebih baik.

Oleh karena itu, pada tahap ini, cara terbaik untuk memeras kinerja lebih banyak lagi dari PHP adalah dengan mengimplementasikan JIT.

Akankah JIT mempercepat situs saya?


Kemungkinan besar, tidak signifikan.

Ini mungkin bukan jawaban yang Anda harapkan. Faktanya adalah bahwa secara umum, aplikasi PHP dibatasi oleh input / output (terikat I / O), dan JIT bekerja paling baik dengan kode yang dibatasi oleh prosesor (terikat CPU).

Apa yang dimaksud dengan "dibatasi oleh I / O dan prosesor"?


Untuk menggambarkan karakteristik kinerja keseluruhan beberapa kode atau aplikasi, kami menggunakan istilah "dibatasi oleh input-output" dan "dibatasi oleh prosesor".

Definisi paling sederhana:

  • kode yang dibatasi oleh I / O akan bekerja lebih cepat jika kita menemukan cara untuk meningkatkan (mengurangi, mengoptimalkan) operasi I / O yang dilakukan;
  • kode terbatas prosesor akan bekerja lebih cepat jika kita menemukan cara untuk meningkatkan (mengurangi, mengoptimalkan) instruksi yang dijalankan oleh prosesor atau secara ajaib meningkatkan kecepatan jam prosesor.

Kode dan aplikasi dapat dibatasi oleh I / O, oleh prosesor, atau keduanya.

Secara umum, aplikasi PHP cenderung dibatasi oleh I / O: hambatan utamanya adalah seringnya operasi I / O - menghubungkan, membaca dan menulis ke database, cache, file, soket, dll.

Seperti apa bentuk kode PHP terbatas prosesor?


Mungkin beberapa programmer PHP baru mengenal kode terbatas prosesor karena sifat sebagian besar aplikasi PHP: mereka biasanya bertindak sebagai tautan ke basis data atau cache, mengambil dan menghasilkan sejumlah kecil tanggapan HTML / JSON / XML.

Anda dapat melihat basis kode Anda dan menemukan banyak kode yang tidak ada hubungannya dengan I / O, kode yang memanggil fungsi yang tidak ada hubungannya dengan I / O. Dan Anda mungkin bingung bahwa ini tidak membuat aplikasi Anda dibatasi oleh prosesor, meskipun kodenya memiliki lebih banyak baris yang tidak berfungsi dengan I / O daripada bekerja.

Faktanya adalah bahwa PHP adalah salah satu bahasa yang ditafsirkan tercepat. Tidak ada perbedaan nyata antara memanggil fungsi yang tidak menggunakan I / O di Zend VM dan dalam kode mesin. Tentu saja, ada beberapa perbedaan, tetapi kode mesin dan Zend VM menggunakan konvensi pemanggilan, jadi tidak masalah -___() Anda memanggil -___() dalam opcodes atau dalam kode mesin - ini tidak akan memiliki efek nyata pada kinerja seluruh aplikasi yang melakukan panggilan.

Catatan: secara sederhana, konvensi panggilan adalah urutan instruksi yang dijalankan sebelum memasuki fungsi lain. Dalam kedua kasus, konvensi pemanggilan meneruskan argumen ke stack.

Anda bertanya: "Bagaimana dengan loop, tail tail, dan banyak lagi?" PHP cukup pintar - dan ketika Pengoptimal dari OpCache dihidupkan, kode Anda akan secara ajaib dikonversi ke versi yang lebih efisien dari apa yang Anda tulis.

Perlu dicatat di sini bahwa JIT tidak akan mengubah konvensi pemanggilan Zend VM. Ini dilakukan karena PHP harus dapat beralih antara mode JIT dan VM kapan saja (oleh karena itu, mereka memutuskan untuk tetap menggunakan konvensi saat ini). Akibatnya, semua panggilan yang Anda lihat di mana saja menggunakan JIT tidak akan berfungsi lebih cepat.

Jika Anda ingin melihat seperti apa kode PHP terbatas prosesor, lihat di sini: https://github.com/php/php-src/blob/master/Zend/bench.php . Ini adalah contoh ekstrem, tetapi itu menunjukkan bahwa semua kemegahan JIT terungkap dalam matematika.

Harus membuat kompromi ekstrem untuk mempercepat perhitungan matematika dalam PHP?


Tidak. Kami melakukan ini demi memperluas jangkauan aplikasi bahasa (dan memperluas signifikan).

Kami tidak ingin sesumbar, tetapi PHP mendominasi web. Jika Anda terlibat dalam pengembangan web dan tidak mempertimbangkan menggunakan PHP dalam proyek Anda berikutnya, maka Anda melakukan sesuatu yang salah (menurut pengembang PHP yang sangat bias).

Sekilas, mungkin tampak bahwa akselerasi perhitungan matematis dalam PHP memiliki aplikasi yang sangat sempit. Namun, ini membuka jalan bagi kita, misalnya, untuk pembelajaran mesin, rendering 3D, rendering 2D (GUI) dan analisis data.

Mengapa ini tidak dapat diimplementasikan dalam PHP 7.4?


Di atas, saya menyebut JIT sebagai kompromi yang ekstrem, dan saya benar-benar berpikir demikian: ini adalah salah satu strategi kompilasi yang paling sulit di antara semua yang ada, jika bukan yang paling sulit. Menerapkan JIT adalah peningkatan kompleksitas yang signifikan.

Jika Anda bertanya kepada Dmitry, penulis JIT, apakah ia membuat PHP rumit, ia akan menjawab: "Tidak, saya benci kompleksitas" (ini kutipan).

Pada dasarnya, "kompleks" berarti "apa yang tidak kita mengerti." Dan hari ini, beberapa pengembang bahasa benar-benar memahami implementasi JIT yang ada.

Bekerja dengan PHP 7.4 mengalami kemajuan pesat, dan pengenalan JIT dalam versi ini akan mengarah pada fakta bahwa hanya sedikit yang dapat melakukan debug, memperbaiki dan meningkatkan bahasa. Ini tidak dapat diterima bagi mereka yang memberikan suara menentang JIT di PHP 7.4.

Sebelum rilis PHP 8, banyak dari kita akan memahami implementasi JIT. Ada fitur yang ingin kita implementasikan, dan alat yang ingin kita tulis ulang untuk versi kedelapan, jadi kita harus terlebih dahulu memahami JIT. Kami membutuhkan waktu ini, dan kami sangat berterima kasih bahwa mayoritas memilih untuk memberikannya kepada kami.

Kompleks tidak identik dengan yang mengerikan. Kompleksitas dapat seindah bintang nebula, dan ini hanya tentang JIT. Dengan kata lain, bahkan ketika 20 orang di tim kami mulai memahami JIT tidak lebih buruk dari Dmitry, ini tidak akan mengubah kompleksitas sifat JIT.

Apakah pengembangan PHP akan melambat?


Tidak ada alasan untuk berpikir demikian. Kami memiliki cukup waktu, sehingga dapat diperdebatkan bahwa pada saat PHP 8 siap, akan ada cukup banyak di antara kita yang telah cukup menguasai JIT untuk bekerja tidak kurang efisien daripada hari ini ketika datang untuk memperbaiki bug dan mengembangkan PHP.

Ketika Anda mencoba menghubungkan ini dengan gagasan tentang kompleksitas asli JIT, ingatlah bahwa sebagian besar waktu yang kami habiskan untuk memperkenalkan fitur baru dihabiskan untuk membahasnya. Paling sering, ketika mengerjakan fitur dan memperbaiki bug, menulis kode membutuhkan beberapa menit atau jam, dan diskusi membutuhkan waktu berminggu-minggu atau berbulan-bulan. Dalam kasus yang jarang terjadi, kode harus ditulis berjam-jam atau berhari-hari, tetapi bahkan diskusi selalu berlangsung lebih lama.

Hanya itu yang ingin saya katakan.

Dan karena kita berbicara tentang kinerja, saya mengundang rekan saya Pavel Murzakov ke laporan pada 17 Mei di konferensi PHP Russia. Pasha tahu cara memeras CPU terakhir dari kode PHP!

Source: https://habr.com/ru/post/id448622/


All Articles