Wydajność Zend Framework - cz. 1

by Mateusz Tymek — on Zend Framework, PHP

Head's up! This post was written back in 2010 and is very likely to contain outdated information.

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:

[email protected]:~> /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.


comments powered by Disqus