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::executeopcode-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()ésthis.memberolvasá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 azJSON.parse(JSON.stringify(o))mélymásolási idiómát.
Saját terhelés mérése
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:
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_MEMBERhí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
awaitpillanatképeken — a specifikáció a generátor felfüggesztését a mikrotask-sorral kombinálja. Amint a bájtkód törzse mindkétyieldésawaitmű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, anewsegí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.