Pereiti prie turinio

Našumo testai

Phasis yra maždaug dviem dydžio eilėmis lėtesnis už V8, kai našumą riboja JavaScript iškvietimų apdorojimas. Tokias ribas lemia pačios PHP aplinkoje veikiančio interpretatoriaus sąnaudos, palyginti su savuoju JIT kompiliatoriumi. Įterpiančiam įrankį svarbu ne „kaip tai atrodo, palyginti su V8“, o „ar to greičio pakanka mano užduotims“. Daugumoje įterpimo atvejų atsakymas – taip.

Kai šios ribos nesvarbios

  • Šablonų apdorojimas — įprasto puslapio SSR tipo atvaizdavimas gerokai greičiau nei per 10 ms.
  • Tikrinimo taisyklės — naudotojo pateikto JS formų tikrinimas baigiamas per mikrosekundes.
  • Duomenų transformacijos — array.map(transform) su šimtais elementų apdorojamas per keliasdešimt milisekundžių.
  • Smėliadėžės vykdyklės — trumpiems naudotojo pateiktiems scenarijams, kai pagrindinė aplinka riboja laiką iki 1 sekundės, lieka didelė atsarga.

Kai šios ribos svarbios

  • Skaičiavimams imlios vidinės ciklų iteracijos — vaizdų apdorojimas, didelių masyvų redukcija, simuliacijos. Skaičiavimus perkelkite į savąją PHP biblioteką ir pateikite kaip pagrindinės aplinkos funkciją.
  • Animacija realiuoju laiku — visa, kas turi veikti 60 fps dažniu. Tokia JS logika turi veikti kliento pusėje (naršyklėje ar savojoje programėlėje), o ne įterptajame variklyje.
  • JS įgyvendinti daug skaičiavimų reikalaujantys algoritmai — kriptografija, glaudinimas, analizavimas. PHP turi savųjų atitikmenų, kurie veikia 1000× greičiau; naudokite juos.

Tinkamas mąstymo modelis: Phasis yra jungiamasis sluoksnis, skirtas JS formos logikai, įterptai į PHP formos programas. PHP pagrindinė aplinka atlieka skaičiavimus, o JS sluoksnis tvarko konfigūraciją, šakojimą ir nedideles transformacijas.

Kodėl variklis greitas (palyginti su paprastu medžio vaikščiotuvu)

  • Baitkodo VM — dauguma aktyvių iškvietimų apdorojama per VM::execute operacijų kodų jungiklį, o ne AST.
  • Tinkinta iškvietimų steko realizacija — kiekvieno JS iškvietimo būsena laikoma VM::execute viduje, o iškvietusiojo kadras išsaugomas telkinyje; kiekvienam JS iškvietimui nesukuriamas naujas PHP kadras. Žr. Baitkodo VM § Tinkinta iškvietimų steko realizacija.
  • Įterptinės podėlio eilutės — kiekvienam PC monomorfinis podėlis leidžia skaitant prototipo metodą egzemplioriuje (c.method() forma) praleisti prototipų grandinės peržiūrą.
  • Kadrų momentinių kopijų generatoriai — paprastų generatorių vykdymas pristabdomas užfiksuojant iškvietimų būseną krūvos objekte, užuot perėjus per PHP Fiber.
  • JsToPhp transpiliatorius — skaitines operacijas ir vietinius kintamuosius intensyviai naudojančios funkcijos (įskaitant obj.method(), new Ctor() ir this.member skaitymus) kompiliuojamos į PHP uždarymus, kuriuos PHP 8.5 sekimo JIT kompiliatorius paverčia savuoju mašininiu kodu.
  • JSON.stringify podėlis — JSON.stringify(o) podėlį saugo įvesties objekte ir transityviai jį anuliuoja pakeitus versiją, taip paspartindamas gilaus klonavimo šabloną JSON.parse(JSON.stringify(o)).

Savo darbo krūvio matavimas

Shell
php bench/run.php

Tai paleidžia kartu su bench/microbench.js pateikiamą mikrotikrinimų rinkinį — kelis sintetinius darbo krūvius (ciklų aritmetiką, rekursines funkcijas, objektų kūrimą, prototipų metodų iškvietimus, masyvo papildymą, JSON pirmyn ir atgal ir kt.) ir pateikia kiekvieno bandymo medianinį sieninio laiko rodiklį. Naudinga greitam patikrinimui; naujausi skaičiai po kiekvieno CI našumo bandymo įrašomi į BENCH.md.

Savo kodui vietoj jo naudokite bet kurį scenarijų:

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

Norint profiliuoti PHP lygmeniu, Xdebug profiliavimo įrankis veikia su Phasis kaip ir su bet kuria kita PHP programa. Paleiskite scenarijų per xdebug.mode=profile ir peržiūrėkite cachegrind išvestį, kad pamatytumėte, kurios Phasis\ klasės dominuoja.

Kūrimo planas

Kas labiausiai sumažintų V8 atsilikimą, apytikriai pagal įgyvendinimo paprastumą:

  • Polimorfiniai įterptiniai podėliai — leisti vienai LOAD_MEMBER iškvietimo vietai išsaugoti 2–4 podėlio formas prieš pereinant prie lėto iškvietimų apdorojimo. Padeda kodui, apdorojančiam skirtingų tipų gavėjus.
  • yield* ir try-with-yield kadrų momentinių kopijų generatorių kelyje — šios formos šiuo metu nukreipiamos į Fiber pagrįstą medžio vaikščiotuvą. Prieš jas galima užfiksuoti kaip momentines kopijas, reikia aiškiai apdoroti būsenų mašiną (deleguotų iteratorių persiuntimą, per sustabdymą išsaugomą apdorojimo steką).
  • Asinchroniniai generatoriai ir await momentinėse kopijose — specifikacija susieja generatoriaus sustabdymą su mikroužduočių eile. Kai baitkodo turinys palaikys ir yield, ir await, asinchroniniai generatoriai taip pat bus pritaikyti momentinėms kopijoms.
  • JsToPhp parametrų tipų išvedimas — funkcijų parametrams pagal numatytuosius nustatymus priskiriamas skaitinis tipas, todėl function (obj) { obj.method() } sustabdo kompiliatorių. Išvedus objektų parametrų tipus pagal tai, kaip jie naudojami funkcijos turinyje, būtų galima kviesti metodus funkcijos argumentams nereikalaujant, kad gavėjas būtų sukurtas vietoje per new.
  • Tiesioginis opcache generavimas — praleisti JsToPhp eval() žingsnį ir tiesiogiai rašyti opcache formato operacijų kodus. Sudėtingiausias punktas; kitus galima įgyvendinti esamoje architektūroje.

Nė vienas iš šių dalykų nėra būtinas darbo krūviams, kuriems įterpti skirtas Phasis.