Lewati ke konten

Tolok Ukur

Phasis kira-kira dua orde magnitudo lebih lambat daripada V8 pada JavaScript yang dibatasi oleh dispatch. Batas atas itu merupakan biaya bawaan interpreter yang di-host oleh PHP dibandingkan dengan compiler JIT native. Pertanyaan bagi embedder bukanlah "bagaimana perbandingannya dengan V8" — melainkan "apakah cukup cepat untuk kebutuhan saya." Untuk sebagian besar beban kerja embedding, jawabannya ya.

Saat batas atas tidak menjadi masalah

  • Templating — render gaya SSR untuk halaman biasa dalam waktu jauh di bawah 10 ms.
  • Aturan validasi — JS yang diberikan pengguna untuk validasi formulir selesai dalam hitungan mikrodetik.
  • Transformasi data — array.map(transform) pada ratusan item selesai dalam hitungan puluhan milidetik.
  • Runner sandbox — skrip singkat dari pengguna dengan batas host 1 detik menyisakan banyak ruang.

Saat batas atas menjadi masalah

  • Loop dalam yang berat komputasi — pemrosesan gambar, reduksi array besar, simulasi. Pindahkan komputasinya ke pustaka PHP native dan ekspos sebagai fungsi host.
  • Animasi real-time — apa pun yang berjalan pada 60 fps. JS tersebut sebaiknya berada di klien (browser, aplikasi native), bukan di engine tertanam.
  • Implementasi ulang JS untuk algoritma yang mahal secara komputasi — kriptografi, kompresi, parsing. PHP memiliki padanan native yang 1000× lebih cepat; gunakan padanan tersebut.

Model mental yang tepat: Phasis adalah lapisan perekat untuk logika berbentuk JS yang disematkan dalam aplikasi berbentuk PHP. Host PHP menangani komputasi; lapisan JS menangani konfigurasi, percabangan, dan transformasi kecil.

Hal yang membuat engine cepat (dibandingkan tree-walker naif)

  • VM Bytecode — sebagian besar dispatch yang sering terjadi berjalan melalui switch opcode milik VM::execute, bukan AST.
  • Call stack khusus — setiap pemanggilan JS menjaga status di dalam VM::execute dan menyimpan frame pemanggil ke dalam pool; tidak ada frame PHP baru untuk setiap pemanggilan JS. Lihat VM Bytecode § Call stack khusus.
  • Cache inline — cache monomorfik per-PC membuat pembacaan metode prototipe pada instans (pola c.method()) melewati penelusuran rantai prototipe.
  • Generator snapshot frame — isi generator sederhana ditangguhkan dengan menangkap status dispatch ke objek heap, alih-alih melintasi Fiber PHP.
  • Transpiler JsToPhp — isi fungsi yang berat pada numerik / variabel lokal (termasuk pembacaan obj.method(), new Ctor(), dan this.member) dikompilasi menjadi closure PHP yang diturunkan oleh tracing JIT PHP 8.5 menjadi kode mesin native.
  • Cache JSON.stringify — JSON.stringify(o) menyimpan output-nya pada objek masukan dengan invalidasi versi transitif, mempercepat idiom deep-clone JSON.parse(JSON.stringify(o)).

Mengukur beban kerja Anda sendiri

Shell
php bench/run.php

Perintah ini menjalankan rangkaian microbench yang disertakan di bench/microbench.js — beberapa beban kerja sintetis (aritmetika loop, fungsi rekursif, pembuatan objek, pemanggilan metode prototipe, penambahan array, bolak-balik JSON, dan sebagainya) dan mencetak median waktu nyata untuk tiap pengujian. Berguna sebagai pemeriksaan kewajaran; angka saat ini di-commit ke BENCH.md pada setiap kali CI menjalankan benchmark.

Untuk kode Anda sendiri, ganti dengan skrip apa pun:

Shell
php -d xdebug.mode=off bench/run.php path/to/your-script.js

Untuk pembuatan profil tingkat PHP, profiler Xdebug bekerja dengan Phasis sama seperti pada aplikasi PHP lainnya. Jalankan skrip melalui xdebug.mode=profile dan periksa output cachegrind untuk melihat kelas Phasis\ mana yang paling dominan.

Peta jalan

Hal-hal yang paling berpotensi memperkecil kesenjangan V8, diurutkan secara kasar berdasarkan tingkat kemudahan penerapan:

  • Cache inline polimorfik — izinkan satu titik pemanggilan LOAD_MEMBER menyimpan 2-4 bentuk cache sebelum kembali ke dispatch lambat. Membantu kode yang memproses receiver heterogen.
  • yield* dan try-with-yield pada jalur generator snapshot frame — bentuk-bentuk ini saat ini dialihkan ke tree-walker berbasis Fiber. Masing-masing memerlukan penanganan state machine secara eksplisit (penerusan delegate-iterator, stack handler yang dipertahankan selama suspend) sebelum aman untuk snapshot.
  • Generator asinkron dan await pada snapshot — spesifikasi menggabungkan penangguhan generator dengan antrean microtask. Setelah isi bytecode mendukung yield dan await, generator asinkron juga akan kompatibel dengan snapshot.
  • Inferensi tipe parameter di JsToPhp — parameter fungsi secara default bertipe numerik, sehingga function (obj) { obj.method() } membuat compiler keluar lebih awal. Inferensi parameter bertipe objek berdasarkan penggunaannya di dalam isi fungsi akan memungkinkan pemanggilan metode pada argumen fungsi tanpa mengharuskan receiver dibuat secara lokal melalui new.
  • Emisi opcache langsung — lewati langkah eval() milik JsToPhp dengan menulis opcode berformat opcache secara langsung. Ini item yang paling rumit; yang lain dapat dikerjakan dalam arsitektur yang ada.

Semua ini tidak diperlukan untuk beban kerja embedding yang ditargetkan Phasis.