Оптимизация производительности PHP-приложений
Самым большим преимуществом программирования на PHP является простота изучения этого языка программирования и его богатых библиотек. Даже если мы мало что знаем о функциях, которые нам нужно использовать, мы можем догадаться, как выполнить конкретную задачу.
Хотя PHP очень прост и легок в освоении, нам все же придется потратить немного времени на изучение некоторых навыков программирования PHP, особенно тех, которые связаны с производительностью и использованием памяти. В PHP есть множество хитростей, позволяющих уменьшить использование памяти и повысить производительность приложения. В этой статье мы кратко представим анализ PHP-приложений, способы изменения кода скрипта и сравним различные значения параметров до и после оптимизации.
Установив процедуры синхронизации в программе и повторно выполняя эти коды, мы можем получить набор данных о скорости выполнения программы. Эти данные можно использовать для обнаружения узких мест в программе и способов ее оптимизации для повышения производительности. приложение.
Возможно, читатели слышали о библиотеке PEAR. Мы будем использовать библиотеку PEAR для создания примеров, которые нам нужно использовать во время анализа. Это также самый простой способ анализа существующего кода. Он позволяет нам анализировать код без использования коммерческих продуктов.
Имя библиотеки, которую мы собираемся использовать, — PEAR::Benchmark, и она очень полезна для профилирования и тестирования производительности кода. Эта библиотека предоставляет класс с именем Benchmark_Timer(), который может записывать время между одним вызовом функции и следующим вызовом функции. При тестировании производительности кода мы можем получить подробный результат выполнения скрипта, что очень просто, следующим образом:
include_once("Benchmark/Timer.php");
$bench = новый Benchmark_Timer;
$ скамейка-> старт ();
$bench-> setMarker('Начало скрипта');
// Теперь в состоянии сна на несколько минут
спать(5);
$ скамейка-> стоп ();
// Получаем аналитическую информацию от таймера
print_r($bench->getProfiling());
?>
Вывод после выполнения приведенного выше кода выглядит следующим образом:
Множество
(
[0] => Массив
(
[имя] => Старт
[время] => 1013214253.05751200
[разница] => -
[всего] => 0
)
[1] => Массив
(
[имя] => Начало скрипта
[время] => 1013214253.05761100
[разница] => 9.8943710327148E-05
[всего] => 9.8943710327148E-05
)
[2] => Массив
(
[имя] => Стоп
[время] => 1013214258.04920700
[разница] => 4.9915959835052
[всего] => 4,9916949272156
)
)
Числа, приведенные выше, могут показаться бессвязным набором чисел, но если размер программы больше, эти числа могут оказаться очень полезными.
Возможно, большинство читателей также догадаются, что первая запись в массиве — это собственно метод вызова класса Benchmark_Timer(), например
$bench->start(), $bench->setMarker() и $bench->stop() Числа, связанные с этими записями, довольно просты. Теперь давайте более подробно рассмотрим эти числа:
[0] => Массив
(
[имя] => Старт
[время] => 1013214253.05751200
[разница] => -
[всего] => 0
)
Запись времени относится к метке времени UNIX при вызове метода start() функции Benchmark_Timer(). Запись diff указывает временной интервал между этим вызовом и последним вызовом. Поскольку предыдущего вызова здесь нет, тире обозначает общую запись. относится к общему времени работы кода с момента начала теста до этого конкретного вызова. Давайте посмотрим на вывод следующего массива:
[1] => Массив
(
[имя] => Начало скрипта
[время] => 1013214253.05761100
[разница] => 9.8943710327148E-05
[всего] => 9.8943710327148E-05
)
Из приведенных выше чисел мы видим, что после вызова $bench->start() программа работает 9,8943710327148E-05 секунд (то есть 0,0000989 секунды) перед вызовом $bench->setMarker(….).
Реальный опыт тестирования производительности
Хотя приведенный выше пример хорош, на самом деле он не является хорошим примером для принятия решения о том, как оптимизировать дизайн кода вашего сайта. Ниже я буду использовать свой личный опыт работы в качестве специалиста по веб-сайтам, чтобы проиллюстрировать, как решать проблемы с производительностью.
Я не совсем понимаю код, используемый веб-сайтом, поскольку он разрабатывался в течение многих лет исходя из конкретных потребностей - один модуль содержит код преобразования веб-сайта, другой модуль записывает использование веб-сайта, а остальные модули имеют свои собственные. роль каждого. Мы с главным разработчиком сайта поняли, что код сайта необходимо оптимизировать, но не знали, в чем проблема.
Чтобы выполнить задачу как можно быстрее, я начал изучать основной код скрипта сайта и добавил несколько команд $bench->setMarker() ко всем кодам скриптов и включенным в них файлам, а затем проанализировал вывод $bench. ->getProfiling(), и результаты меня удивили. Оказалось, что проблема заключалась в вызове функции, связанной с кодом преобразования, для получения определенного имени языка (например, en для английского), которое использовалось сотни раз. на каждой странице. Каждый раз, когда вызывается эта функция, код сценария запрашивает базу данных MySQL, чтобы получить фактическое имя языка из таблицы базы данных.
Поэтому мы создаем систему буферизации для такого типа информации. Всего за 2 дня работы мы значительно улучшили производительность системы, а количество просмотров страниц выросло на 40% за первую неделю. Конечно, это всего лишь один пример того, как анализ кода может повысить производительность интернет-приложения или интернет-сайта.
Вызов функции теста производительности
Хотя Benchmark_Timer() особенно полезен при анализе сценария или веб-страницы (и содержащихся в них файлов), он не является научным, поскольку мы должны загрузить сценарий несколько раз, чтобы получить анализируемые данные, и он не относится к определенному классу или функции. . называется.
Другой класс в библиотеке PEAR::Benchmark, называемый Benchmark_Iterator, может очень хорошо решить эту проблему. Он может отображать аналитическую информацию для конкретной функции или метода класса. Его цель — иметь возможность получать согласованные результаты тестов, поскольку мы знаем, что если мы запускаем скрипт один раз и его выполнение занимает 10 секунд, это не означает, что каждый раз его запуск будет занимать 10 секунд.
В любом случае, давайте посмотрим несколько примеров:
// Код для подключения к базе данных
include_once("DB.php");
$dsn = массив(
'phptype' => 'mysql',
'hostspec' => 'localhost',
'база_данных' => 'имя_базы_данных',
'имя_пользователя' => 'имя_пользователя',
'пароль' => 'пароль'
);
$dbh = DB::connect($dsn);
функция getCreatedDate($id)
{
глобальный $дбх;
> $stmt = "ВЫБЕРИТЕ дату создания_даты ИЗ пользователей ГДЕ id=$id";
// Используем здесь PEAR::DB
$created_date = $dbh-> getOne($stmt);
if ((PEAR::isError($created_date)) ||
(пустой($create_date))) {
вернуть ложь;
} еще {
вернуть $created_date;
}
}
include_once 'Benchmark/Iterate.php';
$bench = новый Benchmark_Iterate;
//Запускаем функцию getDate 10 раз
$bench-> run(10, 'getCreatedDate', 1);
//Печать аналитической информации
print_r($bench->get());
?>
Запуск приведенного выше кода дает результаты, подобные следующим:
Множество
(
[1] => 0,055413007736206
[2] => 0,0012860298156738
[3] => 0,0010279417037964
[4] => 0,00093603134155273
[5] => 0,00094103813171387
[6] => 0,00092899799346924
[7] => 0,0010659694671631
[8] => 0,00096404552459717
[9] => 0,0010690689086914
[10] => 0,00093603134155273
[среднее] => 0,0064568161964417
[итерации] => 10
)
Приведенные выше цифры легко понять. Среднее значение представляет собой среднее время 10 запусков функции getCreatedDate(). При реальном тестировании вам следует запустить его не менее 1000 раз, но результатов этого примера достаточно, чтобы проиллюстрировать проблему.