Обо мне
- Веб-разработчик из Киева
- Core разработчик PHP-фреймворка Yii 2
- Активный член Open Source сообщества
Не написал ни одного проекта на
Сегодня поговорим о том:
- Что такое профилирование и зачем им заниматься
- Как и чем собирать профайлы
- Как их смотреть и анализировать
- Распространённые медленные места в коде
- Как поддерживать производительность
Зачем оптимизировать, если и так работает быстро?
Быстро?
А насколько быстро?
Полсекунды - это быстро, или не очень?
«Все в мире относительно»
Альберт Эйнштейн
Зачем пытаться оптимизировать?
- более быстрый отклик
- больше RPS
- экономия ресурсов → денег
Профилирование
— это сбор характеристик работы программы, используемый
для анализа ее быстродействия и потребления ресурсов с целью дальнейшей оптимизации
Профайлеры дают возможность оценить:
- где именно
- на какие операции
- в каких объемах
тратятся ресурсы при выполнении программы
Я и так догадываюсь, от чего тупняки.
Ща поправим.
Вряд ли. Оптимизация цельного приложения без
профилирования – это догадки и тыканье пальцем в небо.
Какие характеристики могут интересовать?
- время выполнения
- процессорное время
- расход оперативной памяти
- время дисковых операций (IO)
- сетевые операции
- ожидание отклика СУБД*
А как оно работает?
- Профайлер — это программа на C, подключаемая как расширение к PHP
- Запускается по требованию или на каждый запрос, и всё считает
- Записывает подсчитанное в специальной лог профилирования
Чем можно профилировать PHP?
- Самим PHP
- XDebug
- XHprof
- Blackfire
Самим PHP
$start = microtime(true);
$this->check();
$time = microtime(true) - $start;
Не профайлер.
Отладчик с функцией профилирования
Почему нет?
- огромный overhead (время выполнения программы умножается на 2+)
- оптимизация под XDebug на 90% безрезультатна после его отключения
- нельзя включать на проде
Вы всё еще дебажите принтами?
Must have для отладки
Поддерживается в:
Пошаговая отладка
Stacktrace
Просмотр переменных, доступных в текущей области видимости
Преимущества:
- минимальный overhead (~10%)
- можно включать на проде
Недостатки:
-
официально не поддерживает PHP 7.
Но есть форки с поддержкой:
- умеет считать только общее время, процессорное время и память, хотя этого обычно достаточно
Начать — легко!
Законфижить в php.ini
[xhprof]
extension=xhprof.so
xhprof.output_dir="/tmp/xhprof"
Включить в точке входа
<?php
xhprof_enable(XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY);
(new Application())->run();
$xhprof_data = xhprof_disable();
xhprof_disable() возвращает массив вызовов
Каждый вызов описан так:
["yii\base\Module::runAction==>yii\base\Controller::runAction"] =>
array(5) {
["ct"] => int(1)
["wt"] => int(6718)
["cpu"] => int(6720)
["mu"] => int(617624)
["pmu"] => int(672904)
}
- ct – количество вызовов функции
- wt – реальное время выполнения, микросекунд
- cpu – процессорное время, микросекунд
- mu – потребление памяти, байт
- pmu – пиковое потребление памяти, байт
И чё с этим всем делать?
Сохранять
<?php
xhprof_enable(XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY);
(new Application())->run();
$xhprof_data = xhprof_disable();
$XHPROF_LIB_ROOT = "/opt/xhprof/xhprof_lib";
include_once $XHPROF_LIB_ROOT . "/utils/xhprof_lib.php";
include_once $XHPROF_LIB_ROOT . "/utils/xhprof_runs.php";
$xhprof_runs = new XHProfRuns_Default();
$run_id = $xhprof_runs->save_run($xhprof_data, 'my_application');
Эти PHP файлики можно выкачать из
репозитория XHprof
и положить в проект, чтобы не зависить от окружения. А еще их можно модифицировать*
А как смотреть?
Берем директории xhprof_lib и xhprof_html из того-же
репозитория,
кладём их рядом, и поднимаем веб-сервер, указывая root в xhprof_html.
Зная ID профайлов, можно сравнить два запуска:
http://xhprof.localhost/index.php?run1=__ID1__&run2=__ID2__
Эм... Как-то не круто
Можно что-то красивенькое?
Конечно! Ставим graphviz:
apt-get install graphviz
Нажимаем на ссылку [View Full Callgraph]
на странице просмотра профайла и получаем...
Спокойно! Разбираемся:
- Название класса и метода
- Общее время выполнения с учетом вызовов зависимостей и процент от общего времени выполнения скрипта
- Общее время выполнения без учёта зависимостей и тот же процент
- Количество обращений к данному методу
Окей, а можно еще информативнее?
Рисовалка собирает блоки на PHP, можно немного поковырять и добавить вывод нужной инфы, вроде CPU или RAM
Blackfire
- Начинался как форк XHprof, но был серьезно переделан
- Занимаются ребята из Sensio Labs
- Нет overhead'a
- Можно включать на проде
- Поддерживатеся Linux, FreeBSD, Windows, MacOS
- Работает на PHP 7
- Условно бесплатный. Бесплатно: одно приложение, без сети и SQL. Все возможности стоят от 82,50$/месяц
- Очень толковая документация
Blackfire состоит из
- Сайта для просмотра графиков
- Расширения PHP
- Агента для отправки данных
- Расширения для Chrome
- Инструментов командной строки
Включается только по тригеру
Сравнение профайлов
Как я добился ускорения в 10 раз?
Выключил XDebug
У Blackfire есть еще много крутых фишек
Научились рисовать красоту, но что с ней делать?
Анализировать!
Что профилировать в первую очередь?
- Инициализация приложения
- Типовые страницы модулей
- То, что долго отдаётся
Что чаще всего тормозит?
- Плохие/сложные алгоритмы
- Вложенные циклы
- Дисковые операции
- Плохие SQL
- Взаимодействие с внешним миром
- Вызовы консольных команд
- Слишком много объектов (100К+)
Код может взлететь!
Плохие/сложные алгоритмы
- профилировать и улучшать
- искать альтернативные подходы
- кешировать (Memcache, Redis)
Дисковые операции
- Отложенная загрузка (lazy loading)
- SSD
- OpCache
- Memcache, Redis
Плохие SQL
- Оптимизировать запросы (смотреть EXPLAIN)
- Индексы
- Тюнить СУБД
- Не делать сложных запросов
Взаимодействие с внешним миром
- Рано или поздно всё упадёт
- Асинхронность
- Очереди (AMQP, RabbitMQ)
Вызовы консольных команд
- Делать асинхронными
- Минимизировать
- Кешировать
Слишком много объектов (100К+)
- Скорее всего это не нужно
- Искать более элегантный вариант
Профайлеры на проде
- Используйте тригеры для активации
- Помните, что профайлы занимают место
Нужно стараться
- не забывать об архитектуре и чистоте кода
- избегать божественных методов и классов
- помнить о сложности алгоритмов и операций
- не заниматься преждевременной оптимизацией
А еще можно:
- Обновится до PHP 7 (до +40%)
- Настроить кеш (Memcache, Redis)
- Потюнить OpCache (не должно быть misses)