
# Benchmark

Phasis è **circa due ordini di grandezza più lento di V8** nelle operazioni JavaScript limitate dal dispatch. Questo limite è il costo intrinseco di un interprete ospitato da PHP rispetto a un compilatore JIT nativo. Per chi integra il motore, la domanda non è «come si confronta con V8», ma «è abbastanza veloce per ciò che devo fare?». Per la maggior parte dei carichi di lavoro di integrazione, la risposta è sì.

## Quando il limite non conta

- **Templating** — il rendering SSR di una pagina tipica richiede molto meno di 10 ms.
- **Regole di convalida** — JS fornito dall'utente per la convalida dei moduli viene eseguito in microsecondi.
- **Trasformazioni dei dati** — `array.map(transform)` su centinaia di elementi si completa in decine di millisecondi.
- **Runner sandbox** — gli script brevi forniti dall'utente, con un limite host di 1 secondo, hanno ampio margine.

## Quando il limite conta

- **Cicli interni ad alto carico di calcolo** — elaborazione di immagini, riduzioni di array di grandi dimensioni, simulazioni. Spostate i calcoli in una libreria PHP nativa ed esponetela come [funzione host](https://phasis.inline0.dev/it/docs/interop/host-functions).
- **Animazione in tempo reale** — tutto ciò che deve raggiungere 60 fps. Questa JS dovrebbe risiedere nel client (browser o app nativa), non nel motore integrato.
- **Reimplementazioni JS di algoritmi onerosi dal punto di vista computazionale** — crittografia, compressione, analisi. PHP dispone di equivalenti nativi 1000× più veloci: usate quelli.

Il modello mentale corretto: Phasis è uno **strato di collegamento** per la logica in stile JS integrata in applicazioni in stile PHP. L'host PHP gestisce i calcoli; lo strato JS gestisce configurazione, diramazioni e piccole trasformazioni.

## Cosa rende veloce il motore (rispetto a un tree-walker ingenuo)

- **VM bytecode** — la maggior parte del dispatch più frequente passa per lo switch degli opcode di `VM::execute`, non per l'AST.
- **Stack di chiamate personalizzato** — ogni chiamata JS mantiene lo stato dentro `VM::execute` e salva il frame del chiamante in un pool; non viene creato un nuovo frame PHP per ogni chiamata JS. Vedi [Stack di chiamate personalizzato in VM bytecode](https://phasis.inline0.dev/it/docs/advanced/bytecode-vm).
- **Cache inline** — una cache monomorfica per PC consente alle letture di metodi dei prototipi sulle istanze (il modello `c.method()`) di saltare l'attraversamento della catena dei prototipi.
- **Generatori con snapshot del frame** — i corpi semplici dei generatori sospendono l'esecuzione acquisendo lo stato del dispatch in un oggetto heap invece di passare attraverso un PHP Fiber.
- **Transpiler JsToPhp** — i corpi con molte operazioni numeriche o variabili locali (incluse le letture di `obj.method()`, `new Ctor()` e `this.member`) vengono compilati in closure PHP che il JIT di tracciamento di PHP 8.5 trasforma in codice macchina nativo.
- **Cache JSON.stringify** — `JSON.stringify(o)` memorizza l'output sull'oggetto di input, invalidando transitivamente le versioni e accelerando l'idioma della copia profonda `JSON.parse(JSON.stringify(o))`.

## Misurare il proprio carico di lavoro

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

Questo comando esegue la suite di microbenchmark inclusa in `bench/microbench.js`: alcuni carichi sintetici (calcoli nei cicli, funzioni ricorsive, creazione di oggetti, chiamate a metodi dei prototipi, push di array, andata e ritorno JSON e altro) e stampa il tempo mediano di esecuzione per test. È utile come controllo di coerenza; i numeri attuali vengono registrati in [`BENCH.md`](https://github.com/inline0/phasis/blob/main/BENCH.md) a ogni esecuzione dei benchmark in CI.

Per il proprio codice, sostituite lo script con uno qualsiasi:

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

Per l'analisi delle prestazioni a livello PHP, il profiler di Xdebug funziona con Phasis come con qualsiasi altra applicazione PHP. Eseguite uno script sotto `xdebug.mode=profile` ed esaminate l'output di cachegrind per vedere quali classi `Phasis\` sono predominanti.

## Roadmap

Cosa ridurrebbe maggiormente il divario con V8, in ordine approssimativo di fattibilità:

- **Cache inline polimorfiche** — consentire a un singolo punto di chiamata `LOAD_MEMBER` di mantenere in cache 2-4 strutture prima di ricorrere al dispatch lento. Utile per il codice che elabora ricevitori eterogenei.
- **`yield*` e try-with-yield nel percorso dei generatori con snapshot del frame** — queste strutture passano attualmente al tree-walker basato su Fiber. Ognuna richiede una gestione esplicita della macchina a stati (inoltro dell'iteratore delegato, stack dei gestori preservato durante la sospensione) prima di poter usare gli snapshot.
- **Generatori asincroni e `await` sugli snapshot** — la specifica combina la sospensione dei generatori con la coda di microtask. Quando il corpo in bytecode supporterà sia `yield` che `await`, anche i generatori asincroni potranno usare gli snapshot.
- **Inferenza dei tipi dei parametri in JsToPhp** — i parametri delle funzioni sono impostati per impostazione predefinita sul tipo numerico, quindi `function (obj) { obj.method() }` fa interrompere la compilazione. Inferire i parametri di tipo oggetto dal loro uso nel corpo consentirebbe le chiamate ai metodi sugli argomenti delle funzioni senza richiedere che il ricevitore venga creato localmente tramite `new`.
- **Emissione diretta di opcache** — saltare il passaggio `eval()` di JsToPhp scrivendo direttamente opcode nel formato opcache. È l'attività più impegnativa; le altre sono realizzabili nell'architettura esistente.

Nessuna di queste funzionalità è necessaria per i carichi di lavoro di integrazione a cui mira Phasis.
