Jakub Wietrzyk

Jakub Wietrzyk Prezes, Projektant,
Programista, Media
IT Sp z o.o.

Temat: Jak przyspieszyc CakePHP?

Jak wszyscy pewnie wiecie CakePHP nie należy do najszybszych frameworków, w ogóle sam język PHP nie jest szczytem marzeń jeśli chodzi o wydajność - dlatego ciągle i nieustannie, wraz z moim zespołem, poszukujemy sposobów na przyspieszenie Cake'a.

Jeden ze sposobow, to odlaczanie wszystkich niepotrzebnych modeli i wczytywanie modeli tylko wtedy kiedy sa potrzebne (ktoś już o tym wspomniał na tej grupie).

Oczywiście odłączanie relacji ręcznie jest dosyć męczące, wiec zaprojektowałem taka metodę w app_model.php:


function unbindAll() {
$relations = array ('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');

$exclude = func_get_args();

foreach ($relations as $r) {
$out = array();
foreach ($this->{$r} as $modelName=>$relationData) {
if (!in_array($modelName,$exclude)) {
$out[] = $modelName;
}
}
$this->unbindModel(array($r => $out));
}
}


Przykład użycia: Załóżmy, że mamy model User, który, mówiąc językiem CakePHP, $hasMany = ('Post','Comment);

Chcemy wyświetlić i pobrać z bazy tylko dane użytkownika:


$this->User->unbindAll();
$this->set('user', $this->User->read(null, $user_id));


Dzięki użyciu unbindAll() żadne niepotrzebne dane nie zostaną pobrane. Gdybyśmy chcieli wyświetlić użytkownika i jego Posty, wystarczy jako parametr podać, 'Post':


$this->User->unbindAll('Post');
$this->set('user', $this->User->read(null, $user_id));


Mam jeszcze kilka innych sposobów na Cake'a, ale zanim się nimi podzielę chciałbym wcześniej zobaczyć jakieś Wasze pomysły.
Paweł Rabinek

Paweł Rabinek Specjalista Google
AdWords i Analytics

Temat: Jak przyspieszyc CakePHP?

Twoja funkcja jest zbędna. Zamiast read() używam findAll(), lub find() z recursive -1. "Set $recursive to -1 if you want no associated data fetched during the find". Do przyspieszenia to przede wszystkim pobieranie tylko potrzebnych pól z bazy i minimalizacja ilości zapytań. Czasem też cache się przydaje, ale jakoś nie działa za dobrze w połączeniu z własnymi routes :(
Jakub Wietrzyk

Jakub Wietrzyk Prezes, Projektant,
Programista, Media
IT Sp z o.o.

Temat: Jak przyspieszyc CakePHP?

Paweł Rabinek:
Twoja funkcja jest zbędna. Zamiast read() używam findAll(), lub find() z recursive -1. "Set $recursive to -1 if you want no associated data fetched during the find". Do przyspieszenia to (...)

Chyba nie dokońca jasno sie wyrazilem: cała korzyśc z używania mojej "zbędnej", jak twierdzisz, funkcji jest wtedy gdy podasz jakiś parametr, żeby np. odłączyć wszystkie powiązane modele _poza_ tymi wybranymi. Zwykłym cake'owym sposobem musiałbyś użyć kilku funkcji unbindModel i wypełniać skomplikowaną tablicę.

Chodzi o to żeby przy dużym projekcie, gdzie masz masę wielopoziomowymch relacji - korzystać z "dobrodziejstw" cake'a pozostawiając podłączone tylko te modele, które są w danej chwili potrzebne.
Tomasz K.

Tomasz K. Wolny strzelec spod
znaku byka.

Temat: Jak przyspieszyc CakePHP?

Przyznaję że tak robię jak Jakubie wspominasz - już to na CakeBackerze ktoś opisał, jest nawet podobna metoda dla app_model. Raczej więcej oszczędza się pamięci ale czy przyśpiesza - dla dużych ilości danych na pewno. Za to próbowałem potraktować Cake'a IonCubem. na Home.pl coś muli - mają loadery ale aplikacje działają coś wolniej. Po prostu trzeba pisać aplikacje z prostymi relacjami, albo je optymalizować. Muszę spróbować z innymi serwerami. Jak macie jakieś doświadczenie z taką optymalizacją to chętnie się zapoznam.
Jakub Wietrzyk

Jakub Wietrzyk Prezes, Projektant,
Programista, Media
IT Sp z o.o.

Temat: Jak przyspieszyc CakePHP?

Tomasz K.:
(..)
Za to próbowałem potraktować Cake'a IonCubem. na Home.pl coś muli - mają loadery ale aplikacje działają coś wolniej.

A czy IonCube to nie jest encoder / obscufator - wydaje mi sie ze z zalozenia to on nic nie przyspiesza, a jedynie zabezpiecza skrypt.

Ja natomiast testowałem APC (http://pl.php.net/apc), na testach apache benchmark (ab) daje to prawie 70-80% req/s (mam nadzeje ze nie przekrecilem % w kazdym razie roznica byla znaczna) - tylko troche zabawy zeby to (APC) sobie skompilowac

Moze ktos ma doswiadczenia z eAccelleratorem albo czyms podobnym?
Tomasz K.

Tomasz K. Wolny strzelec spod
znaku byka.

Temat: Jak przyspieszyc CakePHP?

Jakub Wietrzyk:
A czy IonCube to nie jest encoder / obscufator - wydaje mi sie ze

Producent twierdzi, że IonCube to także optymalizator. APC będę musiał się przyjrzeć w takim razie.

Temat: Jak przyspieszyc CakePHP?

Takie rzeczy jak unbindAll w AppModel to były fajne 2 lata temu ;)
https://trac.cakephp.org/ticket/669

Teraz masz ContainableBehavior[1] Felixa, albo BindableBehavior[2] Mariano(sam używam Bindable).
A jeśli mówimy o 1.1 (ja od roku tylko 1.2) to jest świetne expects() [3]

Co do przyśpieszania - troche podstaw:
trzeba patrzeć czy dostajesz tylko te dane których potrzebujesz (pola,zapytania itd - j.w.)
używam Memcache (CacheeEgines)
trzeba uważać na uses(),App::import()
elementy (szczególnie z requestAction) - cachowane [4] (można cachować z parametrem - inny cache zależnie np od id zalogowanego usera)
niektóre modele (szczególnie te proste - typu 'enum')- cachowanie całych zapytań, danych
HABTM - czasem dobrze popatrzeć czy nie można przyśpieszyć czegoś używając 'finderQuery'

To kilka rzeczy które mi przyszło od razu do głowy ale jest tego więcej.

Oczywiście optymalizujemy *PO* zbudowaniu aplikacji :)

[1] http://www.thinkingphp.org/2007/06/14/containable-20-b...
[2] http://bakery.cakephp.org/articles/view/bindable-behav...
[3] http://bakery.cakephp.org/articles/view/an-improvement...
[4] http://bakery.cakephp.org/articles/view/creating-reusa...
Jakub Wietrzyk

Jakub Wietrzyk Prezes, Projektant,
Programista, Media
IT Sp z o.o.

Temat: Jak przyspieszyc CakePHP?

Marcin Domański:
Takie rzeczy jak unbindAll w AppModel to były fajne 2 lata temu ;)
https://trac.cakephp.org/ticket/669

