PHP - Cache kompilátoru

Velkou výhodou skriptovacího jazyka Python je ukládání mezikódu jednou zparsovaných skriptů na disk, což velmi zkracuje čas potřebný pro znovuspuštění aplikace. Obdobnou funcionalitu můžeme zprovoznit i pro PHP, stačí k tomu nainstalovat patřičné rozšíření.

Minislovníček

  • PHP interpret - Aplikace umožňující spuštění programu přímo ze zdrojového kódu bez potřeby zdrojáky v předstihu kompilovat.
  • Kompilátor PHP - Součást interpretu. Jedná se o parser, který vytvoří mezikód, jenž je následně vykonán.
  • PHP mezikód - jazyk symbolických adres (assembler), do kterého jsou před vykonáním překládány všechny skripty. Každý skript projde nejprve lexikální analýzou (převedením na tokeny) a syntaktickou analýzou s překladem.

Definice pojmů jsou velmi zjednodušené a jsou zasazeny pouze do kontextu jazyka PHP - jedná se tedy o PHP interpret, kompilátor PHP a PHP mezikód. Jinak obecně interpret samozřejmě nemusí vykonávat pouze zdrojové kódy, ale také mezikód nebo bytecode.

Motivace

Motivací nám bude především to, že u mnoha aplikací zabere více času zparsování všech načítaných skriptů než samotné provedení mezikódu. Zejména pokud je naše aplikace postavená nad frameworkem Zend, je použití cache kompilátoru téměř nutností.

APC

Projektů, které se zabývají znovuvyužitím mezikódu, je několik. My se zaměříme na ten asi nejznámější, vyvíjený samotnými vývojáři PHP a to je projekt APC.

Rozdíly oproti Pythonu

APC narozdíl od Pythonu neukládá mezikód do souborů na disk, ale do operační paměti. To má samozřejmě své výhody i nevýhody. Výhodou je, že spuštění aplikace je ještě rychlejší a je potřeba méně přístupů na disk. Nevýhodou pak je vyšší paměťová náročnost a dočasnost uložení (při restartu Apache o celou cache přijdeme).

Požadavky pro běh interpretu

APC je rozšíření PHP, a proto, aby správně fungovalo, je potřeba aby interpret PHP stále běžel. Pokud spustíme PHP jako CGI skript, bude nám APC na nic, protože se při každém požadavku bude interpret spouštět odznovu a cache APC se po každém požadavku smaže.

Při běhu PHP jako modulu Apache je situace lepší - o celou cache přijdeme jen při restartu Apache. Jelikož interpret PHP neběží pouze v jedné instanci, ale každý child proces Apache má svojí vlastní (těchto procesů může být i desítky - podle zatížení serveru), musejí jednotlivé instance sdílet cache mezi sebou. To může občas vést k tomu, že různí potomci Apache budou mít různou cache.

Při běhu PHP jako FastCGI je situace obdobná jako u modulu.

Nároky na paměť

V základním nastavení si APC alokuje pouhých 30MB pro celý server. To s rezervou stačí na běh tří aplikací rozsahu Magento shopu, takže pro privátní server s jednou doménou je toto nastavení naprosto dostačující.

Instalace

Instalace je na Ubuntu opět velmi jednoduchá.

sudo apt-get install php-apc
sudo apache2ctl graceful

Při instalaci je vytvořen konfigurační soubor /etc/php5/cgi/conf.d/apc.ini, kde můžeme měnit nastavení.

Testování

Sestava: Ubuntu 8.10, Apache 2.2.9, PHP 5.2.6 jako FastCGI, Intel Pentium D 805, 2048 DDR2 RAM. Testováno nástrojem Apache Bench.

aplikace RPS s APC RPS bez APC nárůst výkonu
Magento e-shop (209 vkládaných souborů) 5,26 2,98 176%
Nette Fifteen (56 vkládaných souborů) 121 40 302%
aplikace s databází (37 vkládaných souborů) 86,43 49,23 175%
aplikace bez databáze (32 vkládaných souborů) 164 74 221%
menší aplikace bez databáze (9 vkládaných souborů) 426 177 240%

Z výsledků testů je zřejmé, že nasazení APC se opravdu vyplatí. Narůst výkonu minimálně o 150% může mnoha webům velmi odlehčit.

Odkazy

dokumentace APC

download APC

Utilitka

APC obsahuje moc pěkný skript apc.php, který umí zobrazit aktuální vytížení paměti, úspěšnost cache a další. Utilitka bohužel není obsažena v balíku z repozitáře. Je proto potřeba stáhnout z odkazu výše celé APC a skript si z něj zkopírovat.

apc.php

