Как узнать время выполнения скрипта на PHP. Время выполнения PHP скрипта

Приветствую Вас, уважаемые читатели блога webcodius! Итак, сразу к делу. Сначала отвечу на вторую часть вопроса: зачем же может понадобиться измерить время выполнения скрипта ?.При работе над отладкой веб-проектов важным фактором, который следует обязательно учитывать, является время выполнения скриптов. Этот параметр необходим чтобы понять насколько быстро и производительно работает тот или иной кусок кода. Определив время потраченное сервером на выполнение php скрипта, вы сможете сделать вывод: требуется ли оптимизация кода или нет. Далее рассмотрим как это сделать.

Для анализа производительности php-скриптов существуют различные расширения и библиотеки, например XDebug, которое может выполнять отладку скриптов и профилировать их по времени выполнения. Но для использования таких расширений требуется потратить время на их установку и настройку. Конечно для оптимизации работы крупных проектов со соложной архитектурой, большим количеством скриптов и миллионами строк кода лучше воспользоваться такими расширениями. Но если необходимо оптимизировать 3-4 скрипта с одной или двумя сотнями строк кода, то для измерения времени работы скрипта можно воспользоваться только средствами php.

Как в php определить время выполнения скрипта

Научись создавать профессиональные движки на PHP и MySQL

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

Если запустить скрипт, то результат будет примерно таким:

Время выполнения скрипта: 0,00059400000000001

Кратко, как это работает. С помощью функции microtime(true) запоминаем метку времени начала работы скрипта в переменную $start . Далее идет рабочий код, который выполняет какие-то действия. В данном примере просто вызывается функция usleep() , которая замедляет работу скрипта на задданное количество микросекунд. И затем получаем время окончания работы скрипта, запоминаем ее в переменную $end и вычисляем время выполнения кода путем вычитания $start из $end . В итоге получили время выполнения скрипта.

Теперь зная как определить в php время выполнения скрипта , вы можете приступать к оптимизации различных блоков кода. Желаю удачи и до встречи в следующих постах!

Сколько бы мы не использовали PHP, всё равно всплывают некоторые функции, о которых мы даже не слышали. Некоторые из них были бы нам очень полезны. Я создал небольшой список полезных функций, которые должны быть в арсенале каждого PHP программиста.

1. Создание функций с переменным числом аргументов

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

Но для начала, вспомним как мы создаём функции обычным образом:

// функция с двумя необязательными параметрами function foo($arg1 = "", $arg2 = "") { echo "arg1: $arg1\n"; echo "arg2: $arg2\n"; } foo("hello","world"); /* выведет: arg1: hello arg2: world */ foo(); /* выведет: arg1: arg2: */

Теперь посмотрим на то, как можно написать функцию с неограниченным количеством аргументов. Для этого будет использовать метод func_get_args() :

// не указываем аргументы function foo() { // возвращает массив, переданных аргументов $args = func_get_args(); foreach ($args as $k => $v) { echo "arg".($k+1).": $v\n"; } } foo(); /* ничего не выведет */ foo("hello"); /* выведет arg1: hello */ foo("hello", "world", "again"); /* выведет arg1: hello arg2: world arg3: again */

2. Используем Glob() для поиска файлов

Часто названия функций говорят сами за себя. Такого нельзя сказать о функции glob() .

Если не вдаваться в подробности, её функциональность схожа с методом scandir() . Она позволяет найти необходимый файл по шаблону:

// найти все php файлы $files = glob("*.php"); print_r($files); /* выведет: Array ( => phptest.php => pi.php => post_output.php => test.php) */

Для нахождения файлов нескольких типов надо писать так:

// найти все php и txt файлы $files = glob("*.{php,txt}", GLOB_BRACE); print_r($files); /* на выходе: Array ( => phptest.php => pi.php => post_output.php => test.php => log.txt => test.txt) */

Так же можно в шаблоне указать путь:

$files = glob("../images/a*.jpg"); print_r($files); /* на выходе: Array ( => ../images/apple.jpg => ../images/art.jpg) */

Для того чтобы получить полный путь к документу используйте метод realpath() :

$files = glob("../images/a*.jpg"); // Применить функцию "realpath" к каждому элементу массива $files = array_map("realpath",$files); print_r($files); /* выведет: Array ( => C:\wamp\www\images\apple.jpg => C:\wamp\www\images\art.jpg) */

3. Информация об используемой памяти

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

В PHP существует мощный инструмент отслеживания используемой памяти. В разных частях скрипта нагрузки могут быть разные. Для того чтобы получить значение используемой памяти в данный момент, нам следует использовать метод memory_get_usage() . Для фиксации максимального количества используемой памяти используем memory_get_peak_usage()

Echo "Initial: ".memory_get_usage()." bytes \n"; /* Initial: 361400 bytes */ // дадим небольшую нагрузку for ($i = 0; $i < 100000; $i++) { $array = md5($i); } // и ещё for ($i = 0; $i < 100000; $i++) { unset($array[$i]); } echo "Final: ".memory_get_usage()." bytes \n"; /* Final: 885912 bytes */ echo "Peak: ".memory_get_peak_usage()." bytes \n"; /* Peak: 13687072 bytes */

4. Информация о процессоре

Для этого необходимо использовать метод getrusage() . Но учтите, что на Windows эта функция работать не будет.

Print_r(getrusage()); /* prints Array ( => 0 => 0 => 2 => 3 => 12692 => 764 => 3864 => 94 => 0 => 1 => 67 => 4 => 0 => 0 => 0 => 6269 => 0) */

Картина, изложенная выше, будет понятно тем, у кого есть опыт в системном администрировании. Для всех остальных предлагаем расшифровку:

  • ru_oublock: количество операций блочной записи
  • ru_inblock: количество операций блочного чтения
  • ru_msgsnd: количество отправленных сообщений
  • ru_msgrcv: количество принятых сообщений
  • ru_maxrss: максимальный размер невыгружаемого набора
  • ru_ixrss: общий объем разделяемой памяти
  • ru_idrss: общий объем неразделяемых данных
  • ru_minflt: количество используемых страниц памяти
  • ru_majflt: количество ошибок отсутствия страниц
  • ru_nsignals: количество принятых сигналов
  • ru_nvcsw: количество переключений контекста процессом
  • ru_nivcsw: количество принудительных переключений контекста
  • ru_nswap: количество обращений к диску при подкачке страниц
  • ru_utime.tv_usec: время работы в пользовательском режиме (микросекунды)
  • ru_utime.tv_sec: время работы в пользовательском режиме (секунды)
  • ru_stime.tv_usec: время работы в привилегированном режиме (микросекунды)
  • ru_stime.tv_sec: время работы в привилегированном режиме (секунды)

Для того чтобы узнать какие ресурсы вашего процессора используются скриптом, вам необходимо значение ‘user time’ (время работы в пользовательском режиме) и ’system time’ (время работы в привилегированном режиме). Вы можете получить результат как в секундах, так и в микросекундах. Для того чтобы превратить общее количество секунд в десятичное число, вам необходимо разделить значение микросекунд на 1 миллион и добавить к значению секунд.

Запутанно как-то. Вот пример:

// отдыхаем 3 секунды sleep(3); $data = getrusage(); echo "User time: ". ($data["ru_utime.tv_sec"] + $data["ru_utime.tv_usec"] / 1000000); echo "System time: ". ($data["ru_stime.tv_sec"] + $data["ru_stime.tv_usec"] / 1000000); /* выводит User time: 0.011552 System time: 0 */