Teraz masz ContainableBehavior[1] Felixa, albo BindableBehavior[2] Mariano(sam używam Bindable).
A jeśli mówimy o 1.1 (ja od roku tylko 1.2) to jest świetne expects() [3]

Jeszcze sie do 1.2 nie przekonalem, nie potrafie powiedziec czy stoja za tym jakies rzeczowe argumenty :) ale przyznaje BindableBehavior robi wrazenie, expects dla 1.1 z grubsza dziala tak samo jak moje rozwiazanie (ktorego BTW uzywam od roku conajmniej) - widze ze trzeba do bakery zagladac czesciej...

Co do przyśpieszania - troche podstaw:

:)
trzeba patrzeć czy dostajesz tylko te dane których potrzebujesz (pola,zapytania itd - j.w.)
używam Memcache (CacheeEgines)

Jak uzywasz memcache i do czego? Ja w memcache trzymam wyniki zapytan o uprawnienia (bo te rzadko sie zmieniaja a bywaja skomplikowane), wyniki innych, duzych, czesto i powtarzajacych sie zapytan, statusy online uzytkownikow (fajnie mozna wykorzystac expire time, bez dodatkowych zapytan do bazy czy grzebania po sesjach)

Alternatywa dla powyzszego to cache plikowe, ale to tylko do calych elementow strony typu chmury tagow itp, ktore zmianiaja sie duzo rzadziej. Szkoda cakowe calych widokow tak rzadko sie przydaje...