evaluation

comments

2009-02-08 10:45:27

Rozvinu své výhrady, co jsem psal po twitteru:

Jazyky se v zásadě dělily na jazyky interpretované a jazyky kompilované. U kompilovaných jazyků (typicky C nebo Pascal) existoval program, který se nazýval "kompilátor" (nebo kompiler). Jeho úkolem bylo přeložit zdrojový kód ve vyšším jazyce na kód v jazyce nižším, typicky na strojový kód ("nativní"). Takto přeložený kód pak fungoval bez dalších nutných věcí (a hlavně bez kompileru).
U interpretovaných jazyků (typicky BASIC) existoval místo kompileru tzv. "interpret", což byl program, který prováděl přímo příkazy vyššího jazyka bez překladu do jazyka nižšího - což říkám zjednodušeně, ale dalo by se to připodobnit k "virtuálnímu stroji", který přímo vykonává příkazy zapsané ve vyšším jazyku.

Kompilované jazyky byly rychlejší, interpretované zase měly možnost interaktivní práce a (teoreticky) běhu všude možně kde existoval interpret bez nutnosti překládat do native kódu.

Určitým hybridem byly jazyky s p-kódem. U takových přeložil kompilátor zdrojový kód do "mezikódu" (p-kódu) a ten byl pak interpretován malou "runtime" knihovnou. Výhodou byla přenositelnost (opět spíš teoretická) onoho p-kódu mezi platformami.

Tak bylo. Dnes je trochu jinak, dnes se původně ostrá hrana smyla. Dnes existují i u "interpretovaných" jazyků např. překladače do pseudokódu (tuším že třeba Python to dělá přesně tak), naopak p-kód leckdy není čistě interpretován, ale je překladačem JIT (Just-In-Time) přeložen do nativního kódu před svým prvním použitím...

Mno. A assembler či "jazyk symbolických adres"? Mikroprocesor pracuje na "své" úrovni s instrukčními kódy. Např. u Z80 znamenal kód 0x00 "nedělej nic", kód 0xC3 "přejdi na určenou adresu" nebo 0x21 "naplň registry H a L číslem". A protože by si takové kódy pamatovali jen drsní core programátoři, tak byly nahrazeny (kvůli lidem) tzv. "mnemotechnickým zápisem" v "jazyce symbolických adres". Takže místo 00 jsme psali NOP (No OPeration), JP (JumP) či LD HL,1234 (LoaD). A navíc si nikdo nemusel pamatovat, že rutina pro tisk je na adrese 0x2DC, ale napsalo se "CALL TISK" - tedy ty adresy nebyly absolutní, ale symbolické. No a assembler pak tenhle zápis vzal, mnemotechnické označení přeložil na jejich kódy, spočítal, jaká fyzická adresa odpovídá jaké symbolické, a dal to všechno dohromady. (Víceméně. Ještě tam občas býval např. linker, který to spojil s knihovnami).

Takže asi takový je vztah mezi interpretem, kompilerem, mezikódem, assemblerem a jazykem symbolických adres.

replied by [2] Dundee
replied by [8] M
[2] Dundee
2009-02-08 12:56:31

#1 Martin Malý: Díky za přípomínky.

Ty definice samozřejmě nejsou dostatečně robustní. Snaží se pouze o jednoduché vysvětlení těchto pojmů v kontextu jazyka PHP čtenáři, který o nich nikdy předtím neslyšel.

PHP mezikód, neboli opcode, je jazykem symbolických adres. Viz třeba http://www.zapt.info/opcodes.html. Použil jsem v závorce termín assembler, což samozřejmě není správné, protože tento termín označuje program, který překládá jazyk symbolických adres do strojového kódu. Tento termín se ale často pro označení JSA používá a je použit i v knize Pokročilé programování v PHP 5 od Goerge Schlossnagla (str. 616), takže jsem se nezdráhal ho také použít.

Definice interpretu a kompilátoru je zdá se příliš zjednodušená, opravdu jsem ale nechtěl čtenáře příliš zatěžovat celou problematikou programovacích jazyků a překladačů :)

2009-02-08 14:57:25

Už jen drobnost: Nazývat JSA "assemblerem" je dlouholetý zvyk, v tom bych problém neviděl. Viděl bych spíš problém v tom označovat p-kód (mezikód) za JSA. P-code je BINÁRNÍ kód na úrovni strojového kódu, a MŮŽE BÝT zapsán (kvůli čitelnosti) v odpovídajícím jazyku symbolických adres. Převod mezi textovým a binárním vyjádřením má na starosti assembler (disassembler). Skripty jsou před vykonáním samosebou přeloženy do binárního p-kódu, NIKOLI do JSA. Výsledkem je binární kód, nikoli zdrojový text v JSA.

