Поиск узких мест php

Инструкция по поиску узких мест и ускорению

Для постоянного «живого» мониторинга скорости ответа страницы можно использовать следующий скрипт:

while true; do; curl -s -w %{time_total}\\n -o /dev/null http://site.ru/ ; done

Включение кеширования PHP

Обычно PHP идет с ускорителем типа XCache , eAccelerator , APC , или OPcache . Актуально — OPcache , он стал частью PHP с версии 5.5, другие похоже, что «загнулись».

Какой именно стоит можно из вывода phpinfo() или php -v :

PHP 5.3.3 (cli) (built: Jul 12 2013 20:35:47) Copyright (c) 1997-2010 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies with XCache v3.0.2, Copyright (c) 2005-2013, by mOo with Zend Guard Loader v3.3, Copyright (c) 1998-2010, by Zend Technologies with XCache Optimizer v3.0.2, Copyright (c) 2005-2013, by mOo with XCache Cacher v3.0.2, Copyright (c) 2005-2013, by mOo with XCache Coverager v3.0.2, Copyright (c) 2005-2013, by mOo

OPcache

Настройки — в php.ini , а также доступны через opcache_get_configuration() :

[opcache] ; 1 - включен opcache.enable=1 ; размер в Мб opcache.memory_consumption=256 ; максимальное количество файлов в кеше opcache.max_accelerated_files=20000 ; проверять изменения по временной метке, 1 - проверять, 0 - быстро, не проверять, но в случае "ошибок" требуется перезапустить кеш `opcache_reset()` или веб-сервер opcache.validate_timestamps=1 ; как часто (в секундах) делать перепроверку временной метки, 0 - постоянно, 1 - 1 раз в секунду, но не более 1 раза за запрос opcache.revalidate_freq=2 opcache.force_restart_timeout=180 ; в конфиге еще много интересных настроек

Статистика OPcache доступна через phpinfo() , opcache_get_status() :

Читайте также:  Check for iterable python

Статистика OPcache

XCache

Надо убедиться что XCache включен и настроен в файле xcache.ini : xcache.size должен быть > 0, размер подбирается опытным путем.

Для просмотра состояния кеша следует:

[xcache.admin] xcache.admin.enable_auth = On xcache.admin.auth = On xcache.admin.user = "admin" ; set xcache.admin.pass = md5($your_password) ; login use $your_password xcache.admin.pass = "5f4dcc3b5aa765d61d8327deb882cf99"

Сделать симлинк в дереве веб-документов xcache_html -> /usr/share/xcache .

php_admin_value open_basedir "/usr/share/pear:/etc/xcache/:/usr/share/xcache:/var/www/site.ru/:."

Источник

Нестандартная оптимизация проектов на PHP

В данной публикации хочу рассказать о том, как нетрадиционными методами мы смогли снизить нагрузку на серверы и ускорить время обработки страницы в несколько раз.

image

  • Оптимизация SQL-запросов;
  • Поиск и исправление узких мест;
  • Переход на Memcache для часто используемых данных;
  • Установка APC, XCache и подобных;
  • Клиентская оптимизация: CSS спрайты и т.п.

В нашем же проекте всё это было сделано, но при этом проблема скорости обработки страниц сохранялась. Средняя скорость обработки страницы была в районе 500мс. В один прекрасный момент пришла идея проанализировать, какие ресурсы есть, и на что они могут тратиться.

После анализа были выявлены следующие основные ресурсы, которые нужно мониторить:

  1. Процессорное время;
  2. Оперативная память;
  3. Время ожидания других ресурсов (MySQL, memcache);
  4. Время ожидания диска.

Дальше начался поиск узких мест.

Этап 1

Первое, что было испробовано — профайлинг MySQL запросов, поиск медленных запросов. Подход не принёс особого успеха: несколько запросов было оптимизировано, но среднее время обработки страниц не сильно изменилось.

Этап 2