Mam zasade ze wszelkie "ulepszenie" cake musi byc proste w uzyciu, tak aby od razu caly team nie mial problemow z jego stosowaniem.

Uzycie memcache w modelu ogranicza sie wiec do napisania przed zapytaniem

$this->Model->cacheNext('tutaj jakies tekstowe id zapytania');
$this->Model->findAll(...);

i dziala to tak, ze jesli wynik zapytania jest w memcache (czy tez innym cache engine), to jest zwracane, jesli nie to oczywiscie zapytanie idzie do DB a jego wynik po drodze jest zapisywany do cache - polecam takie rozwiazanie, bardzo latwe do zaimplementowania (dlatego gotowca nie podam) i latwe w uzyciu.
trzeba uważać na uses(),App::import()
elementy (szczególnie z requestAction) - cachowane [4] (można cachować z parametrem - inny cache zależnie np od id zalogowanego usera)

Uwazam ze requestAction jest "evil", Cake traci mase czasu na tworzenie klas modeli, kontrolera, komponentow, helperow (polecam odpalenie jakiegos profilera albo chociaz PEAR::Benchbark), a requestAction odpala ci w jednym requescie "dwa" cake'i, albo wiecej jak wiecej masz takich elementow..
niektóre modele (szczególnie te proste - typu 'enum')-

?
cachowanie całych zapytań, danych
HABTM - czasem dobrze popatrzeć czy nie można przyśpieszyć czegoś używając 'finderQuery'

To kilka rzeczy które mi przyszło od razu do głowy ale jest tego więcej.

Oczywiście optymalizujemy *PO* zbudowaniu aplikacji :)

Tu sie zupelnie nie zgadzam (chyba ze to byl zart):

Z mojego doswiadczenia: w duzej aplikacji, jesli zespol nie ma dobrych nawykow od samego poczatku to pozniej jest koszmar...

W wiekszosci przypadkow juz podczas pisania jakiejs akcji da sie zauwazyc "ciezkie" momenty i zawczasu pomyslec o ich optymalizacji.

Jestem wielkim przeciwnikiem szukania "wąskich gardeł" *PO*, zwlasza w projekcie skladajacym sie z >30 kontrolerow po kilka akcji kazdy...
(linki)
dzieki za ciekawa bibliografie

BTW: Widze ze z CakePHP w Polsce nie jest tak zle, jest kilku ludzi co ma cos ciekawego do powiedzenia. Dzieki za posty i pozdrawiam.

Temat: Jak przyspieszyc CakePHP?

Jakub Wietrzyk:
Jeszcze sie do 1.2 nie przekonalem, nie potrafie powiedziec czy stoja za tym jakies rzeczowe argumenty :)
1.2 powinien się nazywać 1.5 przynajmniej, jak dla mnie doszło tyle nowych rzeczy, że 1.1 nie byłem w stanie już używać :) Behaviors, CacheEngines,nawet EmailComponent (chociaż używam ze zmodyfikowanej wersji która ma SwiftMailer pod maską), Testy!,Reverse Routes, AuthComponent, i18n, nowy FormHelper, walidacja, Set, Debugger, Socket... uff móglbym tak bez końca ;)
Dużo z tych rzeczy było dostępnych przez rozmaite hacki itp ale dobrze mieć to wszytko w core.
ale przyznaje BindableBehavior robi wrazenie, expects dla 1.1 z grubsza dziala tak samo jak moje rozwiazanie (ktorego BTW uzywam od roku conajmniej) - widze ze trzeba do bakery zagladac czesciej...
Tak expects działa podobnie + kilka małych udogodnień.

