HHVM
2014. 03. 30.
A HHVM a Facebook alternatív PHP interpretere, ami azzal az ígérettel érkezik, hogy jelentősen gyorsabb a klasszikusnál. A héten tartottam erről előadást a budapest PHP meetupon, alant ennek összefoglalója jön.
Történeti összefoglaló
Az első változat, akkor még HPHP néven, 2008-ban jelent meg. Ez egy php -> c++ fordító volt, közel a teljes 5.2-es nyelvet ismerte, a két főbb kivétel az eval
és a create_function
. A saját szervereiken eddigre már ezt használtál élesben, drámai sebességnövekedést tapasztalva. Komoly hátránya volt az előállt, esetenként többgigás binárist rövid idő alatt kiszórni a szerverekre.
A HPHPc (compiler) nem volt alkalmas hibakeresésre, ennek megoldására jött létre a HPHPd (debugger) változat, ebben lehetett watch-okat és breakpointokat beállítani, mint ma egy modern IDE + xdebug kombinációban lehetséges.
2010-ben kezdődött a munka a következő változaton, ez már HHVM néven futott. Ahogy a neve is mondja, ez egy virtuális gép, ami a PHP kódot egy köztes bytekódra fordítja, majd ebből just-in-time készít natív x64 kódot, amit futtat végül. Hatalmas előnye a HPHPc-hez képest, hogy itt nincs hatalmas bináris, amit terjeszteni kéne; viszont a teljesítménybeli javulás nagyrészt megmaradt. Ráadásul az időigényes fordítást sem kell kivárni. A HHVM ismeri közel a teljes 5.4-es nyelvet, beleértve az eval
és create_function
függvényeket.
2013-ban a HPHPc deprecated lett. (Van erre értelmes magyar kifejezés?)
2014-ben bemutatták a Hack nyelvet, ami egy erősen bővített PHP, erről később írok bővebben.
Telepítés
A HHVM hivatalosan csak linuxon támogatott. OSX-re és FreeBSD-re vannak nemhivatalos fordítási leírások.
A github wikijén számos disztróra találunk forgatási útmutatót, mindenféle verzióra. Debianra (tehát Mintre és Ubuntura is) valamint Centosra vannak előre elkészített csomagok. Az én laptopomon ennyi volt az egész:
wget http://dl.hhvm.com/conf/hhvm.gpg.key
sudo apt-key add hhvm.gpg.key
echo deb http://dl.hhvm.com/ubuntu saucy main |
sudo tee /etc/apt/sources.list.d/hhvm.list
sudo apt-get update
sudo apt-get install hhvm
Az utolsó parancs végeztével rögtön elérhető a hhvm
parancs:
$ hhvm -–version
HipHop VM 2.4.2 (rel)
Compiler: tags/HHVM-2.4.2-0-g432ecffa04b21c60953bb236a9db8278f4650537
Repo schema: 1be260b29a71097b5d1f78c6e4dcbb981ba03bde
$ hhvm -m d
Welcome to HipHop Debugger!
Type "help" or "?" for a complete list of commands.
hphpd> phpinfo()
HipHop
Aki a saját rendszerén nem szeretne turkálni, vagy nem támogatott oprendszert használ, létezik vagrant box, ami egy ubuntu guestre forrásból forgatja a legfrissebb HHVM-et: javier/hhvm-vagrant-vm
Aki pedig azon gondolkodik, hol tudja ezt saját szerver nélkül hostolni: van már Heroku buildpack , ami push után HHVM-el szolgál ki.
Extensionök
A legfontosabb core extensionök out-of-the-box jönnek a HHVM-el beépítve, és várhatóak újak. [Itt a teljes lista][extensions], a szerintem legfontosabbak pedig:
- APC
- curl
- DOM
- hash
- GD, ImageMagick
- Intl (de ebből még hiányzik a
NumberFormatter
) - JSON
- Memcache(d)
- MySQL
- PDO
- XML
- XSL
És persze adott a lehetőség sajátok írására.
Konfiguráció
Az előadásban erről túl sok szót nem ejtettem, és okkal: annyi beállítási lehetőség van, hogy azok megérnének több saját előadást. Itt is csak a szerintem legfontosabbakról írok. A teljes és részletes leírást Runtime options címen githubon találjátok.
Log {
NoSilencer = false
AlwaysLogUnhandledExceptions = true
InjectedStackTrace = true
NativeStackTrace = true
}
A NoSilencer
kapcsolóval lehet logoltatni a @
operátorral elnyomott hibákat is. Az AlwaysLogUnhandledExceptions
akkor is logolja a fatal errorokat, ha arra van user-defined error handler. Az utolsó kettő pedig a logba írt stack tracke-ek formátumát szabályozza, illetve hogy a php vagy a c++ kódé legyen-e.
Eval.Debugger {
EnableDebugger = true
EnableDebuggerServer = true
Port = 8089
DefaultSandboxPath = path to source files
}
Alapból a debugger (hphpd) nem figyel, így lehet bekapcsolni. Indítás után a logból is látszik, hogy várja a kapcsolatokat.
HHVM mint FCGI
Amikor az előadást tartottam akkor még lehett önálló szerverként futtatni a HHVM-et, nem kellett elé külön http szerver. Ezzel járt az, hogy rengeteg konfigurációs paraméter az egyéb file-okkal foglalkozott, és nem a php értelmezővel. 2013 decemberében bemutatták az új, FastCGI felületét, amivel ezt a problémát megoldották, és a HHVM már tényleg csak a php kód kiszolgálásáért volt felelős.
Időközben kijött a 3.0-ás verzió, amiben ez a működési mód már nem létezik. Jóideje ez volt az ajánlás, és production/staging környezetben valószínűleg nem okoz meglepetést, debug során viszont olyan hasznos volt, mint a php beépített szervere.
A FCGI mód bekapcsolásához ezt kell beleírnunk a konfigunk Server blokkjába:
Server {
// ...
Type = fastcgi
Post = 9000
// vagy
FileSocket = /var/run/hhvm.sock
}
Nginx példa:
root /var/www;
fastcgi_pass unix:/var/run/hhvm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME$document_root$fastcgi_script_name;
include fastcgi_params;
És hogy milyen teljesítménybeli javulást hoz ez? A gyári tesztek szerint egy stock wordpress telepítéssel ennyit (ab
-val tesztelve):
PHP-FPM
Requests per second: 23.17 [#/sec] (mean)
Time per request: 2157.579 [ms] (mean)
Time per request: 43.152 [ms] (mean, across all concurrent requests)
Transfer rate: 275.42 [Kbytes/sec] received
HHVM + FastCGI
Requests per second: 184.71 [#/sec] (mean)
Time per request: 270.689 [ms] (mean)
Time per request: 5.414 [ms] (mean, across all concurrent requests)
Transfer rate: 2194.38 [Kbytes/sec] received
Már itt hatalmas a javulás, de aztán amikor rendesen beindul a JIT, még látványosabb:
Requests per second: 949.21 [#/sec] (mean)
Time per request: 52.676 [ms] (mean)
Time per request: 1.054 [ms] (mean, across all concurrent requests)
Transfer rate: 11276.46 [Kbytes/sec] received
Bár teljesen mesterséges teszt, a Fibonacci-számolás szépen kihozza az interpretált és a natív (JIT-fordított) kód teljesítménye közti különbséget:
PHP-FPM
Requests per second: 13789.24 [#/sec] (mean) Fib(5)
Requests per second: 3202.31 [#/sec] (mean) Fib(15)
Requests per second: 118.94 [#/sec] (mean) Fib(25)
Requests per second: 8.40 [#/sec] (mean) Fib(30)
HHVM + FastCGI
Requests per second: 8842.70 [#/sec] (mean) Fib(5)
Requests per second: 8892.66 [#/sec] (mean) Fib(15)
Requests per second: 5581.37 [#/sec] (mean) Fib(25)
Requests per second: 737.56 [#/sec] (mean) Fib(30)
Hack
Kicsit több, mint egy hete jelentették be a HHVM-re épülő Hack nyelvet. Ez egy jelentősen kibővített PHP, ezekkel a fontos újításokkal:
Skaláris típusok
Objektumokra, interfészekre, tömbökre most is lehet typehintelni, de skalárokra nem. A Hack ezt lehetővé teszi osztályváltozókra, konstansokra, függvényparaméterekre és -visszatérési értékekre:
<?hh
class MyClass {
const int MyConst = 0;
private string $x = '';
public function increment(int $x): int {
$y = $x + 1;
return $y;
}
}
Vegyük még észre, hogy a megszokott <?php
helyett <?hh
a nyitótag.
Genericek
Részben a típusosságból következik, hogy a C++ template-es osztályaival analóg módon tudunk általánosan, típusfüggetlenül saját osztályokat leírni, aztán JIT-fordítás időben létrejön belőle a konkrét típushoz való változat:
<?hh
class Box<T> {
protected T $data;
public function __construct(T $data) {
$this->data = $data;
}
public function getData(): T {
return $this->data;
}
}
Collection-ök
A php tömbjei nagyon sokoldalúak, de pont emiatt bizonyos célokra nem ideálisak. A Hack behozta a C++ STL-jéből ismert Vector
, Map
, Set
és Pair
osztályokat, melyek egy része bár elérhető SPL-ből, ezek az új változatok mégis jóval többet tudnak - a típuskényszerítésen kívül is. Részletes bemutatás a HHVM dokumentációjában.
Új lamda syntax
Ez csak syntax sugar, de jóval egyszerűbben lehet névtelen függvényeket definiálni, mint php-ban:
<?hh
function foo(): (function(string): string) {
$x = 'bar';
return $y ==> $x . $y;
}
function test(): void {
$fn = foo();
echo $fn('baz'); // barbaz
}
Shape-ek
PHP-ban nincs struct típus. Ennek megoldására jön a shape, ami fix kulcsos tömböket jelent - ha valamely hiányzik vagy plusz van, akkor az hiba:
<?hh
type my_shape = shape(
"field1" => int,
"field2" => bool,
);
function first_shape(): my_shape {
return shape("field1" => 42, "field2" => false);
}
Async függvényhívások
Ezt még nem sikerült teljesen felfognom. A lényege annyi, hogy aszinkron függvényhívást tesz lehetővé, amely a futása után meghív egy Callbacket. A node.js-re hasonlít, de további kísérletezést igényel. Részletes leírás.
A Hackkel további ismerkedéshez ajánlom az interaktív tutorialjukat.
Mire jó ez ma?
Célként tűzték ki, hogy minnél több frameworkkel legyenek kompatibilisek, az aktuális állás megtekinthető itt: frameworks. Eszerint például laravelt vagy codeignitert ma már nyugodtan futtathatunk rajta, nem fogunk hibába futni. Van azonban valami, ami a mindennapjainkat nagyban javíthatja:
Composer HHVM-el
$ alias hhvm-composer="hhvm -v ResourceLimit.SocketDefaultTimeout=30
-v Http.SlowQueryTreshold=30000
-v Eval.Jit=false /usr/local/bin/composer`
Az intl-függő csomagokkal probléma lehet, de ezen túllépve négy-ötszörös sebességnövekedést tapasztaltam. A beállítások a lassú hálózaból fakadó problémák elkerülésére szolgálnak, your mileage may vary.
PHPUnit HHVM-el
$ composer global require "phpunit/phpunit=4.0.*"
$ alias hhvm-phpunit="hhvm -v Eval.Jit=false ~/.composer/vendor/bin/phpunit"
Nagyon csábító dolog, de valamire nagyon figyelni kell: ekkor a HHVM stack ellen teszteljük a kódunkat.