Дальше была попытка профайлинга кода с помощью XHProf. Это дало ускорение в узких местах и смогло снизить нагрузку примерно на 10-15%. Но основной проблемы это тоже не решило. (Если будет интересно, могу отдельно написать статью о том, как оптимизировать с помощью XHProf. Вы только дайте знать в комментариях.)

Этап 3

В третьем этапе пришла мысль посмотреть, сколько памяти уходит на обработку запроса. Оказалось, что в этом и есть проблема – простой запрос может требовать загрузить до 20мб кода в ОЗУ. Причём причина этого была непонятна, так как идёт простая загрузка страницы — без запросов в базу данных, или загрузки больших файлов.

Было принято решение написать анализатор и выяснить, сколько памяти требует каждый пхп файл при его включении.

Анализатор очень простой: в проекте уже был автозагрузчик файлов, который на основе имени класса сам подгружал нужный файл (autoload ). В нём просто было добавлено 2 строки: сколько памяти было до загрузки файла, сколько стало после.

Profiler::startLoadFile($fileName); Include $fileName; Profiler::endLoadFile($fileName); 

Первая строка сохраняет, сколько памяти было в начале, последняя строка – вычитает разницу и добавляет в список загрузок. Так же профайлер сохраняет очередь загрузки файла (backtrack).

В конце выполнения всего кода мы добавили вывод панели с собранной информацией.

Анализ показал очень интересные причины того, почему может использоваться излишняя память. После того, как мы проанализировали все страницы и убрали загрузку лишних файлов страница стала требовать менее 8мб памяти. Обновление страницы стало быстрее, нагрузка на сервера уменьшилась и на тех же машинах появилась возможность обрабатывать больше клиентов.

Дальше идёт список вещей, которые мы изменили. Здесь стоит отметить, что сам функционал не изменился. Изменилась только структура кода.

Все примеры сделаны специально упрощёнными, так как предоставить оригинальный код проекта возможности нет.

Пример 1: Загрузка, но не использование больших родительских классов

class SysPage extends Page < static function helloWorld()< echo "Hello World"; >> 

Файл сам по себе очень маленький, но при этом, он тянет за собой по крайней мере один другой файл, который может быть достаточно большой.

  1. Убрать наследование где не нужно;
  2. Сделать чтобы все родительские классы были минимальные по размерам.

Пример 2: Использование длинных классов, хотя нужна только малая часть класса

Очень похоже на предыдущий пункт. Пример:

class Page < public static function isActive()<>// тут длинный код на 1000-2000 строк > 

Естественно, что PHP не знает, что вам нужна только 1 функция. Как только идёт обращение к данному классу, идёт подгрузка всего файла.

Решение:
Если есть подобные классы, то стоит выделить функционал который используется везде в отдельный класс. В текущем классе можно ссылаться на новый класс для совместимости.

Пример 3: Подгрузка большого класса только для констант

$CONFIG['PAGES'] = array( 'news' => Page::PAGE_TYPE1, 'about' => Page::PAGE_TYPE2, ); 

Т.е. пример похож на предыдущий, только теперь константа. Решение такое же как и в прошлом случае.

Пример 4: Авто создание в конструкторе классов, которые возможно и не будут использованы

class Action< public $handler; public function __construct()< $this->handler = new Handler(); > public function handle1()<> public function handle2()<> public function handle3()<> public function handle4()<> public function handle5()<> public function handle6()<> public function handle7()<> public function handle8()<> public function handle9()<> public function handle10()< $info = $this->handler->getInfo(); > > 

Если Handler используется часто, то проблемы нет. Совсем другой вопрос, если он используется только в 1 из функций из 20.

Решение:
Убираем из конструктора, переходим на ленивую загрузку через магический метод __get или же например так:

public function handle10()< $info = $this->handler()->getInfo(); > public function handler()< if($this->handler===null) $this->handler = new Handler(); return $this->handler; > 

Пример 5: Подгрузка ненужных языковых файлов / конфигов

