
# Бенчмаркове

Phasis е **приблизително два порядъка по-бавен от V8** при натоварвания JavaScript, ограничени от извикванията. Този таван е неизбежната цена на интерпретатор, хостван в PHP, спрямо собствен JIT компилатор. Въпросът за вграждащия не е „как се сравнява с V8“, а „достатъчно бърз ли е за това, което правя“. За повечето сценарии на вграждане отговорът е да.

## Кога таванът няма значение

- **Шаблонизиране** — рендирането на типична страница в стил SSR отнема значително по-малко от 10 ms.
- **Правила за проверка** — проверката на формуляри чрез предоставен от потребителя JS завършва за микросекунди.
- **Преобразуване на данни** — `array.map(transform)` върху стотици елементи завършва за десетки милисекунди.
- **Изпълнители в пясъчник** — кратките скриптове, предоставени от потребителя, имат достатъчен запас при лимит от 1 секунда от хоста.

## Кога таванът има значение

- **Интензивни изчислителни цикли** — обработка на изображения, редукции на големи масиви, симулации. Преместете изчисленията в собствена библиотека PHP и я предоставете като [хост функция](https://phasis.inline0.dev/bg/docs/interop/host-functions).
- **Анимация в реално време** — всичко, което трябва да работи при 60 кадъра/сек. Тази JS принадлежи на клиента (браузър, собствено приложение), а не на вградения енджин.
- **Повторни реализации на JS на алгоритми с висока изчислителна цена** — криптография, компресия, парсване. В PHP има собствени еквиваленти, които са 1000 пъти по-бързи; използвайте тях.

Правилният начин да мислите за това: Phasis е **слой за свързване** на логика във формат JS, вградена в приложения във формат PHP. Хостът PHP се грижи за изчисленията, а слоят JS — за конфигурацията, разклоненията и малките преобразувания.

## Какво прави енджина бърз (спрямо наивно обхождане на дърво)

- **Виртуална машина за байткод** — повечето горещи извиквания се изпълняват през превключвателя на опкодове на `VM::execute`, а не през AST.
- **Персонализиран стек за извиквания** — всяко извикване на JS пази състоянието в `VM::execute` и записва рамката на извикващия в пул; при всяко извикване на JS не се създава нова рамка PHP. Вижте [Виртуална машина за байткод § Персонализиран стек за извиквания](https://phasis.inline0.dev/bg/docs/advanced/bytecode-vm).
- **Кешове за вграждане** — мономорфен кеш за всяка PC позиция кара четенията на метод от прототипа върху екземпляр (шаблонът `c.method()`) да пропускат обхождането на веригата от прототипи.
- **Генератори със снимка на рамката** — телата на простите генератори се преустановяват, като записват състоянието на изпълнение в обект в купа памет, вместо да преминават през PHP Fiber.
- **Транспилатор JsToPhp** — тела с много числови операции или локални променливи (включително четенията на `obj.method()`, `new Ctor()` и `this.member`) се компилират до затваряния PHP, които JIT компилаторът с проследяване в PHP 8.5 преобразува до собствен машинен код.
- **Кеш JSON.stringify** — `JSON.stringify(o)` кешира резултата си във входния обект с транзитивно обезсилване по версия, което ускорява идиома за дълбоко клониране `JSON.parse(JSON.stringify(o))`.

## Измерване на собственото ви натоварване

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

Това изпълнява набора от микро-бенчмаркове, предоставен в `bench/microbench.js` — няколко синтетични натоварвания (аритметика в цикъл, рекурсивни функции, създаване на обекти, извиквания на методи от прототипа, добавяне към масив, двупосочно преобразуване JSON и др.) — и показва медианното време за изпълнение за всеки тест. Полезно е за проверка на разумността; текущите резултати се записват в [`BENCH.md`](https://github.com/inline0/phasis/blob/main/BENCH.md) при всяко изпълнение на CI бенчмарковете.

За собствен код просто заменете скрипта:

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

За профилиране на ниво PHP профайлърът на Xdebug работи с Phasis точно както с всяко друго приложение PHP. Изпълнете скрипт под `xdebug.mode=profile` и прегледайте изхода от cachegrind, за да видите кои класове `Phasis\` доминират.

## Пътна карта

Какво би намалило разликата спрямо V8, подредено приблизително по леснота на изпълнение:

- **Полиморфни кешове за вграждане** — позволяват на една точка на извикване `LOAD_MEMBER` да пази 2–4 кеширани форми, преди да премине към бавно изпълнение. Помага при код, който обработва различни типове получатели.
- **`yield*` и try-with-yield при генераторите със снимка на рамката** — тези шаблони понастоящем преминават към обхождащото дърво с Fiber. За безопасно заснемане са нужни изрична обработка с автомат на състоянията (препращане на итератора делегат, запазване на стека на обработчиците при преустановяване).
- **Асинхронни генератори и `await` в снимките** — спецификацията съчетава преустановяването на генератор с опашката за микрозадачи. Когато тялото на байткода поддържа и `yield`, и `await`, асинхронните генератори също ще могат да използват снимки.
- **Извеждане на типовете на параметрите в JsToPhp** — параметрите на функциите по подразбиране получават числов тип, затова `function (obj) { obj.method() }` прекратява компилацията. Извеждането на параметри с обектен тип според употребата им в тялото би позволило извиквания на методи върху аргументите на функцията, без получателят да трябва да е създаден локално чрез `new`.
- **Директно генериране на opcache** — пропускане на стъпката `eval()` на JsToPhp чрез директно записване на опкодовете във формат opcache. Това е най-мащабната задача; останалите могат да се изпълнят в рамките на съществуващата архитектура.

Нищо от това не е необходимо за натоварванията при вграждане, за които е предназначен Phasis.
