Gå til indhold

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::execute og 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() og this.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-idiomet JSON.parse(JSON.stringify(o)) hurtigere.

Mål din egen arbejdsbelastning

Shell
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:

Shell
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 await på snapshots — specifikationen kombinerer generatorsuspendering med microtask-køen. Når bytecode-bodyen understøtter både yield og await, 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 via new.
  • 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.