Ugrás a tartalomhoz

Teljesítménymérések

A Phasis diszpécseléskorlátos JavaScript esetén nagyjából két nagyságrenddel lassabb, mint a V8. Ez a PHP-alapú értelmező natív JIT-fordítóhoz viszonyított, eredendő költsége. A beágyazó számára nem az a kérdés, hogy „hogyan viszonyul a V8-hez”, hanem az, hogy „elég gyors-e ahhoz, amit csinálok”. A legtöbb beágyazási terhelésnél igen a válasz.

Amikor a teljesítménykorlát nem számít

  • Sablonkezelés — egy tipikus oldal SSR-stílusú megjelenítése jóval 10 ms alatt.
  • Validációs szabályok — az űrlapellenőrzéshez megadott JS mikroszekundumok alatt lefut.
  • Adatátalakítások — több száz elemen végzett array.map(transform) művelet tíz milliszekundum alatt befejeződik.
  • Sandbox-futtatók — az 1 másodperces gazdaoldali korláttal futó, rövid, felhasználó által megadott szkripteknek bőven marad tartalékidejük.

Amikor a teljesítménykorlát számít

  • Számításigényes belső ciklusok — képfeldolgozás, nagy tömbök redukciója, szimulációk. Helyezze át a számításokat egy natív PHP könyvtárba, és tegye elérhetővé egy gazdafüggvényen keresztül.
  • Valós idejű animáció — minden, ami 60 fps-sel működik. Ennek a JS-nek a kliensben (böngészőben, natív alkalmazásban) van a helye, nem a beágyazott motorban.
  • JS újraimplementációk számításigényes algoritmusokhoz — kriptográfia, tömörítés, elemzés. A PHP natív megfelelői 1000× gyorsabbak; használja azokat.

A megfelelő gondolati modell: a Phasis ragasztóréteg a PHP-alakú alkalmazásokba ágyazott, JS-alakú logikához. A PHP-gazda végzi a számításokat; a JS-réteg kezeli a konfigurációt, az elágazásokat és a kisebb átalakításokat.

Mitől gyors a motor (egy naiv fastruktúra-bejáróhoz képest)

  • Bájtkód-VM — a gyakran végrehajtott diszpécselések többsége a VM::execute opcode-váltásán fut keresztül, nem az AST-n.
  • Egyedi hívásiverem-kezelés — minden JS hívás állapotát a VM::execute-n belül tartja, a hívó keretét pedig egy készletbe menti; minden JS híváshoz nem jön létre új PHP keret. Lásd: Bájtkód-VM § Egyedi hívásiverem-kezelés.
  • Inline gyorsítótárak — a programszámlálóhelyenkénti monomorf gyorsítótár kihagyja a prototípuslánc bejárását a példányon végzett prototípusmetódus-olvasásoknál (az c.method() mintánál).
  • Keretpillanatképes generátorok — az egyszerű generátortörzsek a diszpécselés állapotát egy halomobjektumba mentve függesztik fel a végrehajtást, így nem kell átlépniük a PHP Fiberbe.
  • JsToPhp transzpiláló — a numerikus vagy sok lokális változót használó törzsek (beleértve a obj.method(), new Ctor() és this.member olvasásokat) PHP lezárásokra fordulnak, amelyeket a PHP 8.5 nyomkövető JIT-je natív gépi kóddá alakít.
  • JSON.stringify gyorsítótár — a JSON.stringify(o) az eredményét a bemeneti objektumon tárolja tranzitív verzióérvénytelenítéssel, gyorsítva az JSON.parse(JSON.stringify(o)) mélymásolási idiómát.

Saját terhelés mérése

Shell
php bench/run.php

Ez a bench/microbench.js alatt található mikromérési csomagot futtatja — néhány szintetikus terhelést (ciklusos aritmetika, rekurzív függvények, objektum-létrehozás, prototípusmetódus-hívások, tömbbővítés, JSON oda-vissza alakítás stb.), majd tesztenként kiírja a medián futásidőt. Józan ész szerinti ellenőrzésként hasznos; az aktuális számok minden CI-mérési futáskor bekerülnek a BENCH.md helyre.

A saját kód méréséhez cserélje ki tetszőleges szkriptre:

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

PHP-szintű profilozáshoz a Xdebug profilozója pontosan úgy működik a Phasis-val, mint bármely más PHP alkalmazással. Futtasson egy szkriptet a xdebug.mode=profile alatt, majd vizsgálja meg a cachegrind kimenetét, hogy lássa, mely Phasis\ osztályok dominálnak.

Ütemterv

A megvalósíthatóság durva sorrendjében ezek csökkentenék leginkább a V8 lemaradását:

  • Polimorf inline gyorsítótárak — egyetlen LOAD_MEMBER hívási hely 2–4 gyorsítótárazott alakot is kezelhetne, mielőtt lassú diszpécselésre vált. Ez a heterogén fogadókon dolgozó kódnak segít.
  • yield* és try-with-yield a keretpillanatképes generátorok útvonalán — ezek az alakok jelenleg a Fiber-alapú fastruktúra-bejáróra kerülnek. A pillanatképek támogatásához mindegyikhez explicit állapotgép-kezelés kell (a delegált iterátor továbbítása, a felfüggesztés alatt megőrzött kezelőverem).
  • Aszinkron generátorok és await pillanatképeken — a specifikáció a generátor felfüggesztését a mikrotask-sorral kombinálja. Amint a bájtkód törzse mindkét yield és await működést támogatja, az aszinkron generátorok is alkalmassá válnak a pillanatképezésre.
  • Paramétertípusok kikövetkeztetése a JsToPhp-ban — a függvényparaméterek alapértelmezés szerint numerikus típusúak, ezért a function (obj) { obj.method() } megszakítja a fordítást. Ha a paraméterek objektumtípusát a törzsön belüli használatukból következtetnénk ki, a függvényargumentumokon végzett metódushívásokhoz nem kellene a fogadót helyben, a new segítségével létrehozni.
  • Közvetlen opcache-kibocsátás — a eval() lépés kihagyásával a JsToPhp közvetlenül írná ki az opcache-formátumú opcode-okat. Ez a legnagyobb falat; a többi a meglévő architektúrán belül megvalósítható.

Ezek egyike sem szükséges azokhoz a beágyazási terhelésekhez, amelyeket a Phasis céloz.