Tervezési mintákkal teletűzdelt PHP gyorsítása
Sziasztok!
Egy barátommal régóta fejlszetek egy keretrendszert, ami elég összetett és szét van tervmintázva ( Absztrakt gyárak, összetételek, egykék, ősosztályok MVC-hez ). Ennek az az ára, hogy egy oldal legenerálásához ~20 osztályt kell példányosítani, persze jó sok öröklődéssel.
Az oldal befordítása ~5 másodpercet vesz igénybe egy egész komoly szerveren. Érdekes, de a futási idő 0.5 mp alatt marad. Mögé tettem egy eacceleratort, ez levitte a forditási időt 2 sec-re.
A kérdésem az lenne, hogy mi lehet az oka, hogy ennyire lassú a fordítás és hogyan lehetne ezt gyorsítani.
Annyi még fontos lehet, hogy minden egyes include az __autoload függvényen keresztül valósul meg.
Segítsetek kérlek!
zottty
■ Egy barátommal régóta fejlszetek egy keretrendszert, ami elég összetett és szét van tervmintázva ( Absztrakt gyárak, összetételek, egykék, ősosztályok MVC-hez ). Ennek az az ára, hogy egy oldal legenerálásához ~20 osztályt kell példányosítani, persze jó sok öröklődéssel.
Az oldal befordítása ~5 másodpercet vesz igénybe egy egész komoly szerveren. Érdekes, de a futási idő 0.5 mp alatt marad. Mögé tettem egy eacceleratort, ez levitte a forditási időt 2 sec-re.
A kérdésem az lenne, hogy mi lehet az oka, hogy ennyire lassú a fordítás és hogyan lehetne ezt gyorsítani.
Annyi még fontos lehet, hogy minden egyes include az __autoload függvényen keresztül valósul meg.
Segítsetek kérlek!
zottty
Bármi
Ha már MVCben fejlesztesz, illene tudnod, hogy ennyiből lehetetlen megmondani. Lehet az adatbázis is, de lehet az objektum modell is.
Pl. ha MySQL/InnoDB-t használsz és foreign keyeket és a foreign key hivatkozik egy mezőre, viszont arra nincs index, akkor az rohadtul belassítja a dolgot. De ugyanúgy lehet, hogy szarul írtátok meg a PHP kódot.
De mondjuk, lehet az is, hogy szarul van fölconfigolva a szerver.
Szóval érted. 20 osztály nem a világ, annyit minden rendes programozó megeszik reggelire, de ha nem jól írtad meg, akkor bizony megeszi a gépet.
Remélem, segített valamit.
J
forditás
A Fordítás lassú, nem a futás!
gondolom
aha
Hát igen, ez így valóban elég lassú lehet, még szerencse, hogy ilyet nem lehet csinálni:
In the referenced table, there must be an index where the referenced columns are listed as the first columns in the same order.
"Mondjuk ha már MVC-ben fejlesztesz", és mondjuk átgondolod, hogy mire is való egy külső kulcs, akkor a manuál nélkül egy kis józan paraszti ésszel is gyanus lehetne, hogy az említett példának nem sok értelme van.
Felhő
re
tessék levenni a könyvespolcról ;) a könyv igen nagy százalékban csak ezzel foglalkozik
Memóriahasználat?
kód nélkül
Így látatlanban nehéz megítélni, hogy ez sok vagy kevés, indokolt vagy sem. Egy egyszerű GET kérés kiszolgálásakor is szükség van ezekre?
Ha esetleg mutattok kódot, akkor lehetne többet mondani a tervezésről.
Hát ezzel elég sokat lehet ronatni a dolgokon, ha nem jól van megcsinálva. Ha itt mindig filerendszerben kutakodsz, akkor az nem túl ideális. A legjobb, ha egy osztály neve egyből leképezhető egy fájlnévre, ami gondolom a factory-k esetén könnyen megoldható, ezért ezekben az esetekben ajánlott az __autoload mellőzése. Azokkal az alap osztályokkal, amikkel ez nem tehető meg, azt is tehetitek, hogy egy tömbe teszitek a példányosításukhoz szükséges include fájl nevét (a kulcsok az osztálynevek), és ez alapján includeoljátok a szükséges fájlt. Plusz megoldás lehet az alap osztályok egy nagy fájlba másolása, és ennek használata (főleg, ha használtok eaccelerator, akkor nem gond).
Plusz érdemes lenne valami profiler cuccal végigmenni rajta, mert az a 0.5 mp sem kevés sőt.
Felhő
____
Szükség sajnos.
Van egy errorhandler, aztán persze van hozzá logger, amit a loggerfactory gyárt, attól függ, mire kell, amit XMLből szed, amit az XML factory gyárt, mert sosem tudhatjuk, milyen XMLkezelő támogatás van az adott rendszeren, aztán akkor már legyen egy Exception-ből származtatott BaseException kivételem, de persze az absztrakt, senki sem dobja, majd példányosít valamit.
Eleve megjön egy DBFactory, aztán az SQLkezelő ösosztályból származtat egy konkrétat, vannak Userek, meg Sessionok, meg minden hozzávaló, változókezelők, és akkor még messze van az MVC, ami persze megint absztrakt, majd mindeki megmondja pontosan mit akar, ehh, meg még ezer dolog...
De a futás tényleg csodás, pikk-pakk < 0.5sec.
De amíg befordítja!!
Az autoloadolás kb így néz ki:
Kell a switch?
Szerintem, azt, hogy milyen modult akarsz betölteni, már sokkal előbb le kell ellenőrizni. Hogy ha GET-ben adod meg a modult, állj át "szép" URLekre és adatbázisból szedd a valid URLeket, így kisebb a valószínűsége egy exploitnak és valszeg gyorsabb is lesz.
Húha
Ha nincsen switch akkor hogyan mondod meg, hogy melyik esetben mit töltsön be? A többe eset között választani kell. Nem minden mehet regexp-pel ...
Nem tudom mit jelent nálad a sokkal előbb. Az autoload elveszti a funkcionalitását, ha előre megmondom, hogy melyik modulhoz milyen osztályok szükségesek, mert az azt jelenti, hogy odateszem a file elejére mondjuk include_once-szal.
Ui:
Ne haragudj proclub, de ennek a válaszodnak megint semmi köze a kérdéshez ...
Autoloader
Viszont az alaposztályok egy file-ba tétele a cache-eléshez jó ötlet azt megnézzük.
refactoring
Pl. ezt tipikusan lehetne optimalizálni, bár a fodítási időben ez nem fog sokat számítani. Én nem szeretek ilyesmi adatokat XML-ben tárolni, de az ízlés kérdése, viszont mindenféleképpen érdemes lenne cachelni, mondjuk serializált formában, ha nem túl nagy.
A switch ide szerintem is fölösleges:
isset($classes[$moduleName]) ? loadModule(); : bibiVan();
Amúgy meg switch esetén érdemes a gyakoriság szerint lefele csökkenő sorrendbe redezni a feltételeket. Plusz egy csomó osztályt nem így példányosítanék, hanem a konkrét factoryk névkonvenció alapján ránthatnák be a szükséges fájlokat, tipikusan ilyen lehetne a DB kapcsolat létrehozása stb..
Ennek még a tizede sem tekinthető igazán gyorsnak, szóval van még ott mit optimalizálni. Gondolj bele, hogy 2-3 kérést tudsz másodpercenként egyszerre kiszolgálni. A túlzott absztrakcióval is csínján kell bánni.
Felhő
bizony
Én ezt csak megerősíteni tudom. Bár nem rendelkezem a ZEND motor ilyen mélységű ismeretével, JAVA világban az öröklés egy tipikusan költséges dolog. Tehát könnyen elképzelhető, hogy a sebesség növelésének érdekében kénytelenek lesztek feláldozni az "eleganciából".
A tervezési minták nyakra-főre alkalmazásával kapcsolatban egyébként is vannak fenntartásaim, különösképp PHP környezetben. Ebben a platformban épp az a pláne, hogy gyakorlatilag semmi megkötést nem kényszerít a fejlesztőre, pont ez a varázsa, és épp ezért felesleges JAVA-t csinálni belőle fejlesztés során (itt jegyezném meg: semmi bajom a JAVA-val, imádom).
Kösz
Adott egy templateredszer, tipikus faszerkezet pl:
T1, T2, T3 : Template
T1.content = "Hello {valami}";
T1.children = Array("valami" => T2);
T2.content = "{mi}ag";
T2.childern = Array( "mi" => T3" );
T3.content = "ag!";
Ebből lesz a "Helló Világ" gondolom trivi, a kérdésem, hogy van-e valami tapasztalatotok arról, hogy hogyan lehet ezt szépen cache-elni.
Ha valamelyikőtöknek lenne egy átfogó válasza, azt nagyon megköszönném. Igazából a tartalom, szóval a childrenek a legtöbb helyen gyakran változnak...
z
P.s.: Meglepő, de az gyárak, meg az XML elég kevés időt vesz igénybe, gyakorlatilag semennyit.
Féligmeddig építő jellegű hozzászólás
Ami építőbb jellegű: próbáld meg a template-et magát cache-elni úgy, hogy lefordítod phpre. Ha az értelmeződ megvan hozzá, onnan már semmibe sem kerül...
Más: gondolom az XML alatt XSL-re gondoltál. Ha azt használod, akkor minek template?
XML
Nemrég megnéztem a smarty cache-elését, fordítását, nekem a Template-et befordítan azért lenne nehéz, mert:
1. egy echo van az egész oldalon, és ez kb a JSP szerű HTTPResponse::sendOutput()
Ez nagyon előnyös pl még nagyon régen készítettem a www.alagastroyal.hu weboldalt és egy rendelést nem csak a képernyőre kellett kiirni, hanem mail-ben is el kellett küldeni, gyakorlatilag így megvan nekem minden szépen faszerkezetben a memóriában és iszonyat könnyű vele ilyen esetekben dolgozni.
2. Ezt a példát nem igazán tudnám echo-san - azaz Template fordítással - megoldani:
Erre vmi gyors megoldás?
Bármit le tudsz fordíani php-re
A "gentime" outbut bufferrel picit bonyolultabb, a $s módszerrel viszont iszonyat egyszerű.
Ezt a "mindent a végén küldünk" kiszolgálási modellt nem nagyon ajánlom, csak ha valami igazán indokolja. Pl szerveroldali XSLT feldolgozás a végén. Főleg hosszan futó php-knél, mert lelassítja a kiszolgálást - az adatok küldése csak a generálás után történik, pedig közben is mehetne.
Túlhasznált patternek
http://www.phppatterns.com/docs/design/hello_world_in_patterns
Ez Harry Fuecks klasszikusa, és a lényege, hogy hogyan lehet a legegyszerűbb feladatot (Hello World) a lehető legjobban elbonyolítani.
+1 pattern
Occam's razor
"Nem szabad a dolgokat a szükségesnél bonyolultabban megfogalmazni"
De asszem ennyi elég a filózgatásokból mára ;)
KISS
Nem fejlesztésre
Az obektumorientált fejlesztés alapja, hogy a kód egy beláthatatlan távoli jövőre van optimalizálva. A Számalkon pl azt vallották (amivel én ugyan nem értek egyet), hogy ami a feladat leírásában mint főnév szerepel abból mind objektumot kell csinálni. Én eltúlzottnak tartom ezt a megközelítést, de akár működhet is egy valódi OO környezetben, ami optimalizálja az optimalizálni valót.
Occam folyt.
Az Occam féle filozófia arról szól, hogy egy probléma esetében a lehetséges (elvileg helyes) magyarázatok körül az a legjobb, amely esetében a lehető legkevesebb előfeltevést fogalmaztuk meg.
Véleményem szerint ez a hozzáállás a fejlesztésben is megállja a helyét. Semmit sem szabad bonyolultabban csinálni, mint amennyire azt adott probléma megoldása feltétlenül megköveteli.
Végezetül egy idézet a Schlossnagle könyvből:
"[...]A színes négyszögek táblára rajzolásával nincs semmi baj, de eme ragaszkodásunk a bonyolulthoz hatalmas hátrányt jelent. Amikor megtervezünk valmit, arra van szükség, hogy az adott problémára adjunk megoldást. Nem szabad előre tekintenünk, hogy a probléma vajon mi lesz évekkel később, egy nagy méretű, összetett felépítményben, amikor pedig egy általános célú eszközt építünk, nem szabad túlzott konkrétsággal megkötnünk a felhasználó kezét."
Azért nem ilyen veszélyes a helyzet
A lassúság oka azóta már kiderült: a sejtéssel ellentétben nem az autoloader volt a probléma, igaz itt is lehetett egy picit nyerni, de messze nem ez volt a szűk keresztmetszet. A templatekben a helyettesítésekkor a regexpek vitték el csúnyán az időt illetve külső http lekérések maradtak benn egy helyen ahol nem kellett volna. A regexp helyett kihasználva a helyettesítés specialitását, sokat nyertünk.
Ui:
Egy ötoldalas site mögét tényleg hiba lenne betenni, de megnyugtathatlak, hogy nem ötoldalas :)
Lehetne konkrétabban?
Kifejtenéd bővebben!
Konkrétabban!
Itt estünk a túlzott általánosság hibájába, hogy ezeket a még bennfelejtett, de az output szempontjából irreleváns részstringeket minden esetben csak az egész output legenerálásakor töröltük ki. Az indok annyi volt, hogy lehetnek olyan helyettesítendő részek, amelyek értéke csak később derül ki. Ez a feltevés részben jogos is, de az esetek döntő többségében nem csak később derül, hogy az adott részstringet ki kell törölni. Ezeket időben kitörölve javítottuk a generálás sebességét.
(A helyettesítés specialitása alatt csak annyit értettem, hogy csak azokkal volt a baj, amelyeket üres stringre kellett helyettesítenünk)
Devel gép
Nem tudom, mennyire illhet a Te helyzetedre, de eszembe jutott, hogy nekem a devel gépemen úgyszint iszonyatosan lassan futnak a dolgok. A szerveren viszont tizedmásodperc tört része alatt.
Fogglmam nincs, hogy mitől, talán Linux powa vagy ilyesmi...
J
Azt a gépet sose dobd ki,
Őőőőő
Igen, remélem még megvan! :D