Оптимизация клиентской стороны на php. Советы по оптимизации PHP-скриптов. Как будем тестировать

В этой статье я хочу дать несколько советов, следуя которым можно значительно ускорить обработку PHP-скриптов, тем самым, разгрузив сервер хостера от бесполезной работы в пользу более полезной…

Используйте одинарные кавычки вместо двойных

Дело в том, что текст, заключенный в двойные кавычки сканируется процессором на наличие в нем переменных, а также мнемонических подстановок (например, \n для вставки в текст символа новой строки).

print("Это совершенно обычный текст, но PHP-процессор сканирует его на наличие переменных и мнемонических подстановок перед тем, как вывести. При достаточной длине такой строки, этот процесс занимает значительное время.");

Если же текст заключен в одинарные кавычки, то PHP-интерпретатор послушно выводит его в первозданном виде, даже не думая искать в нем что-то. Это отнимает намного меньшее время, чем в предыдущем примере.

print("PHP-процессор сильно обрадуется, увидев строку, заключенную в одинарные кавычки. А как тут не обрадоваться — ведь не нужно делать бесполезную работу!");

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

$string="Какое счастье не парсить лишний раз эту строку!";

Не заключайте переменные в кавычки

Очень часто приходиться видеть подобную картину.

print("PHP-процессору пришлось обрабатывать эту страницу $time секунд.");

Усвоя мой предыдущий совет, и зная, что PHP-интерпретатору намного легче "склеивать" строки, чем сканировать их на наличие переменных и заменять их на соответствующие этим переменным строки, нетрудно догадаться, что мой предыдущий пример — пример неэффективного использования ресурсов сервера. А теперь порадуем сервер оптимизированным скриптом.

print("PHP-процессор, радуясь жизни, обработал этот скрипт за ".$time." секунд.");

Для тех, кто в танке: склейка строк производится с помощью точки (.), то есть

print("Адрес этого сайта — http://".$_SERVER["HTTP_HOST"]."/!");

выведет текст «Адрес этого сайта — http://argon.com.ru/!»

Используйте минимум переменных

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

Спрашивается, а нахрена занимать лишнюю память, попусту нагружая сервер?.. Намного эффективней сделать так.

print("PHP-процессору доставляет одно удовольствие выводить такие строки.");

Так что, если какая-то строка используется меньше двух раз, присваивать переменной значение этой строки вредно.

Используйте короткие имена переменных

Если в скрипте используются переменные с неоправданно длинными именами, нагрузка на сервер, естественно возрастает.

$windows_xp_edition="Professional";
$windows_xp_build="2600";
$windows_server2003_edition="Standard";
$windows_server2003_build="3200";

Но и слишком лаконичные имена давать тоже не стоит — в будущем при правке скрипта внушительных размеров сто раз вспомнишь мать его создателя, причем не ласковым словом:)

$ifa="Professional";
$ifb="2600";
$ifc="Standard";
Sifd="3200";

Оптимальный же вариант — давать лаконичные, но понятные имена, не превышающие восьми символов.

$wxped="Professional";
$wxpbld="2600";
$ws2k3ed="Standard";
Sws2k3bld="3200";

Используйте вложенные функции

Очень часто приходиться видеть такое дело.

$string=str_replace("\n"," ",$string);
$string=str_replace("\r"," ",$string);
$string=trim($string);
$string=stripslashes($string);
$string=chop($string);
$string=substr($string,0,1024);
print($string);

$string))))),0,1024));

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

Используйте встроенные функции

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



