Benchmarkresultater
Phasis er omtrent to størrelsesordener langsommere end V8 ved dispatch-begrænsede JavaScript. Denne grænse skyldes den iboende omkostning ved en fortolker hostet i PHP sammenlignet med en native JIT-compiler. Spørgsmålet for en indlejrer er ikke "hvordan klarer den sig mod V8" — men "er den hurtig nok til det, jeg laver." For de fleste indlejringsarbejdsbelastninger er svaret ja.
Når grænsen ikke betyder noget
- Skabelongengivelse — SSR-lignende gengivelse af en typisk side på langt under 10 ms.
- Valideringsregler — brugerleveret JS til formularvalidering afsluttes på mikrosekunder.
- Datatransformationer —
array.map(transform)over hundredvis af elementer afsluttes på titals millisekunder. - Sandbox-kørsler — korte brugerleverede scripts med en værtsgrænse på 1 sekund efterlader masser af råderum.
Når grænsen betyder noget
- Beregningsintensive indre løkker — billedbehandling, reduktioner over store arrays, simuleringer. Flyt beregningen til et native PHP-bibliotek, og eksponér det som en værtsfunktion.
- Animation i realtid — alt, der driver 60 fps. Den slags JS hører hjemme i klienten (browser, native app), ikke i den indlejrede motor.
- JS-genimplementeringer af beregningstunge algoritmer — kryptografi, komprimering, parsing. PHP har native ækvivalenter, der er 1000× hurtigere; brug dem.
Den rette mentale model: Phasis er et limlag til logik formet som JS, indlejret i applikationer formet som PHP. Værten i PHP håndterer beregning; laget i JS håndterer konfiguration, forgreninger og små transformationer.
Hvad der gør motoren hurtig (sammenlignet med en naiv træfortolker)
- Bytecode-VM — det meste hot dispatch går gennem
VM::executes opcode-switch, ikke AST'en. - Brugerdefineret kaldestak — hvert JS-kald holder tilstanden i
VM::executeog gemmer kalderens frame i en pool; ingen ny PHP-frame for hvert JS-kald. Se Bytecode VM § Brugerdefineret kaldestak. - Inline-caches — en monomorf cache pr. PC gør, at opslag af prototypemetoder på instanser (formen
c.method()) springer gennemgangen af prototypekæden over. - Generators med framesnapshot — enkle generator-bodies suspenderes ved at indfange dispatch-tilstanden i et heap-objekt i stedet for at passere gennem en PHP Fiber.
- JsToPhp-transpilerer — bodies med mange tal eller lokale variabler (herunder læsninger af
obj.method(),new Ctor()ogthis.member) kompileres til PHP-closures, som PHP 8.5 sænker til native maskinkode med sin tracing-JIT. - JSON.stringify-cache —
JSON.stringify(o)cacher sit output på inputobjektet med transitiv versionsinvalidering og gør dermed deep-clone-idiometJSON.parse(JSON.stringify(o))hurtigere.
Mål din egen arbejdsbelastning
php bench/run.php
Dette kører mikrobenchmark-pakken, der leveres under bench/microbench.js — en håndfuld syntetiske arbejdsbelastninger (løkkearitmetik, rekursive funktioner, objektoprettelse, kald af prototypemetoder, array-push, JSON tur-retur osv.) og udskriver medianvæggtid pr. test. Nyttigt som et hurtigt tjek; de aktuelle tal er gemt i BENCH.md ved hver CI-benchmarkkørsel.
For din egen kode kan du indsætte et vilkårligt script:
php -d xdebug.mode=off bench/run.php path/to/your-script.js
Til profilering på PHP-niveau fungerer Xdebugs profiler med Phasis præcis som med enhver anden PHP-applikation. Kør et script under xdebug.mode=profile, og undersøg cachegrind-outputtet for at se, hvilke Phasis\-klasser der dominerer.
Køreplan
Hvad der vil mindske forskellen til V8 mest, sorteret omtrent efter hvor let det er at gennemføre:
- Polymorfe inline-caches — lad et enkelt
LOAD_MEMBER-kaldested holde 2-4 cachede former, før det falder tilbage til langsom dispatch. Hjælper kode, der behandler heterogene modtagere. yield*og try-with-yield på generatorstien med framesnapshot — disse former går i øjeblikket til den Fiber-baserede træfortolker. Hver kræver eksplicit tilstandsmaskinehåndtering (videresendelse af delegeret iterator, handlerstak bevaret under suspendering), før de kan bruge snapshots.- Async generators og
awaitpå snapshots — specifikationen kombinerer generatorsuspendering med microtask-køen. Når bytecode-bodyen understøtter bådeyieldogawait, kan async generators også bruge snapshots. - Inferens af parametertyper i JsToPhp — funktionsparametre får som standard typen tal, så
function (obj) { obj.method() }får compileren til at give op. Udledning af parametre med objekttype ud fra deres brug i bodyen ville muliggøre metodekald på funktionsargumenter uden at kræve, at modtageren oprettes lokalt vianew. - Direkte emission af opcache — spring JsToPhp's
eval()-trin over ved at skrive opcodes direkte i opcache-format. Det mest omfattende punkt; de øvrige kan gennemføres inden for den eksisterende arkitektur.
Intet af dette er nødvendigt for de indlejringsarbejdsbelastninger, Phasis retter sig mod.