
# Benchmarks

Phasis é **cerca de duas ordens de grandeza mais lento que V8** em JavaScript limitados pela distribuição de chamadas. Esse teto é o custo inerente de um interpretador executado em PHP em comparação com um compilador JIT nativo. Para quem incorpora o mecanismo, a pergunta não é "como ele se compara a V8" — e sim "ele é rápido o bastante para o que estou fazendo?" Para a maioria das cargas de trabalho de incorporação, a resposta é sim.

## Quando o teto não importa

- **Criação de templates** — a renderização SSR de uma página típica leva bem menos de 10 ms.
- **Regras de validação** — JS fornecidos pelo usuário para validação de formulários são processados em microssegundos.
- **Transformações de dados** — `array.map(transform)` em centenas de itens são concluídas em dezenas de milissegundos.
- **Executores em sandbox** — scripts curtos fornecidos pelo usuário, com um limite de 1 segundo imposto pelo host, ainda têm bastante margem.

## Quando o teto importa

- **Loops internos com cálculos intensivos** — processamento de imagens, reduções de arrays grandes, simulações. Leve os cálculos para uma biblioteca nativa PHP e exponha-a como uma [função do host](https://phasis.inline0.dev/pt/docs/interop/host-functions).
- **Animação em tempo real** — qualquer coisa que precise manter 60 fps. Esse JS deve ficar no cliente (navegador, aplicativo nativo), não no mecanismo incorporado.
- **Reimplementações em JS de algoritmos com alto custo computacional** — criptografia, compactação, análise sintática. PHP oferece equivalentes nativos 1000× mais rápidos; use-os.

O modelo mental adequado: Phasis é uma **camada de integração** para lógica no estilo de JS incorporada a aplicações no estilo de PHP. O host PHP cuida dos cálculos; a camada JS cuida da configuração, dos desvios condicionais e das pequenas transformações.

## O que torna o mecanismo rápido (em comparação com um interpretador de árvore ingênuo)

- **VM de bytecode** — a maior parte das chamadas mais frequentes passa pelo switch de opcodes de `VM::execute`, não pela AST.
- **Pilha de chamadas personalizada** — cada chamada JS mantém o estado dentro de `VM::execute` e salva o quadro do chamador em um pool; não cria um novo quadro PHP a cada chamada JS. Consulte [VM de bytecode § Pilha de chamadas personalizada](https://phasis.inline0.dev/pt/docs/advanced/bytecode-vm).
- **Caches inline** — um cache monomórfico por PC faz com que leituras de métodos de protótipo em instâncias (o padrão `c.method()`) ignorem a busca na cadeia de protótipos.
- **Geradores com instantâneo do quadro** — corpos simples de geradores suspendem a execução capturando o estado de distribuição em um objeto na heap, em vez de atravessar uma Fiber PHP.
- **Transpiler JsToPhp** — corpos com muitos cálculos numéricos ou uso intenso de variáveis locais (incluindo leituras de `obj.method()`, `new Ctor()` e `this.member`) são compilados para closures PHP, que o JIT de rastreamento de PHP 8.5 compila para código de máquina nativo.
- **Cache JSON.stringify** — `JSON.stringify(o)` armazena o resultado no objeto de entrada, com invalidação transitiva por versão, acelerando o padrão de clonagem profunda `JSON.parse(JSON.stringify(o))`.

## Medir suas próprias cargas de trabalho

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

Isso executa o conjunto de microbenchmarks incluído em `bench/microbench.js` — um punhado de cargas de trabalho sintéticas (aritmética em loops, funções recursivas, criação de objetos, chamadas a métodos de protótipo, inclusão em arrays, ida e volta de JSON etc.) — e exibe o tempo de execução mediano de cada teste. É útil como verificação básica; os números atuais são registrados em [`BENCH.md`](https://github.com/inline0/phasis/blob/main/BENCH.md) a cada execução de benchmark na CI.

Para executar o seu próprio código, substitua por qualquer script:

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

Para criar perfis no nível de PHP, o profiler de Xdebug funciona exatamente com Phasis como com qualquer outra aplicação PHP. Execute um script com `xdebug.mode=profile` e examine a saída do cachegrind para ver quais classes de `Phasis\` dominam.

## Roteiro de desenvolvimento

O que reduziria ainda mais a diferença de V8, em ordem aproximada de facilidade de implementação:

- **Caches inline polimórficos** — permitir que um único ponto de chamada `LOAD_MEMBER` mantenha de 2 a 4 formas em cache antes de recorrer à distribuição lenta. Ajuda o código que processa receptores heterogêneos.
- **`yield*` e try-with-yield no caminho de geradores com instantâneo do quadro** — essas estruturas atualmente usam o interpretador de árvore baseado em Fiber. Cada uma exige tratamento explícito por máquina de estados (encaminhamento do iterador delegado, pilha de handlers preservada durante a suspensão) antes de poder usar instantâneos com segurança.
- **Geradores assíncronos e `await` em instantâneos** — a especificação combina a suspensão de geradores com a fila de microtarefas. Quando o corpo de bytecode der suporte tanto a `yield` quanto a `await`, os geradores assíncronos também poderão usar instantâneos.
- **Inferência de tipos de parâmetros em JsToPhp** — os parâmetros de função usam o tipo numérico por padrão, então `function (obj) { obj.method() }` faz o compilador desistir. Inferir parâmetros como objetos com base no uso deles no corpo permitiria chamadas a métodos em argumentos de função, sem exigir que o receptor fosse construído localmente com `new`.
- **Geração direta de opcodes para opcache** — ignorar a etapa `eval()` de JsToPhp e gerar os opcodes diretamente no formato do opcache. É o item mais trabalhoso; os demais podem ser implementados na arquitetura atual.

Nada disso é necessário para as cargas de trabalho de incorporação que Phasis visa.
