Бенчмаркове
Phasis е приблизително два порядъка по-бавен от V8 при натоварвания JavaScript, ограничени от извикванията. Този таван е неизбежната цена на интерпретатор, хостван в PHP, спрямо собствен JIT компилатор. Въпросът за вграждащия не е „как се сравнява с V8“, а „достатъчно бърз ли е за това, което правя“. За повечето сценарии на вграждане отговорът е да.
Кога таванът няма значение
- Шаблонизиране — рендирането на типична страница в стил SSR отнема значително по-малко от 10 ms.
- Правила за проверка — проверката на формуляри чрез предоставен от потребителя JS завършва за микросекунди.
- Преобразуване на данни —
array.map(transform)върху стотици елементи завършва за десетки милисекунди. - Изпълнители в пясъчник — кратките скриптове, предоставени от потребителя, имат достатъчен запас при лимит от 1 секунда от хоста.
Кога таванът има значение
- Интензивни изчислителни цикли — обработка на изображения, редукции на големи масиви, симулации. Преместете изчисленията в собствена библиотека PHP и я предоставете като хост функция.
- Анимация в реално време — всичко, което трябва да работи при 60 кадъра/сек. Тази JS принадлежи на клиента (браузър, собствено приложение), а не на вградения енджин.
- Повторни реализации на JS на алгоритми с висока изчислителна цена — криптография, компресия, парсване. В PHP има собствени еквиваленти, които са 1000 пъти по-бързи; използвайте тях.
Правилният начин да мислите за това: Phasis е слой за свързване на логика във формат JS, вградена в приложения във формат PHP. Хостът PHP се грижи за изчисленията, а слоят JS — за конфигурацията, разклоненията и малките преобразувания.
Какво прави енджина бърз (спрямо наивно обхождане на дърво)
- Виртуална машина за байткод — повечето горещи извиквания се изпълняват през превключвателя на опкодове на
VM::execute, а не през AST. - Персонализиран стек за извиквания — всяко извикване на JS пази състоянието в
VM::executeи записва рамката на извикващия в пул; при всяко извикване на JS не се създава нова рамка PHP. Вижте Виртуална машина за байткод § Персонализиран стек за извиквания. - Кешове за вграждане — мономорфен кеш за всяка 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)).
Измерване на собственото ви натоварване
php bench/run.php
Това изпълнява набора от микро-бенчмаркове, предоставен в bench/microbench.js — няколко синтетични натоварвания (аритметика в цикъл, рекурсивни функции, създаване на обекти, извиквания на методи от прототипа, добавяне към масив, двупосочно преобразуване JSON и др.) — и показва медианното време за изпълнение за всеки тест. Полезно е за проверка на разумността; текущите резултати се записват в BENCH.md при всяко изпълнение на CI бенчмарковете.
За собствен код просто заменете скрипта:
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.