Wednesday
7
April
autor:
Mateusz Tymek
kategorie:

Wydajność Zend Framework - cz. 1

Wiele osób sądzi że Zend Framework jest jednym z najmniej wydajnych rozwiązań tego typu.Tworząc aplikację bazującą na frameworku szybko zauważymy, że działa ona dużo wolniej niż odpowiednik napisany w czystym PHP. Czy powinniśmy się tym przejmować?
Im bardziej rozbudowany projekt, tym proporcjonalnie mniejszy narzut wynikający z zastosowania biblioteki. W większych aplikacjach problemem nie jest framework, lecz (najczęściej) baza danych.

Mimo tego, warto wiedzieć co ma wpływ na tak niską wydajność. A także to, że niewielkim kosztem można ją poprawić.
Dzisiejszy wpis to pierwsza część krótkiej serii artykułów o wydajności frameworku. Zaczniemy od podstaw - pokażę kilka sposobów na pomiar prędkości.

Podstawowe informacje

Najpierw dobrze byłoby wiedzieć w jakim czasie generuje się dana strona. Dodajemy jedną linijkę na początku pliku index.php:

$startTime = microtime(true);
Oraz kilka na końcu:

$time = round(microtime(true) - $startTime, 4);
$memory = round(memory_get_peak_usage(true) / 1024);
$files = count(get_included_files());
echo <<<EOS
<small style="position: fixed; right: 0; bottom: 0; background-color: #dedede; padding: 4px">
    Czas generowania strony: {$time}s <br />
    Pamięć: {$memory} kB <br />
    Dołączone pliki: {$files}
</small>
EOS;

Teraz przy każdym odświeżeniu strony zobaczymy trzy podstawowe, związane z wydajnością parametry.
To rozwiązanie jest dosyć prymitywne i ma pewne ograniczenia (np może psuć wyjście gdy generujemy XML w żądaniach AJAX), jednak ma też swoje zalety. Znacznie więcej informacji pokaże nam ZFDebug - jest to belka informacyjna, wzorowana na swoim odpowiedniku z projektu Symfony. Pod tym adresem można znaleźć więcej informacji na ten temat.

Profilowanie

Aby dowiedzieć się czym dokładnie zajmuje się interpreter, możemy sięgnąć po narzędzie profilujące - Xdebug. Zależnie od ustawionych opcji profilowanie może odbywać się za każdym razem gdy wczytywana jest strona lub (moim zdaniem bardziej praktyczny sposób) - na życzenie, po podaniu parametru XDEBUG_PROFILE w pasku adresu.

Jak skorzystać z profilera?

  • instalujemy rozszerzenie xdebug;
  • dodajemy wpis xdebug.profiler_enable_trigger = 1 do pliku php.ini;
  • restartujemy Apache (jeżeli php pracuje jako moduł tego serwera).

Teraz możemy zabrać się do profilowania. Wpisujemy w przeglądarce:

http://localhost/moja_strona/?XDEBUG_PROFILE

Strona zostanie wygenerowana, a odpowiednie informacje będą umieszczone w katalogu tymczasowym.
Dobrym narzędziem do prezentowania wyników jest Webgrind. Wystarczy go ściągnąć, wgrać do katalogu serwowanego przez Apache i uruchomić w przeglądarce. Po naciśnięciu przycisku "update" wygenerowany zostanie raport z ostatniej sesji profilera:

Webgrind to bardzo praktyczne narzędzie prezentujące wyniki profilowania

W każdym wierszu widać nazwę wywoływanej metody, całkowitą liczbę wywołań i czas wykonywania (najpierw samej metody, potem wraz z funkcjami które sama wywołała). Kliknięcie na nazwę dostarczy jeszcze więcej informacji.

Apache Benchmark

Wreszcie, mając podstawowe informacje, możemy sprawdzić jak strona zachowuje się pod większym obciążeniem. Odpowiedni program to Apache Benchmark. Wykonuje on określoną liczbę zapytań do serwera, które dodatkowo mogą być przeprowadzane jednocześnie.
Mimo swojej nazwy nadaje się także do testowania stron działających pod innymi serwerami (chociażby nginx).

Oto przykładowy wynik działania programu ab:

mateusz@linux-v65i:~> /usr/sbin/ab2 -n 100 -c5 http://domena.com/moja-strona/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done

Server Software:        Apache/2.2.13
Server Hostname:        domena.com
Server Port:            80

Document Path:          /moja-strona/
Document Length:        25480 bytes

Concurrency Level:      5
Time taken for tests:   31.902 seconds
Complete requests:      100
Failed requests:        11
   (Connect: 0, Receive: 0, Length: 11, Exceptions: 0)
