Профилирование программы

Задача
Есть фрагмент программы, и необходимо провести его исследование, чтобы определить время выполнения каждого оператора.

Решение
Для этого предназначен модуль PEAR Benchmark:

require 'Benchmark/Timer.php';
$timer =& new Benchmark_Timer(true);
$timer->start();
// некоторый установочный код
$timer->setMarker('setup');
// еще часть исполняемого кода
$timer->setMarker('middle');
// еще одна часть исполняемого кода
$timer->setmarker('done');
// и наконец последняя часть исполняемого кода
$timer->stop();
$timer->display();

Обсуждение
Вызов функции setMarker() записывает время. Метод display() выводит на печать список маркеров, время их установки и время, прошедшее с момента установки предыдущего маркера:

-------------------------------------------------------------
маркер время установки прошедшее время проценты
-------------------------------------------------------------
Start 1029433375.42507400 - 0.00%
-------------------------------------------------------------
setup 1029433375.42554800 0.00047397613525391 29.77%
-------------------------------------------------------------
middle 1029433375.42568700 0.00013899803161621 8.73%
-------------------------------------------------------------
done 1029433375.42582000 0.00013303756713867 8.36%
-------------------------------------------------------------
Stop 1029433375.42666600 0.00084602832794189 53.14%
-------------------------------------------------------------
total - 0.0015920400619507 100.00%
-------------------------------------------------------------

Модуль Benchmark также включает класс Benchmark_Iterate, позволяющий определить время многократного выполнения одной функции:

require 'Benchmark/Iterate.php';
$timer =& new Benchmark_Iterate;// простая функция определения времени выполнения
function use_preg($ar) {
for ($i = 0, $j = count($ar); $i < $j; $i++) {
if (preg_match('/gouda/',$ar[$i])) {
// it's gouda
}
}
}
// еще одна простая функция определения времени выполнения
function use_equals($ar) {
for ($i = 0, $j = count($ar); $i < $j; $i++) {
if ('gouda' == $ar[$i]) {
// it's gouda
}
}
}
// запускаем функцию use_preg() 1000 раз
$timer->run(1000,'use_preg',
array('gouda','swiss','gruyere','muenster','whiz'));
$results = $timer->get();
print "Mean execution time for use_preg(): $results[mean]\n";
// запускаем функцию use_equals() 1000 раз
$timer->run(1000,'use_equals',
array('gouda','swiss','gruyere','muenster','whiz'));
$results = $timer->get();
print "Mean execution time for use_equals(): $results[mean]\n";

Метод Benchmark_Iterate::get() возвращает ассоциативный массив.


Элемент mean этого массива содержит значение времени выполнения каждой итерации функции. Элемент iterations содержит количество итераций. Время выполнения каждой итерации хранится в элементе массива с целочисленным ключом. Например, время первой итерации находится в элементе $results[1], а время 37-й итерации находится в элементе $results[37].

Для автоматической записи времени, прошедшего после выполнения каждой строки программы, применяется конструкция declare с параметром ticks:

function profile($display = false) {
static $times;
switch ($display) {
case false:

// добавляем текущее время к списку записанных значений времени
$times[] = microtime();
break;
case true:

// возвращаем прошедшее время в микросекундах
$start = array_shift($times);$start_mt = explode(' ', $start);
$start_total = doubleval($start_mt[0]) + $start_mt[1];
foreach ($times as $stop) {
$stop_mt = explode(' ', $stop);
$stop_total = doubleval($stop_mt[0]) + $stop_mt[1];
$elapsed[] = $stop_total - $start_total;
}
unset($times);
return $elapsed;
break;
}
}
// регистрируем обработчик тактов
register_tick_function('profile');
// определяем время старта
profile();
// выполняем код, записывая время выполнения каждого оператора
declare (ticks = 1) {
foreach ($_SERVER['argv'] as $arg) {
print strlen($arg);
}
}
// печатаем прошедшее время
$i = 0;
foreach (profile(true) as $time) {
$i++;
print "Line $i: $time\n";
}

Параметр ticks позволяет многократно выполнять функцию для блока кода.


Число ticks показывает, сколько тактов должно пройти между вызовами функции, зарегистрированной с помощью register_tick_function().

В предыдущем примере мы регистрируем единственную функцию и выполняем функцию profile() для каждого оператора внутри блока declare. Если в массиве $_SERVER['argv'] два элемента, то функция profile() выполняется четыре раза: одно выполнение при каждом проходе цикла foreach и один раз при каждом выполнении строки print strlen($arg). Можно также определить вызов двух функций на каждые три оператора:

register_tick_function('profile');
register_tick_function('backup');
declare (ticks = 3) {
// code...
}

Можно также передать дополнительные параметры в зарегистрированные функции, которые могут быть методами объекта вместо обычных функций:

// передаем "parameter" в функцию profile()
register_tick_function('profile', 'parameter');
// вызываем $car->drive();
$car = new Vehicle;
register_tick_function(array($car, 'drive'));

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

Для удаления функции из списка тактовых функций надо вызвать функцию unregister_tick_function():
unregister_tick_function('profile');.



Оцените статью: (0 голосов)
0 5 0

Статьи из раздела PHP на эту тему:
Аутентификация, основанная на cookies
Буферизация вывода в броузер
Взаимодействие в рамках Apache
Идентификация различных броузеров
Настройка обработки ошибок

Вернуться в раздел: PHP / 8. Основы Web