Výkonnostné testy
Phasis je pri úlohách závislých od dispečingu JavaScript približne o dva rády pomalší než V8. Tento limit vyplýva zo samotnej podstaty interpretera hostovaného v PHP v porovnaní s natívnym JIT kompilátorom. Vkladateľ sa nemusí pýtať „ako si vedie v porovnaní s V8“, ale „je dosť rýchly na to, čo potrebujem“. Pri väčšine úloh s vloženým enginom je odpoveď áno.
Kedy na limite nezáleží
- Šablóny — vykreslenie typickej stránky v štýle SSR trvá výrazne menej než 10 ms.
- Pravidlá validácie — validácia formulára pomocou používateľom zadaného JS sa dokončí za mikrosekundy.
- Transformácie údajov —
array.map(transform)nad stovkami položiek sa dokončí za desiatky milisekúnd. - Spúšťače v izolovanom prostredí — pri krátkych skriptoch dodaných používateľom a limite hostiteľa 1 sekunda zostáva dostatočná časová rezerva.
Kedy na limite záleží
- Výpočtovo náročné vnútorné slučky — spracovanie obrázkov, redukcie veľkých polí, simulácie. Presuňte výpočty do natívnej knižnice PHP a sprístupnite ju ako hostiteľskú funkciu.
- Animácia v reálnom čase — všetko, čo má bežať pri 60 snímkach za sekundu. To JS patrí do klienta (prehliadača, natívnej aplikácie), nie do vloženého enginu.
- JS opätovné implementácie výpočtovo náročných algoritmov — kryptografia, kompresia, parsovanie. PHP má natívne ekvivalenty, ktoré sú 1000× rýchlejšie; použite ich.
Správny mentálny model: Phasis je prepojovacia vrstva pre logiku v štýle JS vloženú do aplikácií v štýle PHP. Hostiteľ PHP zabezpečuje výpočty; vrstva JS sa stará o konfiguráciu, vetvenie a malé transformácie.
Vďaka čomu je engine rýchly (v porovnaní s naivným stromovým interpreterom)
- VM s bajtkódom — väčšina horúceho dispečingu prebieha cez prepínač opkódov
VM::execute, nie cez AST. - Vlastný zásobník volaní — každé volanie JS udržiava stav v
VM::executea ukladá rámec volajúceho do fondu; pri každom volaní JS sa nevytvára nový rámec PHP. Pozrite si Virtuálny stroj bajtkódu § Vlastný zásobník volaní. - Inline cache — monomorfná cache pre každé PC umožňuje pri čítaní metódy prototypu na inštancii (vzor
c.method()) preskočiť prehľadávanie reťazca prototypov. - Generátory so snímkou rámca — jednoduché telá generátorov sa pozastavia zachytením stavu dispečingu do objektu na halde namiesto prechodu cez Fiber PHP.
- Transpilátor JsToPhp — telá funkcií s číselnými hodnotami a množstvom lokálnych premenných (vrátane čítaní
obj.method(),new Ctor()athis.member) sa kompilujú do uzáverov PHP, ktoré trasovací JIT nástroja PHP 8.5 prekladá na natívny strojový kód. - Cache JSON.stringify —
JSON.stringify(o)ukladá výstup do vstupného objektu a používa tranzitívnu invalidáciu verzií, čím zrýchľuje idiom hlbokého klonovaniaJSON.parse(JSON.stringify(o)).
Meranie vlastnej záťaže
php bench/run.php
Týmto sa spustí sada mikrobenchmarkov dodávaná s bench/microbench.js — niekoľkými syntetickými úlohami (aritmetika v cykloch, rekurzívne funkcie, vytváranie objektov, volania metód prototypu, pridávanie prvkov do poľa, spiatočný prevod JSON a podobne) — a vypíše medián času vykonania každého testu. Je to užitočná kontrola; aktuálne výsledky sa pri každom spustení benchmarkov CI ukladajú do BENCH.md.
Pre vlastný kód môžete použiť ľubovoľný skript:
php -d xdebug.mode=off bench/run.php path/to/your-script.js
Na profilovanie na úrovni PHP funguje profiler nástroja Xdebug s Phasis rovnako ako s akoukoľvek inou aplikáciou PHP. Spustite skript pomocou xdebug.mode=profile a vyskúmajte výstup cachegrind, aby ste zistili, ktoré triedy Phasis\ zaberajú najviac času.
Plán vývoja
Čo by najviac zmenšilo rozdiel v V8, zoradené približne podľa náročnosti realizácie:
- Polymorfné inline cache — umožniť jednému volaciemu miestu
LOAD_MEMBERuchovávať 2-4 tvary v cache, kým sa prejde na pomalšie spracovanie. Pomáha kódu, ktorý spracúva rôznorodé prijímajúce objekty. yield*a try-with-yield v ceste generátora so snímkou rámca — tieto konštrukcie sa momentálne presmerujú do stromového interpretera založeného na Fiber. Pred bezpečným vytváraním snímok si každá vyžaduje explicitné spracovanie stavového automatu (posúvanie delegovania iterátora, zachovanie zásobníka obslužných rutín počas pozastavenia).- Asynchrónne generátory a
awaitv snímkach — špecifikácia kombinuje pozastavenie generátora s frontom mikroúloh. Keď telo bajtkódu podporíyieldajawait, asynchrónne generátory budú tiež kompatibilné so snímkami. - Odvodzovanie typov parametrov v JsToPhp — parametre funkcií majú predvolene číselný typ, preto kompilátor pri
function (obj) { obj.method() }zlyhá. Odvodenie parametrov objektového typu podľa ich použitia v tele funkcie by sprístupnilo volania metód nad argumentmi funkcie bez toho, aby sa prijímajúci objekt musel vytvoriť lokálne pomocounew. - Priame generovanie opkódov opcache — preskočenie kroku
eval()transpilátora JsToPhp priamym zapisovaním opkódov vo formáte opcache. Najnáročnejšia položka; ostatné možno realizovať v rámci existujúcej architektúry.
Žiadna z týchto zmien nie je potrebná pre úlohy s vloženým enginom, na ktoré je Phasis určený.