Write errors:           0
Total transferred:      2584189 bytes
HTML transferred:       2547989 bytes
Requests per second:    3.13 [#/sec] (mean)
Time per request:       1595.123 [ms] (mean)
Time per request:       319.025 [ms] (mean, across all concurrent requests)
Transfer rate:          79.10 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3  16.9      0     124
Processing:   865 1575 211.4   1577    2002
Waiting:      847 1518 198.8   1511    1920
Total:        865 1578 214.9   1577    2018

Percentage of the requests served within a certain time (ms)
  50%   1577
  66%   1659
  75%   1737
  80%   1767
  90%   1855
  95%   1948
  98%   2002
  99%   2018
 100%   2018 (longest request)

Ilość zwróconych informacji jest dosyć duża. Zwróćmy uwagę na "Requests per seconds" - pokazuje on ile tak naprawdę "wytrzyma" nasza aplikacja.

Tyle na początek. W następnym artykule z tej serii postaram się odpowiedzieć na pytanie co dokładnie wpływa na prędkość działania frameworka.

 
Tagi: wydajność, apache benchmark
Komentarze
greg606 7 April 2010
Bardzo dobry i przydatny artykuł!
matipl 7 May 2010
AB okłamuje wyniki. Lepszym rozwiązaniem jest Siege, jeśli test ma być rzetelny.

Zupełnie nie podzielam zdania @greg606.
Gdzie dowód tezy "niekwestionowanym królem pośród frameworkowych żółwi jest Zend Framework" ????
jaras 7 May 2010
nie rozumiem co ma temat wpisu do jego treści. Jedyne co pokazujesz to instalacje podstawowych narzędzi profilujących i wywolanie jakiejś aplikacji z kosmosu ( nie pokazałeś nawet źrodła), gdzie dominujacą biblioteką jest doctrine, którego obsługa nie jest zaimplementowana w ZF (używa Zend_Db, wlasnego ORM).

Ponadto gdzie jakiekolwiek przyrównanie do innych frameworków PHP?

Wspomne jeszcze, że nic nie napisałeś o infrstrukturze aplikacji, która testujesz. Program napisany w php wymaga do działania, przynamniej serwera www, o parametrach którego nic nie napisales, nie mowiąc już o DB i samej maszynie.

Na jakiej podstawie mam wyciągnać wnioski.
Mateusz 7 May 2010
@matipl
Napisałem że opinia o powolności Zenda pochodzi "z internetu". Oto jedno ze źródeł gdzie można takie coś wyczytać:

http://symfony-reloaded.org/fast

Kiedy twoja aplikacja potrzebuje 2 sekundy na wygenerowanie prostej podstrony na całkiem przyzwoitym sprzęcie (no dobra, pod Vistą :-)...) okazuje się że takie opinie nie są bezpodstawne.

Inna sprawa że nie da się obiektywnie porównać wydajności frameworków, a przeprowadzanie benchmarków bezpośrednio mierzących prędkość nie ma większego sensu:

http://blog.astrumfutura.com/archives/421-PHP-Framework-Benchmarks-Entertaining-But-Ultimately-Useless.html

Jak pisze autor w/w artykułu, istnieje dobry sposób który pozwala na uzyskanie poprawy wydajności ZF, i zamierzam go opisać w trzeciej części tego artykułu.

Zatem skąd biorą się takie twierdzenia? Przyczyną jest między innymi podejście twórców ZF do swojego produktu. Na przykład zobacz w jaki sposób ładowane są różnego rodzaju view helpery czy walidatory - w rozbudowanych aplikacjach loader musi przeszukać kilka ścieżek aby odnaleźć dany plik. Bardzo ładnie widać to w profilerze. Ten temat także rozwinę później. Chociaż zdanie do którego się odnosisz poprawię, dlatego że może kogoś wprowadzić w błąd.

@jaras
Czytaj proszę ze zrozumieniem. Celem tego artykułu było wprowadzenie do tematu - dokładnie tak jak napisałeś, pobieżny opis kilku narzędzi i PRZYKŁAD działania. Nie też mam zamiaru niczego udowadniać porównując frameworki (patrz link wyżej). W następnych art. spróbuję pokazać skąd bierze się taka a nie inna prędkość działania, oraz co można zrobić aby ją zwiększyć. Wtedy też pojawi się informacja o środowisku testowym.
batman 7 May 2010
Fajnie, że ktoś zabrał się za wydajność ZF. Nie ma co ukrywać - jest to wolna kobyła. Nie podoba mi się jedynie fakt, że nie testujesz czystego ZF. Najwięcej wywołań oraz najdłuższy czas pochodzi z komponentu zupełnie oderwanego od ZF - Doctrine. Obawiam się, że wynik testów zostanie mocno przez to wypaczony.
Mateusz 7 May 2010
Ale przecież to jest tylko przykład użycia Webgrinda!
Następnym razem - tam gdzie będę mówił o konkretnych liczbach - będzie tylko ZF. Optymalizacja Doctrine to zupełnie inna bajka.
matipl 7 June 2010
@Mateusz: bez jaj, strona konkurencji nie będzie obiektywna (chociazby z powodu niewiedzy o produkcie). Tym bardziej Symfony 2.0 które oficjalnie nie wyszło.

Poza tym co to za test pod Vistą. Problemu ładowania plików większego w obecnym PHP nie ma, ponieważ autoload jest poprawiony i pieknie smiga.
Rozwiązanie w Symfony 1.4 to tragedia.
Mateusz 7 June 2010
Szybkość działania ZF jest jeszcze wspominana przy okazji na blogu szefa projektu:

http://weierophinney.net/matthew/archives/241-State-of-Zend-Framework-2.0.html

Wynik pod Vistą to oczywiście skrajność. Test testem ale jeśli musisz tworzyć stronę pod tym systemem i czekać te 2s na każde przeładowanie to prędzej czy później zaczniesz się zastanawiać co jest nie tak. Komfort żaden :)
Ale po kilku zmianach udało mi się znacznie zmniejszyć ten czas.
matipl 7 June 2010
Aplikacja ZF, czy Symfony nigdy nie ładowała mi się 2 sek. Chyba że był to zgoła inny problem, np. na linii framework<>db.
matipl 7 June 2010
PS: Przydałoby się powiadamianie na e-mail o nowych komentarzach. Bo znudziło mi się trzymanie tej strony w zakładce.
Mateusz 7 June 2010
W ten sposób nabijasz mi odwiedziny :-)
Spoko, dodam opcję powiadomień.
Skomentuj artykuł

Nazwa użytkownika powinna składać się z conajmniej pięciu liter, cyfr i znaków podkreślenia. Powinna zaczynać się od litery.

Pole wymagane.
Adres e-mail nie zostanie pokazany publicznie.