Co se snažím říct: Že JSA jsou vlastně "textové aliasy" pro instrukční kódy. Tvrdit, že skripty jsou překládány do JSA je nesmysl, protože jsou překládány právě do těchto kódů - výstupem je tedy binární chuchvalec dat a kódu, nikoli text.

A ještě podotknutí: Člověk, který nikdy neslyšel pojem kompiler a interpret, s největší pravděpodobností nebude ani tušit, co je to lexikální analýza, syntaktická analýza, parser či token... Takže mám dojem, že vysvětlení "kompilátor je součást interpretru a jde o parser" neznalému spíš věci zatemní než vysvětlí. Ale to už jen opravdu na okraj.

replied by [4] Dundee
[4] Dundee
2009-02-08 15:58:52

#3 Martin Malý: Máš pravdu. Díky za upozornění. Vůbec mi nesescvaklo, že přestože se výpisy PHP mezikódu zapisují ve formě JSA, samotný mezikód je vlastně bytecode.

Tak nějak mi přišlo, že funkce parseru je mezi lidmi více znamá, než třeba funkce kompileru. Přece jen parsery se používají i na práci s XML, takže je o nich určité povědomí.

[5] ToM
2009-02-08 22:54:11

No parser( nebo teda spis lexikalni analyzator ) je casti kompileru, tak nejak tu vetu o kompilatoru PHP nechapu. Ale jak to ctu, tak jsi to hodne zjednodusil.

Doporucuji http://c7y.phparch.com/c/entry/1/art,apc_facebook ohledne APC.

Jinak ja pouzivam eAccelerator a taky neni vubec spatnej.

2010-11-13 08:10:09

Sice hodně obecně laděné, nicméně jako doporučení perfektní. Kdysi jsem se nechal ukecat do Xcache, což byla naprostá noční můra. Konkrétně se Zenf Frameworkem to vyříralo paměť, samé segfaults a podobné lahůdky. Po přechodu na APC se serveru nejen více odlehčilo, ale vypadá to i na dlouhou stabilitu.

[7] Martin
2011-01-23 11:05:33

Dříve jsem používal eAccelerator, dneska mám nasazeno APC a musím jedině pochválit. APC se mi zdá o drobek rychlejší než eAccelerator a pokud jej navíc vyvíjejí sami tvůrci PHP, myslím, že není co (h)řešit.

[8] M
2011-04-30 08:48:36

#1 Martin Malý: Assembler = jazyk symbolických adres. Instrukční kód (zápis v číselné podobě) se nazývá zdrojovým kódem. Někdo si plete zdrojový kód a assembler.

2012-01-02 19:15:52

Dnes je i strojový kód většinou p-kódem.

Procesory dnes nevykonávají ani strojový kód, ale berou ho jako p-kód, který si dále kompilují na skutečný jazyk, který vykonávají, tedy sérii instrukcí (mikroinstrukce) pro své jednotky.

Takže pokud doma nevyrábíte vlastní procesory a čipy a nezadáváte práci do výrobny křemíkových waferů, tak nikdo z Vás se nesetká s ničím jiným, než prekabátěným p-kódem, případně zápisem vyššího programovacího jazyka.

Strojový kód, nebo jeho symbolický zápis assembler – je dnes p-kódem procesorů.

PHP si teoreticky, stejně jako Python, a naprosto nic tomu nebrání můžete zkompilovat do jakého strojáku chcete. Že to tak oficiální interpretr nedělá je jiná věc, ale jde to.

Není žádný rozdíl mezi kompilátory a interpretery, což dokazuje vývoj, který těmto rozlišovatelům dává na frak. Jsou jen jazyk, které se někdo rozhodl MOMENTÁLNĚ interpretovat, nebo kompilovat, případně prohánět p–kódem, případně prohánět intepretací s p-kódem a JITem a řady dalších možností ještě přijdou.

Každý programovací jazyk jde kompilovat i interpretovat i přeložit jak do p-kódu, tak do nějakého strojového jazyka.

Jakékoli rozlišování je v zásadě k ničemu.

Jako diplomovou práci si můžete napsat kompilátor PHP do strojáku. Není to až tak složité a už by to dávno někdo tisíckrát udělal, kdyby existovala norma PHP, tedy kdyby nemusel pořád s jazykem na vestě emulovat chování www.php.net produktů.

Už jsem viděl intepretr C++, kompilátor Javy do strojáku, a mnohé další. Pokud má programovací jazyk psanou normu, je to jednoduché.

Miloslav Ponkrác

comments closed