Хотя выполнение скрипта заняло около 3-х секунд, процессор не был сильно нагружен. Дело в том, что при вызове (sleep) скрипт практически не потребляет ресурсов процессора. Вообще существует множество задач, которые занимают значительное время, но при этом не используют процессор. К примеру, ожидание операций связанных с диском. Так что вы не всегда используете процессорное время в своих скриптах.

Вот ещё пример:

// пройтись 10 миллионов раз for($i=0;$i<10000000;$i++) { } $data = getrusage(); echo "User time: ". ($data["ru_utime.tv_sec"] + $data["ru_utime.tv_usec"] / 1000000); echo "System time: ". ($data["ru_stime.tv_sec"] + $data["ru_stime.tv_usec"] / 1000000); /* выводит User time: 1.424592 System time: 0.004204 */

Работа скрипта заняла 1.4 секунды процессорного времени. В данном случае, время системных вызовов вообще низкое.

Время работы в привилегированном режиме (System Time) - это время, которое процессор затрачивает на выполнение системных запросов к ядру от имени программы. Пример:

$start = microtime(true); // вызываем microtime каждые 3 секунды while(microtime(true) - $start < 3) { } $data = getrusage(); echo "User time: ". ($data["ru_utime.tv_sec"] + $data["ru_utime.tv_usec"] / 1000000); echo "System time: ". ($data["ru_stime.tv_sec"] + $data["ru_stime.tv_usec"] / 1000000); /* выводит User time: 1.088171 System time: 1.675315 */

Теперь системного времени затратилось намного больше, чем в прошлом примере. Всё благодаря методу microtime(), который использует ресурсы системы.

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

5. Магические константы

В PHP существует множество магических констант, таких как номер текущей строки (__LINE__), путь к файлу (__FILE__), путь к каталогу (__DIR__), имя функции (__FUNCTION__), имя класса (__CLASS__), имя метода (__METHOD__) и пространства имён (__NAMESPACE__).

Все мы их рассматривать не будем. Посмотрим только лишь парочку:

// этот скрипт зависит от текущего расположения файла и // может вызвать проблемы, если его использовать из разных дирректорий require_once("config/database.php"); // этот скрипт не вызовет проблем require_once(dirname(__FILE__) . "/config/database.php");

Используйте __LINE__ при отладке скриптов:

// код // ... my_debug("some debug message", __LINE__); /* выведет Line 4: some debug message */ // ещё код // ... my_debug("another debug message", __LINE__); /* выведет Line 11: another debug message */ function my_debug($msg, $line) { echo "Line $line: $msg\n"; }

6. Генерирование уникальных ID

Бывают такие моменты, когда вам надо сгенерировать уникальную строку. Множество раз я видел, что для решения этой задачи используют функцию md5():

// генерируем случайную строку echo md5(time() . mt_rand(1,1000000));

Но на самом деле для этих целей в PHP есть специальная функция uniqid()

// генерируем случайную строку echo uniqid(); /* выведет 4bd67c947233e */ // ещё разок echo uniqid(); /* выведет 4bd67c9472340 */

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

Для того чтобы уменьшить шансы получения дубликата, мы можем добавить префикс или использовать второй параметр (увеличит количество символов):

// с префиксом echo uniqid("foo_"); /* выведет foo_4bd67d6cd8b8f */ // со вторым параметром echo uniqid("",true); /* выведет 4bd67d6cd8b926.12135106 */ // оба echo uniqid("bar_",true); /* выведет bar_4bd67da367b650.43684647 */

Этот метод генерирует строки размером меньше, чем md5, тем самым вы сможете сэкономить место.

7. Сериализация

Вам когда-нибудь приходилось хранить комплексные данные в базе или в файле? Для того чтобы сконвертировать объект в строку в PHP предусмотрена специальная функция.

Вообще говоря, этих методов 2: serialize() и unserialize()