Пример: У вас есть большой файл с настройками всех страниц всех пунктов меню. В файле может быть много массивов.

Если часть данных используется редко, а часть часто – то данный файл является кандидатом на разделение.

Т.е. стоит подгружать данные настройки только тогда, когда они нужны.

Пример использований памяти у нас:

Файл в 16 кб, просто массив данных – требует от 100 кб и выше.

Пример 6: Использование serialize/unserialize

Часть настроек, которые часто меняются, мы хранили в файле в формате serialize. Мы обнаружили, что это загружает как процессор, так и оперативную память, причём у нас получилось, что на PHP версии 5.3.х у функции unserialize очень сильная утечка памяти.

После оптимизации мы максимально избавились от этих функций где возможно. Данные в файлах мы решили хранить в виде массива, сохранённого через var_export и загружать обратно через с помощью include/require. Таким образом мы смогли задействовать APC режим.

К сожалению, для данных которые хранятся в memcache, данный подход не работает.

Итоги

Все данные примеры легко находятся и очень легко правятся не сильно меняя структуру проекта. Мы взяли правило: «любые файлы, которые требуют более 100кб, надо проверять на предмет можно ли оптимизировать их загрузку. Так же мы смотрели на ситуации, когда загружалось несколько файлов из одной ветки. В этой ситуации мы смотрели, можно ли вообще не загружать всю ветку файлов. Ключевая мысль была такой: «всё, что мы загружаем, должно иметь смысл. Если можно не загружать каким-либо способом – лучше не загружать.

Заключение

После того, как мы убрали всё, что выше, один запрос к серверу стал требовать примерно в 2 раза меньше оперативной памяти. Нам это дало уменьшение нагрузки процессора примерно в 2 раза без перенастройки серверов и уменьшение средней скорости загрузки одной страницы в несколько раз.

Дальнейшие планы

Если будет интересно, есть в планах идея описать способы профилирования кода и поиска узких мест с помощью XHprof.

Источник

PHP поиск «узких» мест в коде. Xdebug + WinCacheGrind

узкие места кода

Узкие места — это части кода, которые являются основными потребителями времени и ресурсов, во время своего выполнения.
Если с ресурсами все более или менее нормально, то со временем выполнения, на одном из моих ресурсов, определенно проблема. Главная страница формировалась порядка 1500ms, что не приемлемо.

Для заинтересованных, время отработки снизилось до 400ms, что допустимо для такого рода проектов.

Удалось найти несколько узких мест, связанных с ошибками программирования, причем в одном случае незначительная ошибка помноженная на многократный вызов процедуры (70-80 раз) серьезно повлияла на общее время.

Для работы нам понадобится расширение для php под названием xdebug. Не буду описывать процедуру установки, опишу необходимые нам параметры:

[xdebug] xdebug.profiler_enable=1 xdebug.extended_info=0 xdebug.remote_enable=0 xdebug.auto_trace=0 xdebug.profiler_output_dir="/tmp"
[xdebug] xdebug.profiler_enable=1 xdebug.extended_info=0 xdebug.remote_enable=0 xdebug.auto_trace=0 xdebug.profiler_output_dir=»/tmp»

После появления в phpinfo(); информации о расширении, мы создаем профиль тестируемого ресурса, путем запуска последнего (открытия сайта в браузере, можно несколько раз). В папке /tmp появятся дампы запуска, их мы будем скармливать WinCacheGrind.

WinCacheGrind это урезанный KCachegrind для Windows. Можно конечно установить web версию, но по удобствам работы, мне импонирует именно windows версия. Из функционала доступен просмотр вызовов функций, время выполнения и другие полезные плюшки.

WinCacheGrind пример работы

WinCacheGrind пример работы

Дальше дело техники. Перемещаемся по дереву, и находим кусок кода, который создает нам ненужную нагрузку.

Также рекомендую к прочтению статью про другие средства визуальной оценки производительности php приложений http://habrahabr.ru/post/75166/

Категория: Программирование

Источник

Оцените статью