Профилирование

Материал из Eludia
Перейти к: навигация, поиск

Содержание

В Eludia.pm предусмотрены встроенные средства профилирования: при анализе производительности приложения довольно просто получить информацию о том, какие фрагменты кода вызывают торможение, а какие даже бессмысленно оптимизировать.

До 21.10.2010 данная функциональность сводилась к тому, что при усстановленном значении $preconf -> {core_debug_profiling} в STDERR выводилась отладочная информация строго установленного формата.

В актуальной версии реализована событийная модель профилирования, соответствующий открытый API и средство динамической конфигурации обработчиков событий. По умолчанию выдаётся примерно такая же информация, как прежде.

Ниже описано, как использовать этот новый механизм.

Секции и их типы

По ходу вычислений с использованием Eludia.pm (которые практически полностью сводятся либо к обработке HTTP-запросов, либо к исполнению offline- и Update-скриптов) выделяются некоторые фрагменты кода, которые можно назвать секциями профилирования. Типичный пример: доставание данных из БД заданным запросом.

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

Каждая секция имеет тип. Это строка, состоящая из натурального числа идентификаторов (\w+), соединённых символом '.'. Например: 'core.sql.reconnect'.

Имена типов естественным образом образуют иерархию, однако она не имеет никакого отношения к вложенности секций во время исполнения. Например, секция core.sql.reconnect исполняется не в рамках секции core.sql (которой вообще не бывает), а в рамках секции handler.prelude.

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

События

С каждой секцией профилирования (точнее, с каждым её запуском) связано 2 события: начало и окончание. Обозначаются эти события, соответственно, вызовами API-функций:

__profile_in  ('my.profiling.section.type' => {...}); 
 ... # профилируемый код
__profile_out ('my.profiling.section.type' => {...}); 

Закрывать секции надо обязательно. Если по ходу исполнения профилируемого кода возможен ранний выход наверх (скажем, return), то нужно изолировать этот фрагмент в eval или переписать его, явно отложив return после __profile_out.

1-й аргумент __profile_in и __profile_out — имя типа секции. Он, естественно, должен совпадать для in и out.

2-й аргумент __profile_in и __profile_out — набор опций. Туда можно писать произвольную информацию, связанную с данным запуском секции. Крайне желательно при этом не использовать ключи, начинающиеся с '__' — поскольку ядро приписывает туда собственную информацию по таким ключам.

Действие функций __profile_in и __profile_out сводится к 3 вещам:

  • обслуживание стека;
  • уточнение опций;
  • вызов обработчиков.

Стек профайлера

Поскольку секции могут вкладываться друг в друга, но не пересекаться, в любой момент времени имеет смысл список, точнее, стек in-событий, которые пока не закрыты соответствующими out-событиями. Это глобальная переменная в пакете приложения, и называется она @_PROFILING_STACK.

Там откладываются наборы опций, переданные __profile_in. В момент постановки секции на учёт __profile_in приписывает к тому, что передано, 3 собственных элемента:

__time
текущее время;
__type
тип секции;
__level
уровень вложенности.

Со своей сотроны, __profile_out извлекает запомненное состояние и вычисляет для собственного набора опций:

__duration
время исполнения;
__details
хэш, где для типа каждой вложенной секции приводится суммарное время исполнения.

Уточнив аргументы, __profile_in и __profile_out вызывают обработчик нужного события.

Обработчики событий

Как уже отмечалось выше, Eludia.pm давным-давно поддерживала опцию конфигурации $preconf -> {core_debug_profiling}, значение которой подразумевалось скалярным. Сейчас там ожидается ссылка на хэш с конфигурацией обработчиков, который имеет примерно следующий вид:

core_debug_profiling => {
                 => [, 'print_tree'],
 'require'         => [],
 'sql'             => [],
 'draw'            => [],
 'handler.request' => [' ', ['print_tree', 'print_details']],
},

Ключами, как несложно убедиться, являются имена типов секций. Эти имена могут быть указаны не полностью: например, элемент 'draw' устанавливает обработчики для 'draw.table', 'draw.form' и т. д., если для более детального типа нет собственного определения. Элемент задаёт глобальный обработчик по умолчанию.

Значения представляют собой ссылки на массивы из 2 элементов (возможно, пустых). 0-й элемент соответствует событию in, 1-й — событию out. Каждый из этих элементов — либо строка, либо список строк. Для каждой строки $name будет вызван обработчик "__profile_$name" из текущего пакета приложения. Для списка строк — несколько обработчиков.

Обработчик in-события вызывается с параметрами ($type, 0, $options).

Обработчик out-события вызывается с параметрами ($type, 1, $old_options, $new_options).

В ядре на данный момент реализовано 2 стандартных обработчика, оба для out-событий:

__profile_print_tree
для иерархического вывода времён исполнения каждой секции;
__profile_print_details
для составления отчёта "чистого" времени по типам секций.

Как этим пользоваться?

Для начала можно по-старинке включить

core_debug_profiling => 1,

в httpd.conf. По новым правилам это равносильно

core_debug_profiling => {
                 => [' ', 'print_tree'],
},

то есть в конце каждой секции будет печататься строка отладки, учитывающая уровень вложенности секции.

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

 'require'         => [],
 'sql'             => [],
 'draw'            => [],

Не исключено, что Вам покажется удобным получать после каждого запроса сводку по чистому времени:

 'handler.request' => [' ', ['print_tree', 'print_details']],

И, наконец, Вы можете самостоятельно написать обработчик и назначить его на заданный тип событий. К примеру, заказать себе извещение по e-mail для каждого слишком длинного SQL-запроса или запись отладочной информации в стороннюю БД.

Кроме того, можно вызывать _profile_in / _profile_out внутри приложения и, таким образом, определять собственные секции профилирования.

Типы секций профилирования

call....
вызов callback-функции приложения, такой, как select_users, draw_item_of_user и т. п.;
core.gzip
сжатие данных;
core.memory
измерение объёма памяти, занятого текущим процессом;
core.out_html
передача содержимого на клиент;
core.sql.connect
установление связи с БД;
core.sql.reconnect
проверка связи с БД (и, если она недоступна, то вызов core.sql.connect);
draw.form
draw_form;
draw.table
draw_table;
draw.tree
draw_tree;
handler.action
реализация действия;
handler.prelude
выяснение, требуется ли аутентификация и если нет, то обслуживание специальных типов запросов;
handler.request
обслуживание HTTP-запроса в целом;
handler.setup_page
подготовка данных о пользователе, текущем разделе приложения и видимом меню;
handler.setup_user
подготовка данных о пользователе;
handler.showing
извлечение и отрисовка данных для экрана, не модифицирующего данные;
require.config
обновление конфигурации приложения;
require.file
обновление сведений о файле с исходными текстами;
require.model
обновление схемы данных;
require.module
обновление исходных текстов для заданного типа экрана;
sql.execute
исполнение SQL-запроса (пока только для Oracle);
sql.fetch
извлечение результатов SQL-запроса (пока только для Oracle);
sql.prepare
подготовка SQL-запроса (пока только для Oracle).
Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Разработчику
Администратору
Инструменты