Všechny své aplikace se snažím optimalizovat, jak nejlépe to jde. Občas se mi sice stane, že honba za rychlostí se změní přímo v posedlost, ale o pár dní a několik revertů později z mého snažení téměř vždy vzejde něco použitelného. PHP aplikace píšu optimalizované tak nějak „od pasu“ (a když ne přímo optimalizované, tak alespoň bez nějakých očividných bottlenecků), ale říkal jsem si, že by nebylo na škodu se i u nich pomocí nějakého profileru přesvědčit, zda se někde nedá vyždímat nějaká milisekunda k dobru.
Závod s časem
Slyšel jsem už tisíckrát moudra typu „Rychlost není důležitá. Důležité je, aby byl kód jednoduchý a přehledný“. To beru, za předpokladu, že se v kódu vrtá spousta lidí a je kladen důraz na srozumitelnost. Ale přehlednost a rychlost se přece nevylučuje. Když by někde mohl být polynomiální algoritmus, ale já tam mám exponenciální, tak je to prostě špatně, přehlednost nepřehlednost. A jelikož je mým denním chlebem správa Java Enterprise aplikací, je mi smutno vždycky, když vývojáři své počínání ospravedlňují slovy „Výpočetní výkon je levný“. Naštěstí je můj přístup přesně opačný. Nechci nechat uživatele čekat pět minut na vykreslení okna jen proto, že jsem líný aplikaci napsat pořádně. A totéž platí i u webových aplikací.
Xdebug + KCacheGrind
Hledal jsem tedy nějaký hezký a snadno použitelný PHP profiler. A našel jsem xdebug. Instaluje se jako klasické PHP rozšíření skrze PEAR/PECL. Na oficiálních stránkách má velmi slušně zpracovanou dokumentaci, takže nastavení profileru bylo otázkou dvou řádků v configu. Pak už zbývá jen restartovat HTTP server (resp. CGI/FastCGI) a může se vesele profilovat. Xdebugem vytvořené profily se pak dají otevřít a analyzovat v aplikaci zvané KCacheGrind. KCacheGrind zobrazí nasbíraná data v přehledných grafech a pavoucích a řekne vám, jak dlouho trvalo provádění té či oné funkce a odkud byla kolikrát volána. Obrázek vydá za tisíc slov.
Instalace a užití
Jak jsem již zmínil, samotný xdebug se instaluje jako PECL balíček
pecl install xdebug
Poté je třeba jej v php.ini nastavit jako Zend extenstion, nejlépe s absolutní cestou.
zend_extension="/usr/lib/php5/xdebug.so"
Restartujte FastCGI a pomocí funkce phpinfo() ověřte, že je rozšíření správně nahráno. V případě, že jste jej nahráli jako obyčejný PHP extension a ne jako Zend extension, bude křičet
XDEBUG NOT LOADED AS ZEND EXTENSION
Pokud je vše v pořádku, v tuto chvíli už vám funguje debugger. Místo strohých hlášek
Fatal error: Call to a member function fetch_assoc() on a non-object in /var/www/db/mysqli.php on line 127
Uvidíte krásný stack trace
PHP Fatal error: Call to a member function fetch_assoc() on a non-object in /var/www/db/mysqli.php on line 127
PHP Stack trace:
PHP 1. {main}() /var/www/index.php:0
PHP 2. Content::Load() /var/www/index.php:91
PHP 3. DB::LoadPreviewBlocks() /var/www/presentation/content.php:78
Po tomhle ale momentálně neprahnete, takže pokračujte s nastavováním profileru. Do php.ini přidejte
xdebug.profiler_enable = 1
xdebug.profiler_output_dir = "/var/www/xdebug/"
a opět restartujte FastCGI. Volba xdebug.profiler_output_dir udává, jak název napovídá, adresář do kterého budou profily ukládány.
Dalším krokem je stažení KCacheGrindu. Linuxoví uživatelé si jej mohou zkompilovat sami, uživatelé Windows pak najdou zkompilovanou binárku na SourceForge. No a pak už můžete vytvořený profil nahrát do KCacheGrindu a sprostě nadávat nad tím, kterej pitomec vymyslel ten padesátkrát vnořenej cyklus.