
# Pruebas de rendimiento

Phasis es **aproximadamente dos órdenes de magnitud más lento que V8** en JavaScript limitadas por el despacho. Ese límite es el coste inherente de un intérprete alojado en PHP frente a un compilador JIT nativo. La pregunta para quien lo integra no es «¿cómo se compara con V8?», sino «¿es lo bastante rápido para lo que necesito?». Para la mayoría de las cargas de trabajo de integración, sí.

## Cuándo no importa el límite

- **Plantillas**: renderizado de una página típica al estilo SSR en bastante menos de 10 ms.
- **Reglas de validación**: JS proporcionado por el usuario para validar formularios se ejecuta en microsegundos.
- **Transformaciones de datos**: `array.map(transform)` sobre cientos de elementos se completa en decenas de milisegundos.
- **Ejecutores en entornos aislados**: los scripts breves del usuario con un límite de 1 segundo en el host dejan un amplio margen.

## Cuándo importa el límite

- **Bucles internos con mucho cálculo**: procesamiento de imágenes, reducciones de matrices grandes, simulaciones. Traslade el cálculo a una biblioteca nativa PHP y expóngala como [función del host](https://phasis.inline0.dev/es/docs/interop/host-functions).
- **Animación en tiempo real**: cualquier tarea que genere 60 fps. Ese JS debe ejecutarse en el cliente (navegador o aplicación nativa), no en el motor integrado.
- **Reimplementaciones de JS de algoritmos con alto coste de cálculo**: criptografía, compresión y análisis sintáctico. PHP cuenta con equivalentes nativos 1000× más rápidos; utilícelos.

El modelo mental adecuado: Phasis es una **capa de enlace** para lógica con forma de JS integrada en aplicaciones con forma de PHP. El host PHP se encarga de los cálculos; la capa JS gestiona la configuración, las ramificaciones y las transformaciones pequeñas.

## Qué hace rápido al motor (frente a un recorrido de árbol ingenuo)

- **Máquina virtual de bytecode**: la mayor parte del despacho intensivo pasa por la instrucción switch de `VM::execute`, no por el AST.
- **Pila de llamadas personalizada**: cada llamada JS mantiene el estado en `VM::execute` y guarda el marco del llamador en un grupo; no se crea un marco PHP nuevo por cada llamada JS. Consulte [Máquina virtual de bytecode § Pila de llamadas personalizada](https://phasis.inline0.dev/es/docs/advanced/bytecode-vm).
- **Cachés en línea**: una caché monomórfica por PC hace que las lecturas de métodos de prototipo en instancias (el patrón `c.method()`) omitan el recorrido de la cadena de prototipos.
- **Generadores con instantánea de marco**: los cuerpos sencillos de generador se suspenden capturando el estado del despacho en un objeto del montón, en lugar de pasar por un Fiber PHP.
- **Transpilador JsToPhp**: los cuerpos con muchas operaciones numéricas o variables locales (incluidas las lecturas de `obj.method()`, `new Ctor()` y `this.member`) se compilan a cierres PHP que el JIT de trazado de PHP 8.5 convierte en código máquina nativo.
- **Caché JSON.stringify**: `JSON.stringify(o)` almacena el resultado en el objeto de entrada con invalidación transitiva de versiones, lo que acelera el modismo de clonación profunda `JSON.parse(JSON.stringify(o))`.

## Medir su propia carga de trabajo

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

Esto ejecuta el conjunto de micropruebas incluido en `bench/microbench.js`: unas cuantas cargas de trabajo sintéticas (aritmética en bucles, funciones recursivas, creación de objetos, llamadas a métodos de prototipo, inserción en matrices, ida y vuelta de JSON, etc.) e imprime el tiempo de pared mediano de cada prueba. Sirve como comprobación rápida; las cifras actuales se guardan en [`BENCH.md`](https://github.com/inline0/phasis/blob/main/BENCH.md) en cada ejecución de pruebas de rendimiento de CI.

Para probar su propio código, sustituya cualquier script:

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

Para crear perfiles a nivel de PHP, el perfilador de Xdebug funciona con Phasis igual que con cualquier otra aplicación PHP. Ejecute un script en `xdebug.mode=profile` y examine el resultado de cachegrind para ver qué clases `Phasis\` predominan.

## Hoja de ruta

Qué permitiría reducir más la diferencia con V8, en orden aproximado de facilidad de implementación:

- **Cachés polimórficas en línea** — permiten que un único punto de llamada `LOAD_MEMBER` mantenga entre 2 y 4 formas en caché antes de recurrir al despacho lento. Ayudan al código que procesa receptores heterogéneos.
- **`yield*` y try-with-yield en la ruta del generador con instantáneas de marcos** — estas formas actualmente se dirigen al recorrido del árbol basado en Fiber. Cada una necesita un manejo explícito de la máquina de estados (reenvío del iterador delegado, pila de manejadores conservada durante la suspensión) antes de ser segura para instantáneas.
- **Generadores asíncronos y `await` en instantáneas** — la especificación combina la suspensión del generador con la cola de microtareas. Cuando el cuerpo de bytecode admita tanto `yield` como `await`, los generadores asíncronos también serán compatibles con las instantáneas.
- **Inferencia de tipos de parámetros en JsToPhp** — los parámetros de función tienen por defecto un tipo numérico, por lo que `function (obj) { obj.method() }` hace que el compilador abandone. Inferir parámetros de tipo objeto según cómo se usen en el cuerpo permitiría llamar métodos en argumentos de función sin tener que construir localmente el receptor mediante `new`.
- **Emisión directa de opcache** — omitir el paso `eval()` de JsToPhp escribiendo directamente opcodes en formato opcache. Es el elemento más complejo; los demás pueden implementarse con la arquitectura existente.

Nada de esto es necesario para las cargas de trabajo de integración que Phasis tiene como objetivo.