Zza kulis to niedługo(jak to w świecie Cake jest to określenie relatywne;) będzie Bakery 2.0.
trzeba patrzeć czy dostajesz tylko te dane których potrzebujesz (pola,zapytania itd - j.w.)
używam Memcache (CacheeEgines)

Jak uzywasz memcache i do czego?
Zależnie od ilości pamięci dostępnej i użycia zapytań ale uprawnienia są wysoko na liście. Przed cachowaniem zwykle robię testy co najdłużej zajmuje i co moge wrzucić do plików, a co bardziej opłaca sie do memcache.
Ja w memcache trzymam wyniki zapytan o uprawnienia (bo te rzadko sie zmieniaja a bywaja skomplikowane), wyniki innych, duzych, czesto i powtarzajacych sie zapytan, statusy online uzytkownikow (fajnie mozna wykorzystac expire time, bez dodatkowych zapytan do bazy czy grzebania po sesjach)

Alternatywa dla powyzszego to cache plikowe, ale to tylko do calych elementow strony typu chmury tagow itp, ktore zmianiaja sie duzo rzadziej. Szkoda cakowe calych widokow tak rzadko sie przydaje...
Tak ,ale własnie już elementy można śmiało cachować (oczywiście też z rozwagą).

Uzycie memcache w modelu ogranicza sie wiec do napisania przed zapytaniem
Jako, że 1.2 ma CacheEngines[1] (jednym z nich jest własnie memcache) to używanie memcache jest jeszcze prostsze - polecam popatrzeć na testy po przykłady użycia.

Uwazam ze requestAction jest "evil", Cake traci mase czasu na tworzenie klas modeli, kontrolera, komponentow, helperow (polecam odpalenie jakiegos profilera albo chociaz PEAR::Benchbark), a requestAction odpala ci w jednym requescie "dwa" cake'i, albo wiecej jak wiecej masz takich elementow..
Tak zdecydowanie jest 'evil' i często można go uniknąc na etapie projektowania, jednak czasem jest potrzebny. Chyba nie mogę ujawnić aktualnych przykładów ale np. chmura tagów jest dobrym 'elementem' gdzie można użyć requestAction i cachować ja na dzień dwa lub więcej zależnie od potrzeb. Można nawet cache odświeżac z crona(ok to troche na wyrost do tego przykładu ale chodzi mi o samą możliwośc)(co do crona - nowe Console w 1.2 to miód :)
niektóre modele (szczególnie te proste - typu 'enum')-
sorki skrót myślowy - wiadomo, że enum jest 'evil' brak możliwości łatwego dodania nowych opcji itp więc wyjścia są 2: a) wrzucić opcje do php jako array - nie idealne ale czasem wystarczające albo wrzucić do opcje do bazy zrobić model i powiązać. te modele zmieniaja się _bardzo_ rzadko ale jednak zmieniają.

