Это небольшая инструкция о том, как выяснить узкое место в программе, которое долго обрабатывается.
Заходим на медленную страницу, открываем инструменты разработчика (я использую chrome) и видим примерно следующую картину:
Это означает что загружаемый ресурс загружается очень медленно. Причин этому может быть много. Напимер сам файл очень большого размера. Но в данном случе это старничка html, а время загрузки большое из-за того, что скрипт, генерирующий ее очень долго работает.
В писке узкого места поможет xdebug. Установим есго:
pecl install xdebug
Можно так же установить в ручную:
wget xdebug.org/link.php?url=xdebug201
tar -xzf xdebug-2.0.1.tgz
cd xdebug-2.0.1
phpize ./configure --enable-xdebug --with-php-config=/usr/bin/php-config
make
cp modules/xdebug.so /usr/lib/apache2/modules/xdebug.so
После установки необходимо отредактировать файл php.ini
xdebug.profiler_enable = On
xdebug.profiler_enable_trigger = On
xdebug.profiler_output_dir='/var/www/html'
xdebug.profiler_output_name='%h_%r'
В конфигурации мы включили профилировщик, указали в какой папке и с каким именем собирать дампы.
Теперь нам понадобится программа для просмотра дампов. Для linux это KCachegrind. Можно найти в своем репозитории. Пользователям windows надо помучить гугл с поиском программы WinCacheGrind
Теперь, когда все приготовления сделаны, можно перезагрузить страницу с долгим скриптом. В папке, указанной в параметре xdebug.profiler_output_dir должен появиться файл с дампом, который надо открыть в KCachegrind
На скриншоте видно что блок main вызывает 3 функции. На самом деле больше, но показаны самые нагруженные. У блоков с вызываемыми функциями в процентах указано кто сколько нагружает. В данном примере видно что дольше всего отрабатывает функция CIBlockElement::GetList. Это вызов API Битрикса, для получения списка элементов инфоблока. Откроем на редактирование страницу и обычным поиском найдем где идет вызов данной функции.
Исследуемая страница содержит вывод списка пользователей Битрикса. Но помимо таблицы пользователей, в системе имелась сущьность, связанная с пользователем. Медленный код выглядел так:
<?while($rsUsers->NavNext(true, "f_")) :?>
<?
$user = [
'rid'=>$f_UF_EXT_ID,
'name'=>$f_LAST_NAME.' '.$f_NAME,
];
$votes = CIBlockElement::GetList(
($by = "ID"),
array('PROPERTY_C'=>$f_ID,'IBLOCK_ID'=>13),
false,
false,
array('ID', 'PROPERTY_CANCELED', 'PROPERTY_c')
)->fetch();
if($votes['PROPERTY_CANCELED_ENUM_ID'] == 3){
$list1[] = $user;
}else{
$list2[] = $user;
}
?>
<?endwhile;?>
Как видно из кода, функция CIBlockElement::GetList вызывается в цикле. И если $rsUsers содержит в себе пару пользователей, то ничего страшного не произойдет. В моем же случае переменная содержала много записей (чуть меньше тысячи). Если проанализировать код, то можно понять, что выборка нужна для сортировки пользователей по двум массивам, в зависимости от параметров, хранящихся в другом инфоблоке.
Данный пример я считаю огромным камнем в огород Битрикса, который не умеет по другому работать со связями в моделях. Для начала я попробовал вынести результат выборки медленной функции в статический массив. Результаты не заставили себя ждать:
время генерации снизилось примерно в 3 раза! Значит дело за нормализацией базы данных. У нас есть инфоблок с параметрами пользователя и таблица пользователей. Если добавить пользователю дополнительное поле и фильтровать по нему, то эффект будет таким же, за исключением того, что снизится расход ОЗУ
<?while($usr = $rsUsers->NavNext(true, "f_"))) :?>
<?
$user = [
'rid'=>$f_UF_EXT_ID,
'name'=>$а_LAST_NAME.' '.$а_NAME,
];
if($f_UF_С){
$list1[