Към съдържанието

Бенчмаркове

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.