Oczywiście optymalizujemy *PO* zbudowaniu aplikacji :)
Tu sie zupelnie nie zgadzam (chyba ze to byl zart):
Nie był :)
Z mojego doswiadczenia: w duzej aplikacji, jesli zespol nie ma dobrych nawykow od samego poczatku to pozniej jest koszmar...
Zdecydowanie tak, ale właśnie te dobre nawyki nie mogą być zbyt 'dobre' ;)
Widziałem tyle przypadków gdzie ludzie chcieli oszczędzic jedno proste zapytanie do bazy i wymyślali takie rzeczy, że nigdy bym takiego kodu nie pokazał nikomu ;) (tak na #cakephp zdarzają sie takie chwile) Oczywiście to są skrajności. Podsumowując - jestem za optymalizacją ale nie można przesadzać (a łatwo wpaść w "szał" premature optimization - wiem z własnej ręki).

Cytując wikipedie [2]
Donald Knuth said, paraphrasing Hoare,
* "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil."
Charles Cook commented,

* "I agree with this. It's usually not worth spending a lot of time micro-optimizing code before it's obvious where the performance bottlenecks are. But, conversely, when designing software at a system level, performance issues should always be considered from the beginning. A good software developer will do this automatically, having developed a feel for where performance issues will cause problems. An inexperienced developer will not bother, misguidedly believing that a bit of fine tuning at a later stage will fix any problems."

Wszystko sprowadza się do doświadczenia.

Na CakeFest była przentacja ludzi z mozilli (dużo ciekawego na ich blogu AMO odnośnie wydajności cake - naprawde wyciskają z niego siódme poty) podobno ciekawa i dotyczyła w dużej mierze cachowania. Ma być wypuszczone dvd (a raczej miało jak byłem na fosdem gwoo zaklinał się, że jak najszybciej - przy okazji jest video i pdf z fosdem - tam też jest dużo informacji o 1.2)
BTW: Widze ze z CakePHP w Polsce nie jest tak zle, jest kilku ludzi co ma cos ciekawego do powiedzenia. Dzieki za posty i pozdrawiam.
Jest troche ludzi w Polsce, często przewijają sie polacy przez #cakephp/#cakephp.pl

[1] http://api.cakephp.org/1.2/class_cache_engine.html#d7e...
http://book.cakephp.org/view/213/cache
[2] http://en.wikipedia.org/wiki/Optimization_(computer_sc...
Maciej G.

Maciej G. Programista
PHP/JavaScript/Flex
Webdeveloper HTML,
CSS

Temat: Jak przyspieszyc CakePHP?

Aż miło się czyta waszą dyskusję! Przelatuję od postu Marcina do Jakuba i z powrotem i co chwilę dowiaduję się czegoś nowego. Takich dyskusji mi tu właśnie brakowało! Dzięki za to, że dzielicie się wiedzą!

I jeszcze tak z ciekawości spytam... Jakub, jak to implementujesz?

$this->Model->cacheNext('tutaj jakies tekstowe id zapytania');
$this->Model->findAll(...);

Wykorzystujesz callback beforeFind?

Temat: Jak przyspieszyc CakePHP?

Maciej G.:
Aż miło się czyta waszą dyskusję! Przelatuję od postu Marcina do Jakuba i z powrotem i co chwilę dowiaduję się czegoś nowego. Takich dyskusji mi tu właśnie brakowało! Dzięki za to, że dzielicie się wiedzą!
Nie ma sprawy - sami też macie dobre wiki :)
I jeszcze tak z ciekawości spytam... Jakub, jak to implementujesz?

$this->Model->cacheNext('tutaj jakies tekstowe id zapytania');
$this->Model->findAll(...);

Wykorzystujesz callback beforeFind?
nie wiem jak Jakub to implementuje ale z tego co wiem w beforeFind nie można zwrócić wyników zapytania z pominięciem finda(moge się mylić).
Jedną z opcji jest przeładowanie metody find w AppModel i tam sprawdzenie czy dla danego zapytania jest cache czy nie. Można też sprawdzać wpierw czy wogule cachujemy zapytania dla aktualnego modelu(żeby nie narzucać dodatkowego sprawdzania cachu).
w 1.2 mialem cos takiego (z pamięci bo nie mam kodu przy sobie)
find('all', array('conditons' => 'blablabla', 'cache' => "1 day")
potem sprawdzałem
a) czy używam cache dla tego modelu
b) czy jest klucz 'cache' w danym zapytaniu
c) sprawdzam cache
nazwa cachowana to było cos w stylu "model_all_md5(serialize(array_z_conditions_etc))"

Jak macie jakieś inne strategie to chętnie posłucham :)



Wyślij zaproszenie do