// сложный массив $myvar = array("hello", 42, array(1,"two"), "apple"); // конвертируем в строку $string = serialize($myvar); echo $string; /* выведет a:4:{i:0;s:5:"hello";i:1;i:42;i:2;a:2:{i:0;i:1;i:1;s:3:"two";}i:3;s:5:"apple";} */ // получаем исходное значение $newvar = unserialize($string); print_r($newvar); /* выведет Array ( => hello => 42 => Array ( => 1 => two) => apple) */

Вот так вот работают эти функции. Однако из-за бурного роста популярности JSON, в PHP 5.2 были добавлены 2 метода json_encode() и json_decode(). Их работа схожа с serialize():

// сложные массив $myvar = array("hello", 42, array(1,"two"), "apple"); // конвертируем в строку $string = json_encode($myvar); echo $string; /* выведет ["hello",42,,"apple"] */ // восстанавливаем исходное значение $newvar = json_decode($string); print_r($newvar); /* prints Array ( => hello => 42 => Array ( => 1 => two) => apple) */

Этот вариант более компактный и совместимый с другими языками, такими как JavaScript. Однако при работе с очень навороченными объектами может возникнуть потеря данных.

8. Сжатие строк

Кода мы говорим о сжатии, то на ум сразу же приходят архивные файлы в формате ZIP. PHP предоставляет возможность сжатия длинных строк без всяких файлов.

В следующем примере продемонстрируем работу функций gzcompress() и gzuncompress() :

$string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ut elit id mi ultricies adipiscing. Nulla facilisi. Praesent pulvinar, sapien vel feugiat vestibulum, nulla dui pretium orci, non ultricies elit lacus quis ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pretium ullamcorper urna quis iaculis. Etiam ac massa sed turpis tempor luctus. Curabitur sed nibh eu elit mollis congue. Praesent ipsum diam, consectetur vitae ornare a, aliquam a nunc. In id magna pellentesque tellus posuere adipiscing. Sed non mi metus, at lacinia augue. Sed magna nisi, ornare in mollis in, mollis sed nunc. Etiam at justo in leo congue mollis. Nullam in neque eget metus hendrerit scelerisque eu non enim. Ut malesuada lacus eu nulla bibendum id euismod urna sodales. "; $compressed = gzcompress($string); echo "Original size: ". strlen($string)."\n"; /* выведет Original size: 800 */ echo "Compressed size: ". strlen($compressed)."\n"; /* выведет Compressed size: 418 */ // возвращаем $original = gzuncompress($compressed);

В наших силах уменьшить объём текста на 50%. В этих же целях можно использовать методы gzencode() и gzdecode(), которые используют другой алгоритм сжатия.

9. Выполнить перед завершением

В PHP существует функция register_shutdown_function() , которая позволит вам выполнить какой-то код перед завершением работы скрипта.

Допустим, вы хотите узнать какую-то информацию… Время работы скрипта:

// получаем время начала $start_time = microtime(true); // какие-то операции // ... // выводим время работы echo "execution took: ". (microtime(true) - $start_time). " seconds.";

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

При использовании метода register_shutdown_function() код выполнится в любом случае:

$start_time = microtime(true); register_shutdown_function("my_shutdown"); function my_shutdown() { global $start_time; echo "execution took: ". (microtime(true) - $start_time). " seconds."; }

Вывод

PHP это целая планета, которая не перестаёт нас удивлять своим содержимым. А что думаете вы о данных функциях?

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


Определить эту самую скорость можно очень просто, при помощи функции microtime() .
По умолчанию функция возвращает строку в формате "msec sec", где sec - это количество секунд, прошедших с начала Эпохи Unix (The Unix Epoch, 1 января 1970, 00:00:00 GMT), а msec - это дробная часть.
Но если функция вызывается с необязательным параметром microtime([ bool $get_as_float ] ) то при условии, что переданный аргумент get_as_float , равен TRUE или 1 , вернет действительное число, тоесть:

