Профилирование кода. Основы.

Избалованные современными технологиями и уровнем технического прогресса, многие разработчики не используют в своей работе такой принцип как профилирование кода. Суть его заключается в поиске проблемных, “узких” мест в коде программы и их ликвидация.

Если действие узких мест в малых проектах, рассчитанных на простой функционал или на малую аудиторию, не особо заметно (порой вообще нет никаких видимых проявлений), то с увеличением нагрузки падение эффективности может иметь чуть ли не экспоненциальный характер! И решить проблему на стадии завершения работы над проектом гораздо сложнее (а иногда и идеологически невозможно), нежели на стадии проектирования и первичной реализации.

Далее я буду рассматривать 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, чтобы вынести его из циклов и тем самым уменьшить временные издержки, связанные с процессом профилирования.

На этом я заканчиваю подавать Вам основы, остальное - дело других статей. =) Вопросы приветствуются!

Комментариев: 17

  1. Stac пишет:

    Отличная заметка, Павел!
    Вот такой стиль подачи мне нравится: одна статья - одна идея и четкое соответствие заголовку.

    Вопрос у меня такой: Нужно ли делать профилирование только на стадии разработки или продолжать после запуска проекта в production (например, для мониторинга изменений в работе скриптов с ростом объема БД или числа посетителей)?

  2. Павел Воронин пишет:

    =) Спасибо за тёплое слово, Слава.

    Отвечаю по вопросу: профилирование нужно стараться делать на стадии разработки, готовя продукт к выпуску. Для этого использовать искусственную нагрузку, моделировать разные стрессовые ситуации… Но, как ты верно подметил, моя статья соответствует заголовку: “… Основы.”, а моделирование ситуаций не подходит под понятие основы. =)

    На стадии production разработчик, как правило, рассматривает рекламации и исправляет ошибки, добавляет недостающие функции. Профилирование - это инструмент, он создан (сформулирован скорее) для упрощения жизни разработчику. Если последнему удобно использовать этот инструмент (и он умеет это делать!), то это нужно делать. Вот чего не стоит делать, так это профилировать ради профилирования.

  3. Stac пишет:

    Что я имел в виду под провилированием в стадии production (может быть тут нужно вместо профилирования говорить о мониторинге): по всей программе расставлены вызовы твоего класса, который сечет время выполнения разных кусков кода.
    разумно предположить, что эти времена будут лежать в каких-то определенных диапазонах на стадии production.
    Это значит, что все работает нормально. А если какое-то время выходит за пределы “стандартного” диапазона - “видимо, что-то случилось…” - пора подавать сигнал админу.

    Есть ли в этом смысл?

  4. Павел Воронин пишет:

    А! =) Интересную ты затронул тему. На самом деле, это на моей памяти не применялось, но я тоже задумывался об этом.

    Тут вот в чём дело: каждый из этих вызовов забирает некоторую (учитывая объектную модель PHP, довольно большую) часть памяти и процессорного времени себе. Как я уже писал, такая ситуация может быть незаметна при разовом выполнении скрипта, но при многократном возрастании нагрузки (переход в стадию production) может быть весьма ощутимо и заметно падение эффективности системы.

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

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

  5. Stac пишет:

    Ну а если реализовать это по другому, не применяя специального класса, а скажем, заставить определенные функции движка писать в файл какие-то коды, временные штампы. Этот файл будт анализироваться другой программой на том же или другом сервере.

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

    Я плавно подвожк тебя к теме автоматического мониторинга и восстановления после сбоев. Знаешь что-то про это?

  6. Павел Воронин пишет:

    Знать-то знаю. ) И намёк твой уже понял… Будет статья. =)

  7. Иван пишет:

    Большое спасибо автору , действительно полезная статья!

  8. Павел Воронин пишет:

    Приятно слышать такие отзывы. =) Значит, не зря я трудился. Спасибо Вам!

  9. Мустафа пишет:

    Не хочется казаться “как все”, но не выразить свое благодарство я просто не могу! Спасибо, статья бесценная!

  10. Павел Воронин пишет:

    Удивлён реакцией общества. =) А я и не знал, что среди меня читающих столько людей, интересующихся данной тематикой.

  11. Тень пишет:

    Отличная статья, продолжайте в том же духе!

  12. Дубровский пишет:

    Спасибо за статью!
    А как сложилось судьба профайлера? По ссылке - пустота :( А было бы интересно поглядеть.

  13. Павел Воронин пишет:

    К сожалению, Тот Самый Класс не выжил, а новый я не закачал. Если сегодня найду какой-нибудь, то обязательно выложу. =)

  14. Дубровский пишет:

    Павел, спасибо!
    Надеюсь, что найдете - мне очень интересно. Примитивный таймер уже не удовлетворяет, собрался делать свой профайлер, но жалко времени на велосипед :)

  15. Павел Воронин пишет:

    Нашёлся. =) Теперь он доступен по ссылке, указанной в статье. Он не является законченным профессиональным инструментом, был написан специально для статьи, чтобы продемонстрировать возможности технологии. Для профессионального использования нужно писать свой профайлер, либо обратиться к каким-то продуктам.

  16. Дубровский пишет:

    Спасибо!
    Да, теперь понял Вашу мысль (в описание класса не стал вникать, не видя кода). Это не совсем то, что мне нужно - такой подход интересно использовать для бенчмарков, а мне нужно препарировать готовое работающее приложение.

    Ну, собственно, в гугле ничего пристойного (и не слишком монстроподобного) не нашлось, так что буду писать свой, обещаю поделиться результатом ;)

    Сейчас я использую что-то в этом духе (сорри, без комментариев). Для начала добавлю туда иерархию вызовов, а там - как фантазия разгуляется :)

  17. Павел Воронин пишет:

    С удовольствием посмотрю на Ваш вариант. =)

Оставьте свой отзыв!

Спамерам: подумайте, нужно ли Вам трудиться? Nofollow и noindex не спят, а Akismet отстрелил 33,129 спам-комментов.