$string=str_replace("<","<",$string);
$string=str_replace(">"",">",$string);
print($string);

Этого же самого можно добиться, если использовать такой код.

print(htmlspecialchars($string));

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

Еще один пример. С помощью этого скрипта можно прочитать содержимое какого-нибудь текстового файла в массив.

$fp=fopen("database.txt","r");
while($array=fgets($fp,1024));
fclose($fp);

Но PHP имеет жутко полезную встроенную функцию file(), которая выполняет абсолютно то же самое, но намного быстрее!

file("database.txt");

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

Используйте собственные функции

Часто необходимо обработать несколько строк по совершенно одинаковому алгоритму. Вот, что делают неопытные программисты…

print(substr(chop(stripslashes(trim(str_replace("\r"," ",str_replace("\n"," ",$string1 ))))),0,1024));
print(substr(chop(stripslashes(trim(str_replace("\r"," ",str_replace("\n"," ",$string2 ))))),0,1024));

А опытные пойдут более простым путем.

function format(&$string){

}
print(format($string1));
print(format($string2));

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

Не создавайте лишних функций

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

function optimize(&$string){
$string=str_replace("&","&",$string);
$string=str_replace(""",""",$string);
$string=str_replace("<;","<",$string);
$string=str_replace(">"",">",$string);
}
print(optimize($string));

А ведь можно обойтись более простыми средствами.

print(htmlspecialchars($string);

Не используйте лишние функции

Вот пример неэффективного кода.

print("Мой дядя ");
print("самых честных грабил…");

Но его можно очень просто оптимизировать.

print("Мой дядя "."самых честных правил…");

Чувствуете разницу? Поверьте, хостер в этом случае вам будет только благодарен за освободившиеся ресурсы сервера…

Рационально используйте функции

Например, такой скрипт.

print(ereg_replace("нормальный","несчастный",$string));

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

print(str_replace("несчастный","счастливый",$string));

Не дергайте лишний раз обработчик

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

…шапка страницы…
print($заголовок);
?>
…навигация…

?>
…подвал страницы…

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

print("…шапка страницы…".$заголовок."…навигация…");
include("динамический контент");
print("…подвал страницы…");
?>

Продолжение следует

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

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

Удачной вам оптимизации!

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

Fastcgi

FastCGI — это один из вариантов подключения PHP к Web серверу. Лучше всего использовать в связке с Nginx. PHP-fpm (Fastcgi контейнер для PHP) и Nginx по умолчанию поддерживают совместную работу и очень легко настраиваются .

OpCache


Как обычно выполняется PHP скрипт? PHP открывает файл с кодом, компилирует его, затем выполняет. Поскольку файлов может быть много, процесс их открытия, чтения и компиляции может отнимать кучу ресурсов. Если файлы не меняются, то постоянную компиляцию можно и не делать. Лучше сделать ее один раз и закэшировать результат.

Именно это и делает модуль opCache. Результат первой компиляции будет сохранен в кэш, с которым и будет работать PHP. Таким образом это ускорит выполнение за счет отсутствия тяжелого процесса компиляции. Когда файлы изменятся, модуль сам сбросит кэш и обеспечит перекомпиляцию. Короче, этот модуль делает очень полезную экономию ресурсов. И это без необходимости его настраивать.

В версии PHP5.5+ этот модуль поставляется в стандартной сборке. В предыдущих версиях модуль нужно устанавливать самостоятельно. Проверить наличие можно так: php -i | grep opcache

# Пустой вывод будет означать, что модуля нет

Если версия слишком ранняя, лучше использовать APC: apt-cache search php-apc

# Это альтернатива opCache, но делает то же самое

Кэширование

Часто код просто медленный. Например:

  • обращения к внешним API
  • тяжелые выборки из баз данных
  • обработка больших файлов
session.save_handler = memcache session.save_path = "tcp://localhost:11211 "

# localhost:11211 это стандартный хост и порт Memcache

Кроме этого, такая схема хранения позволит масштабироваться на несколько серверов.

Оптимизация кода

ООП

Помните! ООП — это всегда медленно. Объекты нужно создавать, где-то хранить и уничтожать. Не используйте объекты, если они не нужны. Например, тут:

set_title($_GET["title"]); $post->set_description($_GET["description"]); $post->save();

# $posts = список объектов Post, полученных каким-то образом foreach ($posts as $post) { echo $post->title . "
"; }

# Используем список объектов только для того, чтобы вывести свойство

В этих примерах использование ООП не имеет особого смысла. Зато расходует ресурсы. Старайтесь использовать массивы, когда объекты не нужны:

$_GET["title"], "description" => $_GET["description"]]);

# Избежали создания объекта, функция просто сохраняет данные из массива в базу

"; }

# Намного лучше — сделать простую выборку и вывести нужные данные из массива

Мелочи

При работе с файлами используйте абсолютные пути. Тогда не будут происходить лишние операции поиска файла:

include "/var/www/file.php"; file_get_contents("/var/www/dir/data.txt");

Константы классов работают эффективнее, чем define:

class posts { const PER_PAGE = 10; ... }

Не используйте функции в условии for, т.к. они будут повторяться на каждой итерации цикла:

$max = mysql::get_col("SELECT count(*) FROM posts"); for ($i = 0; $i < $max; $i++) { ... }

В качестве ключей массивов всегда указывайте строки с кавычками :

$post["title"] = "Первый пост";

Используйте встроенные функции работы со строками вместо регулярных выражений, если это возможно.

strpos($post["title"], "хорошо");

Используйте строки с одинарными кавычками:

$post["title"] = "В этом случае нет дополнительной обработки переменных"

PHP cron-скрипты

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

(.+?)<\/title/", $rss, $matches ); }

(.+?)<\/title/", $rss); if ($has_something) $updates = time(); $rss = file_get_contents("http://othersource.com/rss"); $has_something = preg_match("/title>(.+?)<\/title/", $rss); if ($has_something) $updates = time(); }

Теперь переменная $updates будет расти до максимального предела. Когда будет достигнут лимит по памяти, скрипт будет остановлен. Уследить за всеми переменными довольно тяжело, поэтому лучше использовать функции. Все переменные, созданные внутри функции будут удаляться после ее завершения:

process(); function process() { $rss = file_get_contents("http://somesite.com/rss"); $has_something = preg_match("/title>(.+?)<\/title/", $rss); if ($has_something) $updates = time(); $rss = file_get_contents("http://othersource.com/rss"); $has_something = preg_match("/title>(.+?)<\/title/", $rss); if ($has_something) $updates = time(); }

Самое важное

  • Обязательно используйте opCache для PHP. Это бесплатно экономит ресурсы.
  • Используйте FastCGI (лучше всего Nginx + PHP-fpm).
  • Функции в крон задачах помогут избежать утечек памяти.
  • Кэширование медленных участков кода часто самое простое решение для ускорения работы.
  • Помните о
  • Как оптимизировать сайт и ускорить его работу?
  • С какой скоростью будет и может работать сайт, в соответствии с теми технологиями на которых он будет запущен?
  • Какие технологии следует использовать настраивая сервер или VPS?

Типичная проблема:
В какой-то момент сайт начинает открываться и работать слишком медленно. Бывает, что хостинговая компания блокирует сайт за превышение нагрузки или перерасход ресурсов. Что же делать в такой ситуации?

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

И если говорить о серверах для PHP, то такой проблемой является способ исполнения php кода, ровно как и другие значимые настройки окружения на сервере.
Не зависимо от того, есть ли проблема в вашем коде или её нет, высокая у вас посещаемость или нет, от настроек сервера зависит очень многое. Что бы все сказанное не звучало пустыми словами и была написана эта статья.

В этом обзоре я протестирую только что установленный сайт на одном из самых распространённых движков управления контентом Drupal 7.33 .

Для теста выбрана лишь одна составляющая php-хостинга. Мы будем тестировать web-серверы Nginx и Apache2 , модули mod_php и php-fpm , версии php php53 и php56 , посмотрим, как влияют оптимизаторы apc и opcache на скорость работы сайта.


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

Дано:
  • Операционная система Centos 6.7
  • Сервер баз данных: MariaDB 10.21
  • Все сессии сайтов хранятся в memcache, чтобы убрать влияние скорости установки сессии на скорость работы сайта.
  • На всех тестах в качестве frontend выступает web-server nginx 1.93. В случае с Apache2, Nginx выступает в качестве балансировщика, а также для отдачи статики. В конфигурациях без использования Apache2 - непосредственным web-сервером является Nginx
  • Конфигурация Nginx и MariaDB содержат множество оптимизаций, направленных на достижение максимальной производительности, но для всех участников теста эти настройки одинаковые и поэтому их влиянием следует пренебречь
  • Параметры opcache и apc взяты из рекомендаций Bitrix, так как они оптимальны и универсальны для большинства сайтов
Как будем тестировать?

В локальной сети есть сервер zabbix и его задачи каждую минуту:

  • Открывать главную страницу испытуемого сайта, дожидаться определенного содержимого на странице, убеждаться, что ответ от сервера - код 200.
  • Следующим шагом идет авторизация в админку сайта, это происходит отправкой соответсвующего POST запроса. Сверка текста и кода ответа на странице с заложенным эталоном. Этот шаг касается почти всех подсистем web-сервера, и во многом его скорость зависит от скорости взаимодействия с базой данных
  • Последним шагом является выход из админки сайта, сверка кода ответа и текста на странице выхода
  • По итогам каждого шага, zabbix будет скрупулезно замерять и записывать скорость рендеринга php-кода в html понятный браузеру и демонстрировать нам графики полученных результатов
  • Для каждого испытуемого будут записываться значения в течение одного часа и в качестве результата будет выступать средние значения за этот час
  • Тестирование будет происходить внутри локальной сети, так что влияние на результат скорости интернет-соединения исключено
  • Для удобства восприятия, все результаты показываю в порядке возрастания. Т.е. самый первый результат - это самый медленный. Все конфигурации были вынесены под условный номер, это позволит лучше ориентироваться в результатах
  • Верхние графики - скорость генерации кода, чем выше значение, тем лучше. Нижние графики - время ответа сервера и чем ниже значение, тем лучше
  • Тестируемые сайты живут своей жизнью, в них происходят регулярные операции с базами данных и выполняются задания по расписанию, именно поэтому кривая на графиках может иметь взлеты и падения

Тестирование:

1. Nginx + php-fpm56 без оптимизатора opcache

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

Производительность оставляет желать лучшего, но нагрузку такой вариант будет выдерживать гораздо лучше чем вариант №2 с Apache2. Так же, такой вариант будет расходовать оперативную память существенно эффективнее.

2. Apache2 + mod_php53 без оптимизатора apc

Cамый типичный для хостингов вариант. 90% популярных хостинг-провайдеров используют этот вариант. Хоть php53 давно не поддерживается разработчиками, но в интернете очень много сайтов, до сих пор работающих на этой версии.

Такой вариант не только очень медленный, но и быстро падает под небольшой нагрузкой из-за нехватки рабочих процессов Apache2, либо из-за нехватки оперативной памяти на сервере.

3. Балансировка и статика через Nginx, динамическая часть Apache2 + mod_php56 без оптимизатора opcache

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

К сожалению, далеко не все сайты могут работать полноценно c этой версией. Почти каждая новая версия PHP перестает поддерживать некоторые устаревшие и «небезопасные» функции, нарушая работу «старого» кода.
Сам по себе php56 без оптимизатора довольно медленный, а mod_php склонен падать и занимать всю память на сервере под нагрузкой.

4. Nginx + php-fpm53 без оптимизатора apc

Достаточно передовая конфигурация, для тех кто не желает иметь проблемы из-за ошибок с оптимизатором кода. При этом используется «совместимая» версия интерпретатора PHP, а также из связки убирается ресурсоемкий Apache2.

5. Балансировка и статика через Nginx, динамическая часть Apache2 + mod_php53 + apc

Еще одна распространенная вариация. Очень многие хостинги применяют именно её, при этом либо используют по умолчанию, либо дают возможность включать оптимизатор в своих панелях управления.
Обычно Apache2 оставляют для работы.htaccess-правил, таких как преобразование ссылок и ЧПУ.

Получаем прирост скорости в 3,5 раза, по сравнению с вариантом без использования оптимизатора.
Сам по себе Apache (при использовании его собственного модуля mod_php) расходует для свой работы гораздо больше ресурсов, чем вариант с php-fpm. Apache2 склонен падать, если в одном из его модулей случается сбой или заполнять собой всю оперативную память сервера.

6. Nginx + php-fpm53 + apc

Отличный вариант для сайтов на старых движках, не требующих сложных.htaccess

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

7. Балансировка и статика через Nginx, динамическая часть Apache2 + php-fpm53 + apc

Вариант для устаревших сайтов со сложными.htaccess. Например - старые инсталляции Bitrix.

Это идеальный вариант для устаревших сайтов. Данная конфигурация устойчива к высоким нагрузкам, совместима и достаточно производительна.
Отлично подходит, когда нужны правила.htaccess и дополнительные модули Apache2.
Из недостатков - устаревшая и не обновляемая версия php, но если нет выбора - это самый лучший вариант. Отлично подходит для старой версий Bitrix, Joomla и других распространенных CMS не самых свежих версий.

8. Балансировка и статика через Nginx, динамическая часть Apache2 + mod_php56 + opcache

Достаточно производительная, но ресурсоёмкая конфигурация со всеми недостатками mod_php.

Достаточно быстро, но под нагрузкой у сервера может закончится память, а скорость существенно упадет.

9. Nginx + php-fpm56 + opcache

Самый производительный вариант.

Это самый лучший вариант для всех современных сайтов. Хорошо держит нагрузку, показывает самый лучший результат с точки зрения производительности. Именно такой вариант я использую, когда стоит задача оптимизировать производительность сайта и увеличить скорость его работы.
Единственный недостаток - это то, что мы не сможем использовать.htaccess и все правила mod_rewrite нужно переписать на синтаксис Nginx.
Также не будут работать модули Apache2. Если таковые используются, то этот вариант не подойдёт.

10. Балансировка и статика через Nginx, динамическая часть Apache2 + php-fpm56+ opcache

Самый лучший вариант для сайтов, где нужен.htaccess. Идеально подходит для свежих версий Bitrix.

Хорошо держит нагрузку за счет php-fpm. Активно использую этот вариант для большинства сайтов.

Главная страница тестового сайта
Номер конфигурации Архитектура Средний отклик мс.
1 77,04 103,6
2 78,79 103,98
3 78,85 102,38
4 81,55 97,88
5 Apache2 + mod_php53 + apc 303,37 29,36
6. Nginx + php-fpm53 + apc 312,33 24,73
7. Apache2 + php-fpm53 + apc 339,63 23,32
8. 484,96 16,91
9. Nginx + php-fpm56 + opcache 546,34 14,08
10. Apache2 + php-fpm56+ opcache 571,14 13,78
Авторизация в админке тестового сайта
Номер конфигурации Архитектура Средняя скорость загрузки кб. Средний отклик мс.
1 Nginx + php-fpm56 без оптимизатора opcache 67,51 239,01
2 Apache2 + mod_php53 без оптимизатора apc 64,61 257,51
3 Apache2 + mod_php56 без оптимизатора opcache 66,75 242,42
4 Nginx + php-fpm53 без оптимизатора apc 68.79 233.15
5 Apache2 + mod_php53 + apc 173,81 94,26
6. Nginx + php-fpm53 + apc 173,3 91,3
7. Apache2 + php-fpm53 + apc 182,1 90,5
8. Apache2 + mod_php56 + opcache 218,35 77,55
9. Nginx + php-fpm56 + opcache 252,83 62,25
10. Apache2 + php-fpm56+ opcache 262,8 60,85
Выход из админки тестового сайта
Номер конфигурации Архитектура Средняя скорость загрузки кб. Средний отклик мс.
1 Nginx + php-fpm56 без оптимизатора opcache 41,01 184,49
2 Apache2 + mod_php53 без оптимизатора apc 42,42 188,97
3 Apache2 + mod_php56 без оптимизатора opcache 42,06 188,37
4 Nginx + php-fpm53 без оптимизатора apc 45,48 169,15
5 Apache2 + mod_php53 + apc 190,1 41,87
6. Nginx + php-fpm53 + apc 185,92 41,24
7. Apache2 + php-fpm53 + apc 202,78 39,21
8. Apache2 + mod_php56 + opcache 315,56 26,23
9. Nginx + php-fpm56 + opcache 373,19 20,43
10. Apache2 + php-fpm56+ opcache 381,21 20,57

В качестве итогов:

  • В реальной жизни, все варианты с Apache2 могут быть медленней, так как в своих тестах я умышленно передал отдачу статики Nginx. Это сделано, чтобы исключить влияние скорости отдачи статики на результаты замера скорости работы интерпретатора PHP. Одной из наиболее слабой стороной Apache2 и при этом сильной Nginx - является скорость отдачи статики. Особенно, это заметно на высоких нагрузках. Кроме того, Nginx менее подвержен атаке «медленных соединений»
  • mod_php очень быстро занимает всю доступную память сервера и теряет производительность на нагрузках
  • php-fpm расходует память значительно эффективнее, безопаснее и гибче в настройках. В ряде случаев он быстрее и без высоких нагрузок.
  • Тест имеет узкую специфику, тут мы увидели особенности работы движка Drupal, другие могут вести себя иначе, но общая тенденция будет такой же.

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

Если у вас возникнут вопросы, трудности или потребуется совет:
Мои контакты в

От автора: каждый уважающий себя разработчик переживает за «судьбу» своего кода. Старается сделать приложение как можно удобнее, быстрее и повысить его отказоустойчивость. Но не всегда оптимизация PHP позволит вам достичь этих высот.

Не лечи руку, если хромой на ногу!

Пардон за народный ирландский афоризм! Но он в самое «яблочко» отражает всю суть проблемы. Чаще всего оптимизация кода не позволит вам повысить быстродействие созданного скрипта или ресурса. А в случае с все крайне усложняется из-за большого количества факторов, повлиять на которые вы (как разработчик) просто не в силах.

Что касается неподвластных явлений, то я имею в виду скорость интернет-соединения, загруженность сервера, настройки ОС на клиентской машине, мощность железа ПК пользователя. На все эти составляющие вы не сможете повлиять. И в итоге получается, что проведенная оптимизация PHP кода не даст полноценного результата.

Что остается под властью веб-разработчика:

Настройки сервера – ограниченно. Настройка параметров через файл конфигурации Apache httpd.conf позволяет задать количество дочерних процессов, таймаут соединения через сокет, объем буфера для вывода при соединении TCP/IP, время простоя и другие.

Настройки ядра языка – через параметры, прописанные в файле php.ini. Позволяет задать значения буферизации, изменить максимальное время выполнения скриптов, обработку ошибок, управление логами и другие настройки.

С помощью PHP оптимизации изображений – об этом позже. Оптимизация программного кода – позволяет «сэкономить» ресурсы и повысить быстродействие.

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

Работа с изображениями

Я не сторонник обработки изображений на стороне сервера. Это также ведет к трате драгоценных ресурсов, которых на хостинге всегда мало. Получается, что экономя на одном, мы понапрасну расходуем другие «запасы».

Более оптимальным является вариант загрузки изображений на сторонний сервис, откуда они уже в оптимизированном виде подгружаются в браузер пользователя по заданному адресу. В Сети таких сервисов множество. Хочу назвать только несколько из них: kraken.io, TinyPNG

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

Кроме этого обладает собственным встроенным инструментарием для «облегчения» изображений. Например, функция imagecopyresampled (). Она уменьшает вес контента за счет ресамплинга и изменения размеров рисунка. Пример использования:

header ("Content-Type: image/jpeg" ) ;

$file = "sample.jpg" ;

$img_obrabot = imagecreatetruecolor (200 , 100 ) ;

$img_orig = imagecreatefromjpeg ($file ) ;

imagecopyresampled ($img_obrabot , $img_orig , 0 , 0 , 0 , 0 , 200 , 100 , 541 , 286 ) ;

imagejpeg ($img_obrabot ) ;

Что можно еще

Также не забывайте о применении оптимизации клиентской стороны с помощью PHP. В какой-то мере сервер может влиять на клиентский браузер через Cache-Control, а также атрибуты этого заголовка: post-check, max-age и другие.

Кроме этого управлять состоянием кэша на клиентской машине позволяют заголовки Last-Modified и ETag. Для изменения каждого файла они устанавливают уникальный идентификатор. Благодаря чему сервер не пересылает ресурсы заново, а лишь ответы 304 статусом.

В статье мы не поднимали вопроса оптимизации с помощью PHP FPM. Но для его рассмотрения потребуется отдельная статья. А на сегодня все. До следующей встречи!

Тема данной статьи - тот факт, что применение FastCGI в PHP не ускоряет время загрузки PHP-кода по сравнению, например, с mod_php.

Большинство традиционных языков Web-программирования (Perl, Ruby, Python и т. д.) поддерживают написание скриптов, работающих в так называемом «FastCGI-режиме». Более того, Ruby on Rails, к примеру, вообще невозможно использовать в CGI-режиме, т.к. он тратит на подключение всех своих многочисленных библиотек десятки секунд.

Ниже я расскажу о методе, который позволяет в ряде случаев ускорить загрузку объемного PHP-кода более чем в 20 раз, не получая при этом побочных эффектов и значительных неудобств. Но вначале давайте поговорим об основах…

Что такое FastCGI?

Вначале поговорим, что называется, о «классическом» FastCGI, который применяют в Си, Perl, Ruby и т. д. В PHP же FastCGI имеет свои особенности, мы их рассмотрим чуть позже. Сейчас речь идет о не-PHP.

FastCGI работает следующим образом: скрипт загружается в память, запускает некоторый тяжелый код инициализации (например, подключает объемистые библиотеки), а затем входит в цикл обработки входящих HTTP-запросов. Один и тот же процесс-скрипт обрабатывает несколько различных запросов один за другим, что отличается от работы в CGI-режиме, когда на каждый запрос создается отдельный процесс, «умирающий» после окончания обработки. Обычно, обработав N запросов, FastCGI-скрипт завершается, чтобы сервер перезапустил его вновь уже в «чистой песочнице».

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

Старые скрипты, написанные с расчетом на CGI, приходится дорабатывать, чтобы они могли работать в FastCGI-окружении (касается использования FastCGI в Си, Perl и т. д.; на PHP дорабатывать не нужно, но у этого свои недостатки, см. ниже). Действительно, ведь раньше скрипт стартовал каждый раз «с чистого листа», а теперь ему приходится иметь дело с тем «мусором», который остался от предыдущего запроса. Если раньше CGI-скрипт на Perl выглядел как

use SomeHeavyLibrary; print "Hello, world!\n";

то после переделки под FastCGI он становится похож на что-то типа

use SomeHeavyLibrary; while ($request = get_fastcgi_request()) { print "Hello, world!\n"; }

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

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

Собственно, техника «иницилизируйся один раз, обрабатывай много запросов» работает не только в FastCGI. Любой сервер, написанный на том же самом языке, что и скрипт, под ним запущенный, использует ее неявно. Например, сервер Mongrel написан целиком на Ruby как раз для того, чтобы под ним быстро запускать Ruby On Rails. Сервер Apache Tomcat, написанный на Java, быстро выполняет Java-сервлеты, т.к. не требует их повторной инициализации. Технология mod_perl также основана на том, что Perl-код не выгружается между запросами, а остается в памяти. Конечно, все эти серверы имеют те же самые проблемы с непредсказуемостью, утечками памяти и сложностью перезапуска, что и FastCGI-приложение.

Почему FastCGI не ускоряет PHP

Вероятно, вы слышали, что PHP тоже можно запускать в режиме FastCGI, и что так делают многие нагруженные проекты (Мамба, некоторые проекты Mail.Ru и т. д.). Это якобы дает «существенный прирост» производительности, потому что (согласно слухам) FastCGI экономит время инициализации скрипта и подключения библиотек.

Не верьте! В действительности поддержка FastCGI в PHP имеет чисто номинальный характер. Точнее, она не дает преимуществ в том смысле, в котором ей привыкли оперировать для уменьшения времени инициализации скрипта. Конечно, вы можете запустить PHP FastCGI-сервер и даже заставить nginx или lighttpd работать с ним напрямую, однако прирост скорости на загрузку кода, который вы от этого получите, будет нулевым. Тяжелые PHP-библиотеки (например, Zend Framework) как загружались долго в mod_php- или CGI-режимах, так и будут продолжать загружаться долго в режиме FastCGI.

Собственно, это неудивительно: ведь чтобы запустить любой PHP-скрипт в FastCGI-режиме, его не приходится дорабатывать. Ни строчки измененного кода! Когда я впервые решил поэкспериментировать с FastCGI в PHP, я потратил несколько часов времени на поиски в Интернете информации о том, как именно следует модифицировать PHP-код, чтобы оптимально запускать его в режиме FastCGI. Я проштудировал всю документацию PHP и несколько десятков форумов PHP-разработчиков, даже просмотрел исходный код PHP, но так и не нашел ни единой рекомендации. Имея прежний опыт работы с FastCGI в Perl и Си, я был несказанно удивлен. Однако все встало на свои места, когда выяснилось, что изменять код не нужно и, хотя в рамках одного FastCGI-процесса обрабатываются несколько соединений, PHP-интерпретатор каждый раз инициализируется заново (в отличие от «классического» FastCGI). Более того, похоже, большинство PHP-разработчиков, радостно использующих FastCGI+PHP, даже не подозревают о том, что оно должно работать как-то по-другому…

eAccelerator: ускорение повторной загрузки PHP-кода

Каждый раз, когда PHP-скрипт получает управление, PHP компилирует (точнее, транслирует) код скрипта во внутреннее представление (байт-код). Если файл небольшой, трансляция происходит очень быстро (Zend Engine в PHP - один из лидеров по скорости трансляции), однако, если включаемые библиотеки «весят» несколько мегабайтов, трансляция затягивается.

Существует ряд инструментов, кэширующих в разделяемой оперативной памяти (shared memory) оттранслированное внутреннее представление PHP-кода. Таким образом, при повторном включении PHP-файла он уже не транслируется, а байт-код берется из кэша в памяти. Естественно, это значительно ускоряет работу.

Одним из таких инструментов является eAccelerator. Он устанавливается в виде расширения PHP (подключается в php.ini) и требует самой минимальной настройки. Рекомендую включить в нем режим кэширования байт-кода исключительно в оперативной памяти и отключить сжатие (установить параметры eaccelerator.shm_only=1 и eaccelerator.compress=0). Также установите и настройте контрольную панель control.php, идущую в дистрибутиве eAccelerator, чтобы в реальном времени отслеживать состояние кэша. Без контрольной панели вам будет очень трудно проводить диагностику, если eAccelerator по каким-то причинам не заработает.

Преимущество eAccelerator в том, что он работает весьма стабильно и быстро даже на больших объемах PHP-кода. У меня ни разу не возникало проблем с этим инструментом (в отличие от Zend Accelerator, к примеру).

«Мой скрипт вместе с библиотеками занимает 5 МБ, как же быть?..»

Думаете, 5 МБ кода - это чересчур много для PHP? Ничего подобного. Попробуйте воспользоваться такими системами, как Zend Framework и Propel , чтобы убедиться в обратном. Zend Framework целиком занимает как раз около 5 МБ. Классы, сгенерированные Propel-ом, также весьма объемисты и могут отнять еще несколько мегабайтов.

Многие на этом месте посмеются и скажут, что не надо использовать Zend Framework и Propel, т.к. они «тормозные». Но действительность заключается в том, что тормозные вовсе даже не они… Плохую производительность имеет метод, который по умолчанию использует PHP для загрузки кода. К счастью, ситуацию нетрудно исправить.

Чтобы не быть голословным, я приведу результаты небольшого тестирования, которое я специально провел в «чистом» окружении, не привязанном к какому-либо конкретному проекту. Тестировалась скорость подключения всех файлов Zend Framework (за исключением Zend_Search_Lucene). Предварительно из кода были вырезаны все вызовы require_once, а загрузка зависимостей поизводилась только через механизм autoload .

Итак, всего подключались 790 PHP-файлов общим объемом 4.9 МБ. Немало, верно? Подключение осуществлялось примерно так:

function __autoload($className) { $fname = str_replace("_", "/", $className) . ".php"; $result = require_once($fname); return $result; } // Подключаем классы один за другим в порядке их зависимостей. class_exists("Zend_Acl_Assert_Interface"); class_exists("Zend_Acl_Exception"); class_exists("Zend_Acl_Resource_Interface"); class_exists("Zend_Acl_Resource"); // ... и так для всех 790 файлов

Благодаря тому, что используется autoload, вызов class_exists() заставляет PHP подключить файл соответствующего класса. (Это самый простой способ «дернуть» autoload-функцию.) Порядок подключения я выбрал таким, чтобы каждый следующий класс уже имел загруженными все свои зависимые классы на момент запуска. (Этот порядок нетрудно установить, просто печатая в браузер значение $fname в функции __autoload).

Вот результаты тестирования с eAccelerator-ом и без на моем не очень мощном ноутбуке (Apache, mod_php):

  • Подключение всех файлов по одному, eAccelerator выключен: 911 мс.
  • Подключение всех файлов по одному, eAccelerator включен: 435 мс. Занято 15 М кэш-памяти под байт-код.

Как видите, eAccelerator дает примерно двукратное ускорение на 790 файлах общим объемом 4.9 МБ. Слабовато. К тому же, 435 мс - явно чересчур для скрипта, который только и делает, что подключает библиотеки.

А теперь добавим стероидов

Ходят слухи, что PHP гораздо быстрее загружает один большой файл, чем десять маленьких того же суммарного объема. Я решил проверить это утверждение, объединив весь Zend Framework в один файл размером 4.9 МБ и подключив его единственным вызовом require_once. Давайте посмотрим, что получилось.

  • Включение одного большого слитого файла, eAccelerator выключен: 458 мс.
  • Включение одного большого слитого файла, eAccelerator включен: 42 мс. Занято 31 МБ кэш-памяти под байт-код.

Первая строчка говорит о том, что один большой файл размером 4.9 МБ и правда загружается быстрее, чем 790 маленьких: 458 мс против 911 мс (см. выше). Экономия в 2 раза.

А вот вторая строчка заставила меня от удивления подпрыгнуть на стуле и перепроверить результат несколько раз. Надеюсь, это же произойдет и с вами. Действительно, 42 мс - это в 11 раз быстрее, чем с отключенным eAccelerator-ом! Получается, что eAccelerator еще меньше любит мелкие файлы (кстати, даже в режиме eaccelerator.check_mtime=0): экономия в 11 раз.

Итак, мы действительно получили ускорение загрузки в 22 раза, как и было обещано в заголовке. Раньше весь Zend Framework, разбитый на файлы, подключался 911 мс, а с использованием eAccelerator и объединенем всех файлов в один - 42 мс. И это, заметьте, не на реальном сервере, а всего лишь на рядовом ноутбуке.

Вывод: ускорение в 22 раза

Подведем итоги.

  • Слияние всех файлов в один большой плюс включение eAccelerator для этого большого файла дает ускорение в 22 раза при объеме кода 4.9 МБ и числе файлов 790.
  • В случае небольшого числа файлов значительного объема eAccelerator может дать 10-кратное ускорение. Если файлов много, а суммарный объем большой, то ускорение примерно в 2 раза.
  • Расход кэш-памяти зависит от числа файлов разбиения: при фиксированном объеме чем файлов больше, тем расход меньше.

При всем при этом eAccelerator лишен основных проблем «настоящего» FastCGI-сервера. Скрипты избавлены от утечек памяти и запускаются в гарантировано «чистом» окружении. Вам также не надо следить за изменением кода и перезапускать сервер всякий раз, когда внесены модификации в мало-мальски глубинный код системы.

Заметьте также, что мы подключали весь Zend Framework. В реальных скриптах объем кода будет сильно меньше, т.к. обычно для работы требуется лишь незначительная часть ZF. Но даже при условии, что библиотеки занимают 4.9 МБ, мы получаем время загрузки 42 мс - вполне приемлемое для PHP-скрипта. Ведь в нагруженных проектах PHP-скрипты могут работать и несколько сотен миллисекунд (Facebook, Мой Круг и т. д.).

Конечно, если вы планируете запускать FastCGI в PHP не из соображений производительности, а просто чтобы не «завязываться» за Apache и ограничиться связкой «nginx+PHP» или «lighttpd+PHP», ничто этому не мешает. Более того, задействовав eAccelerator для FastCGI+PHP и слив много файлов кода в один большой, вы получите то же самое ускорение, которое я описал выше. Однако не тешьте себя надеждами, что ускорение дал именно FastCGI: это не так. Применяя mod_php+eAccelerator, вы достигли бы практически такого же результата, что и FastCGI+eAccelerator.

Вручную объединять все файлы библиотек в один - утомительное занятие. Лучше написать утилиту, которая будет автоматически анализировать список PHP-файлов, подключенных скриптом, а при следующем запуске - объединять эти файлы и записывать во временную директорию (если это еще не сделано), после чего - подключать по require_once. Сегодня я оставляю написание такой утилиты (плюс-минус детали) на совести читателя.

Также рекомендую вам отказаться от явного включения файлов по require_once и переходить на autoload. Только не пытайтесь использовать для этого Zend_Loader: он очень медленный (по моим замерам, подключение ста файлов отнимет дополнительно около 50 мс). Лучше напишите собственную несложную autoload-функцию, которая будет быстро выполнять всю работу. Autoload позволит вам безопасно объединять несколько файлов библиотек в один, не думая о том, как бороться с «ручными» require_once.

Наконец, применяйте функцию set_include_path() , чтобы код подключения библиотек выглядел вот так:

require_once "Some/Library.php";

require_once LIB_DIR . "/Some/Library.php";

Константы, определяющие путь к директории библиотек явным образом, - большое зло и усложнение программы. Они также косвенно противоречат стандартам кодирования Zend Framework и PEAR, которых я тоже рекомендую по возможности придерживаться.

Итак, хотите использовать «тяжелые» библиотеки в PHP-скриптах - на здоровье! PHP - скриптовый язык, по-настоящему позволяющий это делать без оглядки на неудобства FastCGI и проблемы «встроенных» серверов.