Echo microtime(); //получим 0.79687900 1234083984 echo microtime(0); //получим 0.79687900 1234083984 echo microtime(false); //получим 0.79687900 1234083984 echo microtime(1); //получим 1234083984.79 echo microtime(true); //получим 1234083984.79
Из этого следует, что написав в самом начале php скрипта строку:
$time_start_script = microtime(1);
а в конце скрипта:
$time_stop_script = microtime(1); $time_script_work = $time_stop_script - $time_start_script; echo $time_script_work;

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

Ну а если вспомнить про "велосипед", то можно получить время с точностью до 10-15 знаков после запятой (по крайней мере у меня получалось именно столько в зависимости от времени выполнения скрипта), но это в большей мере повлияет на время выполнения скрипта, в отличии от выше приведенного примера, зато вы узнаете об еще одной полезной функции.
Напишем в самом начале php скрипта строку:
$time_start_script = microtime(); $time_start_script = explode(" ", $time_start_script); $time_start_script = $time_start_script["0"] + $time_start_script["1"];
а в конце скрипта:
$time_stop_script = microtime(); $time_stop_script = explode(" ", $time_stop_script); $time_stop_script = $time_stop_script["0"] + $time_stop_script["1"]; $time_script_work = $time_stop_script - $time_start_script; echo $time_script_work." sec";
Этот пример вывел в моем случае 0.000353097915649 sec.
А теперь давайте разберемся, что это за странная функция explode() .
array explode(string $separator, string $string[, int $limit ])
Функция возвращает массив строк, полученных разбиением строки string с использованием separator в качестве разделителя. Если передан аргумент limit , массив будет содержать максимум limit элементов, при этом последний элемент будет содержать остаток строки string.
Если separator - пустая строка (""), explode() возвращает FALSE. Если separator не содержится в string, то explode() возвращает массив, содержащий один элемент string .
Пример использования: $pizza = "piece1 piece2 piece3 piece4 piece5 piece6"; $pieces = explode(" ", $pizza); echo $pieces; // piece1 echo $pieces; // piece2
Вот в принципе и все. Теперь Вы можете оценить скорость работы написанных вами скриптов.
В приложенном файле есть исходные коды приведенных тут скриптов, при этом, для того, чтобы скрипт выполнялся дольше, в нем записан пустой цикл, который выполняется миллион раз:).
Удачи!

Как долго продолжают работать "тяжелые" скрипты, если не дожидаясь окончания их работы закрыть браузер? И продолжат ли они свою работу? Как показали наблюдения, они продолжают жить пытаясь выполнить поставленную перед ними работу. Многие для управления вот такого рода временем "темной" жизни скриптов прибегают к инструкции вида

ignore_user_abort(boolean mode)

Если эта инструкция вызывается с false параметром, то она равнозначна ее отсутствию. Обычно она указывается с параметром true - ignore_user_abort (true) . Это необходимо в случаях, когда нужно продолжить работу скрипта, даже если браузер сбросил соединение. Вообще в php имеются возможности для отслеживания состояния подключения. Меня заинтересовала ситуация с продолжением выполнения скрипта даже после закрытия окна браузера. Проверить это очень просто: напишите скриптик, который выполняется некоторое время, запустите его и недожидаясь окончания работы скрипта закройте окно браузера, не забудьте в скрипте добавить маркер завершения работы, например создать файлик. Так вы сможете отслеживать за выполняемостью вашего скриптика. Вообще после закрытия сеанса агента (браузера) скрипт должен умереть, но как вы можете убедиться, это не совсем так: он все же пытается прожить подольше. В этом случае есть смысл перенести его в фоновое выполнение (cron). Но все же интересно было проанализировать данную особенность. Для отслеживания состояния подключения используется инструкция connection_status() . Для дальнейших манипуляций были написаны свои обертки:

// return code of connect status
function getConnectStatus() {
return connection_status();
}

