
# Benchmarks

Phasis is bij dispatchgebonden JavaScript **grofweg twee ordes van grootte trager dan V8**. Dat plafond is de inherente prijs van een interpreter die in PHP is gehost, vergeleken met een native JIT-compiler. Voor wie de engine insluit, is de vraag niet "hoe verhoudt hij zich tot V8", maar "is hij snel genoeg voor wat ik doe?" Voor de meeste workloads waarbij de engine wordt ingesloten, is het antwoord ja.

## Wanneer het plafond er niet toe doet

- **Sjablonen** — SSR-achtige weergave van een doorsnee pagina in ruim minder dan 10 ms.
- **Validatieregels** — door gebruikers aangeleverde JS voor formuliervalidatie zijn binnen microseconden klaar.
- **Gegevenstransformaties** — `array.map(transform)` over honderden items is binnen tientallen milliseconden klaar.
- **Sandbox-runners** — korte scripts van gebruikers met een hostlimiet van 1 seconde laten ruim voldoende speelruimte.

## Wanneer het plafond er wel toe doet

- **Rekenintensieve binnenste lussen** — beeldverwerking, bewerkingen op grote arrays, simulaties. Verplaats de berekeningen naar een native PHP-bibliotheek en stel die beschikbaar als [hostfunctie](https://phasis.inline0.dev/nl/docs/interop/host-functions).
- **Realtimeanimatie** — alles wat 60 fps aanstuurt. Die JS hoort thuis in de client (browser, native app), niet in de ingesloten engine.
- **Herimplementaties van JS van algoritmen die veel berekeningen vergen** — cryptografie, compressie, parsing. PHP heeft native tegenhangers die 1000× sneller zijn; gebruik die.

Het juiste denkmodel: Phasis is een **lijmlaag** voor logica in de stijl van JS, ingebed in applicaties in de stijl van PHP. De PHP-host voert de berekeningen uit; de JS-laag verzorgt configuratie, vertakkingen en kleine transformaties.

## Waardoor de engine sneller is (dan een naïeve tree-walker)

- **Bytecode-VM** — het grootste deel van de veelgebruikte dispatch loopt via de opcode-switch van `VM::execute`, niet via de AST.
- **Aangepaste aanroepstack** — bij elke JS-aanroep blijft de status binnen `VM::execute` en wordt het frame van de aanroeper opgeslagen in een pool; er komt geen nieuw PHP-frame per JS-aanroep. Zie [Bytecode-VM § Aangepaste aanroepstack](https://phasis.inline0.dev/nl/docs/advanced/bytecode-vm).
- **Inlinecaches** — een monomorfe cache per PC zorgt ervoor dat het lezen van een prototypemethode op een instantie (de vorm `c.method()`) de prototypeketen niet hoeft te doorlopen.
- **Generators met framesnapshots** — eenvoudige generatorbodies pauzeren door de dispatchstatus vast te leggen in een heapobject, in plaats van over te gaan naar een PHP Fiber.
- **JsToPhp-transpiler** — bodies met veel numerieke bewerkingen of lokale variabelen (waaronder `obj.method()`, `new Ctor()` en `this.member`-lezingen) worden gecompileerd naar PHP-closures, die de tracing-JIT van PHP 8.5 omzet naar native machinecode.
- **JSON.stringify-cache** — `JSON.stringify(o)` slaat de uitvoer op het invoerobject op en invalideert die bij transitieve versie wijzigingen, waardoor het idioom `JSON.parse(JSON.stringify(o))` voor diep klonen sneller wordt.

## Uw eigen workload meten

```bash
php bench/run.php
```

Dit voert de microbench-suite uit die bij `bench/microbench.js` wordt geleverd — een handvol synthetische workloads (lusberekeningen, recursieve functies, objectaanmaak, aanroepen van prototypemethoden, array-push, JSON round-trip, enz.) — en drukt de mediane wandkloktijd per test af. Handig als snelle controle; de huidige cijfers worden bij elke CI-benchrun vastgelegd in [`BENCH.md`](https://github.com/inline0/phasis/blob/main/BENCH.md).

Vervang voor uw eigen code het script:

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

Voor profilering op PHP-niveau werkt de profiler van Xdebug op precies dezelfde manier met Phasis als met elke andere PHP-applicatie. Voer een script uit met `xdebug.mode=profile` en bekijk de cachegrind-uitvoer om te zien welke `Phasis\`-klassen de meeste tijd kosten.

## Routekaart

Wat de kloof met V8 zou verkleinen, ongeveer in volgorde van haalbaarheid:

- **Polymorfe inlinecaches** — laat één `LOAD_MEMBER`-aanroeppunt 2-4 vormen cachen voordat het terugvalt op trage dispatch. Helpt code die heterogene ontvangers verwerkt.
- **`yield*` en try-with-yield op het pad met generators die framesnapshots gebruiken** — deze vormen gaan nu via de Fiber-ondersteunde tree-walker. Voor snapshotondersteuning moet elke vorm expliciet als toestandsmachine worden afgehandeld (doorsturen van gedelegeerde iterators, handlerstack behouden tijdens pauzeren).
- **Async-generators en `await` op snapshots** — de specificatie combineert het pauzeren van generators met de microtaskwachtrij. Zodra de bytecodebody zowel `yield` als `await` ondersteunt, zijn async-generators ook geschikt voor snapshots.
- **Type-inferentie van parameters in JsToPhp** — funct parameters krijgen standaard het numerieke type, waardoor `function (obj) { obj.method() }` de compiler afbreekt. Objectgetypeerde parameters afleiden uit hun gebruik in de body zou methodeaanroepen op functieargumenten mogelijk maken zonder dat de ontvanger lokaal via `new` moet worden aangemaakt.
- **Directe opcache-emissie** — sla de `eval()`-stap van JsToPhp over door opcodes rechtstreeks in opcache-indeling te schrijven. Dit is het meest ingrijpende punt; de andere kunnen binnen de bestaande architectuur worden uitgevoerd.

Niets hiervan is noodzakelijk voor de workloads waarbij Phasis wordt ingesloten.
