
# Тестування продуктивності

Phasis приблизно у **сто разів повільніший за V8** під час обробки JavaScript, обмеженої диспетчеризацією. Така межа зумовлена самою природою інтерпретатора на базі PHP порівняно з нативним JIT-компілятором. Розробнику, який вбудовує рушій, важливо не те, «як він порівнюється з V8», а те, «чи достатньо він швидкий для моїх завдань». Для більшості сценаріїв вбудовування відповідь — так.

## Коли обмеження не має значення

- **Шаблонізація** — рендеринг типової сторінки у стилі SSR значно швидше ніж за 10 ms.
- **Правила перевірки** — перевірка форм за допомогою наданого користувачем JS завершується за мікросекунди.
- **Перетворення даних** — `array.map(transform)` для сотень елементів завершується за десятки мілісекунд.
- **Середовища запуску ізольованого коду** — для коротких скриптів користувача з обмеженням у 1 second на боці хоста запасу продуктивності більш ніж достатньо.

## Коли обмеження має значення

- **Обчислювально інтенсивні внутрішні цикли** — обробка зображень, редукція великих масивів, симуляції. Перенесіть обчислення до нативної бібліотеки PHP і відкрийте її як [функцію хоста](https://phasis.inline0.dev/uk/docs/interop/host-functions).
- **Анімація в реальному часі** — усе, що має працювати з частотою 60 fps. Таку JS слід виконувати на клієнті (у браузері чи нативному застосунку), а не у вбудованому рушії.
- **Повторні реалізації JS обчислювально дорогих алгоритмів** — криптографії, стиснення, синтаксичного аналізу. У PHP є нативні аналоги, які у 1000× швидші; використовуйте їх.

Правильна модель: Phasis — це **рівень зв’язування** для логіки у стилі JS, вбудованої в застосунки у стилі PHP. Обчисленнями займається хост PHP, а шар JS відповідає за конфігурацію, розгалуження та невеликі перетворення.

## Що робить рушій швидким (порівняно з наївним інтерпретатором дерева)

- **Віртуальна машина байткоду** — більшість гарячих операцій диспетчеризації проходить через перемикач кодів операцій `VM::execute`, а не через AST.
- **Власний стек викликів** — кожен виклик JS зберігає стан у `VM::execute` і кладе кадр виклику до пулу; для кожного виклику JS не створюється новий кадр PHP. Докладніше див. [Віртуальна машина байткоду § Власний стек викликів](https://phasis.inline0.dev/uk/docs/advanced/bytecode-vm).
- **Вбудовані кеші** — мономорфний кеш для кожного PC дає змогу читанню методу прототипу в екземплярі (форма `c.method()`) оминати обхід ланцюжка прототипів.
- **Генератори зі знімком кадру** — прості тіла генераторів призупиняються, зберігаючи стан диспетчеризації в об’єкті купи, замість переходу до Fiber PHP.
- **Транспілятор 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.