// return type of connect status by code
function getConnectStatusByCode($connectionStatus) {
switch ($connectionStatus) {
case CONNECTION_NORMAL:
$status = "Normal";
break;
case CONNECTION_ABORTED:
$status = "User Abort";
break;
case CONNECTION_TIMEOUT:

$status = "Max Execution Time exceeded";
break;
case (CONNECTION_ABORTED & CONNECTION_TIMEOUT):
$status = "Aborted and Timed Out";
break;
default:
$status = "Unknown";
break;
}
return $status;
}



}

Теперь можно написать функцию, которая будет имитировать какую-либо долгую работу

// imitation long script
function scriptImitation() {

$start = time();

$limitIteration = 1000000;
for ($i = 0; checkConnectionStatus() && ($i < $limitIteration); $i++) {
// some long work...
}

$end = time();
$runTime = $end - $start;

If ($i === $limitIteration || !checkConnectionStatus()) {
$connectStatus = getConnectStatusByCode(getConnectStatus());
$scriptImitationLog = "Connect status: $connectStatus";
$scriptImitationLog .= "; ";
$scriptImitationLog .= "Iteration: $i";
$scriptImitationLog .= "; ";
$scriptImitationLog .= "Run time: $runTime second";

File_put_contents("scriptImitationLog.txt", $scriptImitationLog);
}

Собственно дальше периодически запускаем с остановами работу нашего скрипта. Как бы быстро не пытался закрыть окно агента, скрипт все также успешно отрабатывал и на выходе создавался лог. Не стоит забывать про max_execution_time , дольше этого времени скрипт точно не будет жить. В нашем случае он был увеличин. Давайте заглянем в лог:

Connect status: Normal; Iteration: 1000000; Run time: 5 second

Как видно, подключение в норме, цикл отработал до конца. Так в чем же дело? В этой статье на stackoverflow нашел вот такое решение - http://stackoverflow.com/questions/2389035/php-connection-status . И связано оно было с буфером вывода: периодически в буфер вывода скрипта дописывается небольшая порция данных (например, пустой символ) с последующим сбросом (обычно для php подразумевается принудительная отправка всего содержимого с последующей очисткой). Для этого предлагается использовать совместное использование функций ob_flush и flush . И так как для сброса подразумевается наличие подключения (ведь буфер формируется для отправки в направлении браузера - инициатора), то при очередном сбросе при отсутствии нормального подключения его статус переводится в иное состояние (User aborted ). Причем если в скрипте отсутствует инструкция или приведена в виде

ignore_user_abort (false);

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

ignore_user_abort (true);

Даже при изменении статуса подключения скрипт продолжит выполняться до конца, но именно в этом случае можно отслеживать статус подключения. С учетом этого, немного видоизменим одну из оберток

// check the connect status (true - normal, false - abnormal)
function checkConnectionStatus() {
print " ";
ob_flush();
flush();
return connection_status() === 0;
}

Теперь все находится под контролем, и в случаях, когда мы закроем наш браузер, работа скрипта будет приостановлена или можно будет организовать иное поведение.
На последок приведу результаты еще одного наблюдения. Если сравнить время выполнения скрипта по логам до внесения изменений и после в обертку - checkConnectionStatus ,
можно сделать вывод, что сброс буфера вывода занимает продолжительное время. Так время на одну итерацию в тестовом скрипте без сброса буфера в среднем заняло - 0,005 ms , со сбросом - 0,028 ms , то есть сброс буфера в среднем заняло 0,023 ms , что в 4,6 раза больше времени одной итерации. Отсюда видно, что приведенный метод отслеживания за жизнью работы "тяжелых" скриптов может увеличивать основное время работы. Данный тест проводился на Firefox version 29.0.1. Всем успехов.



В продолжение темы:
Windows

Часть вторая : "Важнейшие характеристики каждого семейства процессоров Intel Core i3/i5/i7. Какие из этих чипов представляют особый интерес" Введение Сначала мы приведём...

Новые статьи
/
Популярные