Профилирование кода. Основы.
Избалованные современными технологиями и уровнем технического прогресса, многие разработчики не используют в своей работе такой принцип как профилирование кода. Суть его заключается в поиске проблемных, “узких” мест в коде программы и их ликвидация.
Если действие узких мест в малых проектах, рассчитанных на простой функционал или на малую аудиторию, не особо заметно (порой вообще нет никаких видимых проявлений), то с увеличением нагрузки падение эффективности может иметь чуть ли не экспоненциальный характер! И решить проблему на стадии завершения работы над проектом гораздо сложнее (а иногда и идеологически невозможно), нежели на стадии проектирования и первичной реализации.
Далее я буду рассматривать PHP, но всё это в равной степени годится практически для всех языков программирования. Обычно хостинговые компании устанавливают квоты на используемые ресурсы, нормы у всех разные. В принципе, серьёзные проекты должны крутиться на своих серверах, остальные же должны ориентироваться на квоты. Различают несколько типов узких мест в программах, рассмотрим основные.
Работа с памятью
Весьма понятное, но редко улавливаемое местечко. PHP - интерпретируемый язык, а все интерпретируемые языки жадные до ресурсов. На каждое обращение клиента к серверу выделяется некоторое количество памяти, которое используется для вычислений. Если неэффективно строить приложение, то не только увеличивается время выполнения, но и съедается кусочек общей казны - квоты. При единичных запросах это не критично, но если одновременно выполнение запросит большое количество пользователей, то существует риск опрокинуть сервер на спину.
Процессорные вычисления
Аналогично работе с памятью. Если скрипт занимает много процессорного времени, то сервер рискует захлебнуться и аварийно выпасть из рабочего состояния. Задачей разработчика является оптимальное распределение нагрузки между разными частями проекта. Также на этапе проектирования должны закладываться идеи масштабирования нагрузки между несколькими серверами. Но в рамках статьи это рассматриваться не будет.
Коммуникационные издержки
Если проект использует для своей работы другие сервисы или внешние источники данных, то следует учесть, что это ложится испытанием на каналы связи. Оптимальным является применение пакетных запросов данных везде, где это возможно. В этом случае сервер может серьёзно уменьшить время выполнения скрипта за счёт экономии на сеансах связи.
Работа с БД
Это и вправду узкое место с максимальным рейтингом! Малый процент разработчиков действительно эффективно программирует на SQL, что удручает. Самые распространённые ошибки - это, конечно же, использование JOIN-выражений. Зачастую неумело составленное SQL-предложение только замедлит работу и не принесёт желаемого эффекта. Также следует избегать множественных обращений к БД (к примеру, WordPress крайне неэффективно до некоторого времени обращался с БД - количество запросов для генерации одной страницы могло переваливать за несколько десятков!).
Я описал лишь основные проблемные места, но это список далеко не полный! Научиться избегать проблемных мест и использовать оптимальные алгоритмы можно только с опытом. Вот приобретением первого опыта мы сейчас и займёмся! =) Я набросал код очень простого Профайлера (класса, используемого для профилирования кода).
В простейшем случае профилирование заключается в открытии потока профилирования, установления временных меток в разных частях кода и вывода статистики по этим меткам. Этого, на самом деле, достаточно для довольно эффективного исследования кода. Стоит заметить, что профилирование кода накладывает одно не слишком значительное, но требующее полного понимания условие - любое профилирование как процесс измерения вносит свои временные издержки. Таким образом, ни один код никогда (никогда не говори “никогда”!) не будет совершенно точно профилирован. =) Я не слишком часто говорю слово “профилирование”? =)
Класс поддерживает неограниченное количество потоков профилирования, то есть Вы можете вести учёт сразу нескольких событий. Каждый поток может иметь неограниченное количество меток. Также доступно два типа простейшей статистики - время выполнения скрипта и средний размер каждого учтённого отрезка времени.
Доступные методы:
Profiler::Notch ( "имя_потока" );
Установка временной метки (засечка). Если не указано имя потока, то будет использован стандартный. Если поток не существует, то он создастся.
Profiler::Drop ( "имя_потока" )
Сброс потока в нулевое состояние (удаление, если быть точным). Если не указано имя потока, то будет удалён стандартный.
Profiler::Out ( "имя_потока" )
Вывод времени жизни потока (разница между значением последней и первой засечки).
Profiler::Average ( "имя_потока" )
Вывод усреднённого значения интервалов между засечками. Полезно и удобно использовать для циклов.
Пример использования класса:
Profiler::Notch ( );
for ( $i = 0; $i < 10; $i++ )
{
//~ некоторые вычисления
Profiler::Notch ( );
}
echo "Average: ";
Profiler::OutAverage ( );
echo "\n";
echo "Total : ";
Profiler::Out ( );
Класс можно совершенствовать и улучшать до бесконечности - это я с полной уверенностью заявляю! =) Это будет Вам домашним заданием. Например, добавить возможность указания количества итераций в метод ::Notch, чтобы вынести его из циклов и тем самым уменьшить временные издержки, связанные с процессом профилирования.
На этом я заканчиваю подавать Вам основы, остальное - дело других статей. =) Вопросы приветствуются!