Výkonnostní testy
Phasis je u úloh vázaných na volání zhruba o dva řády pomalejší než V8JavaScript. Tento limit je inherentní cenou interpretru hostovaného v PHP oproti nativnímu JIT kompilátoru. Pro toho, kdo jej vkládá, není otázkou „jak si vede ve srovnání s V8“ — ale „je dost rychlý pro to, co dělám“. Pro většinu úloh s vloženým interpretem je odpověď ano.
Kdy na limitu nezáleží
- Šablony — vykreslení typické stránky ve stylu SSR trvá výrazně méně než 10 ms.
- Validační pravidla — uživatelem zadané JS pro ověřování formuláře se dokončí v řádu mikrosekund.
- Transformace dat —
array.map(transform)nad stovkami položek se dokončí v řádu desítek milisekund. - Spouštěče v sandboxu — krátké skripty dodané uživatelem mají při limitu hostitele 1 sekunda dostatečnou rezervu.
Kdy na limitu záleží
- Výpočetně náročné vnitřní smyčky — zpracování obrázků, redukce velkých polí, simulace. Přesuňte výpočty do nativní knihovny PHP a zpřístupněte ji jako hostitelskou funkci.
- Animace v reálném čase — cokoli, co má běžet při 60 fps. Tato zátěž JS patří do klienta (prohlížeče nebo nativní aplikace), ne do vloženého enginu.
- Opětovné implementace algoritmů náročných na výpočty v JS — kryptografie, komprese, parsování. PHP má nativní ekvivalenty, které jsou 1000× rychlejší; použijte je.
Vhodný způsob uvažování: Phasis je propojovací vrstva pro logiku psanou v duchu JS vloženou do aplikací psaných v duchu PHP. Hostitel PHP zajišťuje výpočty; vrstva JS zajišťuje konfiguraci, větvení a drobné transformace.
Díky čemu je engine rychlý (ve srovnání s naivním stromovým interpretem)
- VM s bajtkódem — většina často prováděných volání prochází přepínačem operačních kódů v
VM::execute, nikoli AST. - Vlastní zásobník volání — každé volání JS uchovává stav uvnitř
VM::executea ukládá rámec volajícího do fondu; při každém volání JS nevzniká nový rámec PHP. Viz Virtuální stroj s bajtkódem § Vlastní zásobník volání. - Mezipaměti inline — monomorfní mezipaměť pro každé PC umožňuje při čtení metody prototypu instance (vzor
c.method()) přeskočit procházení řetězce prototypů. - Generátory se snímkem rámce — jednoduchá těla generátorů se pozastaví zachycením stavu zpracování do objektu na haldě, takže není nutné přecházet přes Fiber PHP.
- Transpiler JsToPhp — těla s převahou číselných operací a lokálních proměnných (včetně čtení
obj.method(),new Ctor()athis.member) se kompilují do uzávěrů PHP, které trasovací JIT kompilátor v PHP 8.5 převádí na nativní strojový kód. - Mezipaměť JSON.stringify —
JSON.stringify(o)ukládá výstup do vstupního objektu a transitive invalidací verzí zrychluje idiom hlubokého klonováníJSON.parse(JSON.stringify(o)).
Měření vlastního zatížení
php bench/run.php
Spustí sadu mikrovýkonnostních testů dodávanou v bench/microbench.js — několik syntetických zátěží (aritmetika ve smyčce, rekurzivní funkce, vytváření objektů, volání metod prototypu, přidávání do pole, převod JSON tam a zpět atd.) — a vypíše medián uplynulého času pro každý test. Hodí se jako základní kontrola; aktuální výsledky se při každém běhu CI testů výkonu ukládají do BENCH.md.
U vlastního kódu nahraďte skript libovolným jiným:
php -d xdebug.mode=off bench/run.php path/to/your-script.js
Pro profilování na úrovni PHP funguje profiler Xdebug s Phasis stejně jako s jakoukoli jinou aplikací PHP. Spusťte skript pod xdebug.mode=profile a prohlédněte si výstup cachegrind, abyste zjistili, které třídy Phasis\ převažují.
Plán dalšího vývoje
Co by v přibližném pořadí podle náročnosti nejvíc zmenšilo rozdíl ve výkonu V8:
- Polymorfní mezipaměti inline — umožnit jednomu místu volání
LOAD_MEMBERuchovávat 2–4 struktury před přechodem k pomalejšímu zpracování. Pomůže kódu, který zpracovává různorodé příjemce volání. yield*a try-with-yield v cestě generátorů se snímkem rámce — tyto tvary nyní přecházejí ke stromovému interpretu založenému na Fiber. Než budou bezpečné pro snímkování, každý vyžaduje výslovné zpracování stavovým automatem (předávání iterátoru delegáta, zachování zásobníku obslužných rutin při pozastavení).- Asynchronní generátory a
awaitve snímcích — specifikace kombinuje pozastavení generátoru s frontou mikroúloh. Až tělo bajtkódu podpoříyieldiawait, budou pro snímkování vhodné také asynchronní generátory. - Odvození typů parametrů v JsToPhp — parametry funkcí mají ve výchozím stavu číselný typ, takže
function (obj) { obj.method() }zastaví kompilátor. Odvozování objektových typů parametrů podle toho, jak se používají v těle funkce, by umožnilo volání metod nad argumenty funkcí bez nutnosti vytvářet příjemce lokálně pomocínew. - Přímé generování opkódů pro opcache — přeskočit krok
eval()v JsToPhp přímým zápisem opkódů ve formátu opcache. Nejnáročnější položka; ostatní lze zvládnout v rámci stávající architektury.
Nic z toho není potřeba pro integrační zátěže, na které cílí Phasis.