
# Primerjalni preizkusi

Phasis je pri obremenitvah JavaScript, omejenih s pošiljanjem, **približno dva velikostna reda počasnejši od V8**. Ta zgornja meja je neizogiben strošek tolmača, ki gostuje v PHP, v primerjavi z izvornim prevajalnikom JIT. Vprašanje za tistega, ki vgrajuje tolmač, ni »kako se primerja z V8«, temveč »ali je dovolj hiter za moje potrebe«. Za večino primerov vgradnje je odgovor pritrdilen.

## Kdaj omejitev ni pomembna

- **Predloge** — upodobitev običajne strani v slogu SSR traja precej manj kot 10 ms.
- **Pravila preverjanja** — preverjanje obrazca z uporabniško določenim JS se konča v mikrosekundah.
- **Pretvorbe podatkov** — `array.map(transform)` nad več sto elementi se zaključi v nekaj deset milisekundah.
- **Peskovniki za izvajanje** — kratki skripti, ki jih poda uporabnik, ob omejitvi gostitelja na 1 sekundo pustijo veliko časovne rezerve.

## Kdaj je omejitev pomembna

- **Računsko zahtevne notranje zanke** — obdelava slik, redukcije velikih polj, simulacije. Računsko delo prenesite v izvorno knjižnico PHP in jo izpostavite kot [funkcijo gostitelja](https://phasis.inline0.dev/sl/docs/interop/host-functions).
- **Animacija v realnem času** — vse, kar poganja 60 fps. To JS sodi v odjemalca (brskalnik, izvorna aplikacija), ne v vdelani pogon.
- **JS ponovne implementacije računsko zahtevnih algoritmov** — kriptografija, stiskanje, razčlenjevanje. PHP ima izvorne ustreznice, ki so 1000× hitrejše; uporabite jih.

Ustrezen miselni model: Phasis je **povezovalni sloj** za logiko v obliki JS, vdelano v aplikacije v obliki PHP. Gostitelj PHP skrbi za računanje, sloj JS pa za konfiguracijo, razvejanje in manjše pretvorbe.

## Zaradi česa je pogon hiter (v primerjavi z naivnim sprehajalnikom drevesa)

- **Navidezni stroj z bajtno kodo** — večina vročih dispečerskih klicev poteka prek preklopa za operacijske kode v `VM::execute`, ne prek AST-ja.
- **Po meri izdelan sklad klicev** — vsak klic JS ohrani stanje znotraj `VM::execute` in shrani okvir klicatelja v področje; za vsak klic JS ni novega okvira PHP. Glejte [Navidezni stroj z bajtno kodo § Sklad klicev po meri](https://phasis.inline0.dev/sl/docs/advanced/bytecode-vm).
- **Vgrajeni predpomnilniki** — enomodni predpomnilnik za posamezni PC omogoča, da branje metode prototipa na primerku (oblika `c.method()`) preskoči sprehod po verigi prototipov.
- **Generatorji s posnetki okvirov** — izvajanje preprostih teles generatorjev se prekine tako, da se stanje dispečiranja shrani v objekt na kopici, namesto da bi prečkalo vlakno PHP.
- **Transpiler JsToPhp** — telesa, ki intenzivno uporabljajo števila ali lokalne spremenljivke (vključno z branji `obj.method()`, `new Ctor()` in `this.member`), se prevedejo v zaprtja PHP, ki jih sledilni JIT v PHP 8.5 prevede v izvorno strojno kodo.
- **Predpomnilnik JSON.stringify** — `JSON.stringify(o)` predpomni svoj izhodni rezultat na vhodnem objektu s prehodnim razveljavljanjem različic, kar pospeši idiom globokega kloniranja `JSON.parse(JSON.stringify(o))`.

## Merjenje lastne delovne obremenitve

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

S tem zaženete zbirko mikroprimerjalnih testov, priloženo pod `bench/microbench.js` — nekaj sintetičnih delovnih obremenitev (aritmetika v zankah, rekurzivne funkcije, ustvarjanje objektov, klici metod prototipov, dodajanje v polje, povratno pretvarjanje JSON itd.) — in izpišete srednji stenski čas za vsak test. Uporabno za osnovni pregled; trenutne številke se ob vsakem izvajanju primerjalnih testov CI zapišejo v [`BENCH.md`](https://github.com/inline0/phasis/blob/main/BENCH.md).

Za svojo kodo lahko uporabite poljubni skript:

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

Za profiliranje na ravni PHP profiler iz Xdebug deluje z Phasis enako kot s katero koli drugo aplikacijo PHP. Zaženite skript z `xdebug.mode=profile` in preglejte izhod cachegrind, da ugotovite, kateri razredi `Phasis\` prevladujejo.

## Načrt razvoja

Kaj bi najbolj zmanjšalo vrzel do V8, razvrščeno približno po izvedljivosti:

- **Polimorfni vgrajeni predpomnilniki** — omogočite, da eno mesto klica `LOAD_MEMBER` shrani 2–4 predpomnjene oblike, preden preklopi na počasno dispečiranje. Pomaga pri kodi, ki obdeluje heterogene prejemnike.
- **`yield*` in try-with-yield na poti generatorjev s posnetki okvirov** — te oblike se trenutno preusmerijo na sprehajalnik dreves, podprt z vlakni. Preden bodo primerne za posnetke, je treba izrecno obravnavati stanje (posredovanje iteratorja delegata, ohranitev sklada obravnavalnikov med prekinitvijo).
- **Asinhroni generatorji in `await` na posnetkih** — specifikacija združuje prekinitve generatorjev z vrsto mikroopravil. Ko telo bajtne kode podpira oboje, `yield` in `await`, bodo tudi asinhroni generatorji primerni za posnetke.
- **Sklepanje o vrstah parametrov v JsToPhp** — parametri funkcij so privzeto številskega tipa, zato `function (obj) { obj.method() }` prekine prevajanje. Sklepanje o objektnih vrstah parametrov na podlagi njihove uporabe v telesu bi omogočilo klice metod na argumentih funkcije, ne da bi bilo treba prejemnik lokalno ustvariti z `new`.
- **Neposredno ustvarjanje opkod v opcache** — preskočite korak `eval()` v JsToPhp tako, da neposredno zapišete opkode v obliki opcache. To je največji poseg; druge izboljšave je mogoče izvesti v obstoječi arhitekturi.

Nobena od teh izboljšav ni potrebna za vdelane delovne obremenitve, ki jih podpira Phasis.
