Работа в интернете

Ускорение PHP-программ. Оптимизация PHP – главный признак профессионального кода Действия по оптимизации расхода оперативной памяти

Тема данной статьи - тот факт, что применение 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 и проблемы «встроенных» серверов.

Изучил весь httpd.conf, перекопал кучу гайдов по highload (они старые и с сомнительными советами типа "отключить лишние модули"
Один из первых модулей, который стоит отключить у Apache"а, для скорости - это поддержку файлов.htaccess, сама эта поддержка производительности не добавляет, а наличие этих файлов - уж и подавно.
1) Это у всех VPS так называемый "мощный" процессор медленнее, чем на каком-то жалком хостинге, пусть и с VIP-тарифом?
Нет, возможно это у Вас, персонально, какой-то дрянной VPS-хостер, или того хуже, тариф аки "OpenVZ, мы не перепродаём проданные ресурсы... ну разве что раз 10, но больше не перепродаём"
2) Поможет ли в такой ситуации FastCGI?
FastCGI - это режим работы PHP, напрямую, на производительность в значительной степени он не влияет, более того, сама логика работы FCGI (если сравнивать Apache-FCGI и Apache-mod_php) будет медленнее, по тому как для взаимодействия FastCGI будет использоваться сокет ("обычный" или unix-сокет), что подразумевает сетевое взаимодействие, вместо непосредственной работы интерпретатора PHP "внутри" сервера. Думаю, Вам поможет несколько другое (постараюсь описать ниже).
3) Почему не популярны фишки типа eAccelerator (кеширование AST и т.п.)?
Понятия не имею, почему они не популярны и откуда у Вас такая статистика... Но, возможно, дело в том, что eAccelerator морально и физически устарел, и если верить например, вот такой банальной статье (нет, я не работаю с такой "шедевральной" CMS как "Битрикс", просто это первое упоминание про eAccelerator, которое пришло мне в голову) - с версиями PHP выше 5.3 не работает.
Я знаю, что многие из них заброшены, но это не причина, а следствие.
Не могу прокомментировать это, так как Вы не указали следствие - чего именно. Другими словами, я не совсем понимаю, что Вы хотели этим сказать.
4) Что еще может помочь?
Ну так, сходу, по памяти (варианты могут быть не связаны между собой):
1. Отказ от поддержки.htaccess в Apache или хотя бы сокращение их количества
2. Установка Nginx в качестве фронтального сервера, для отдачи статики
3. Полный отказ от Apache вообще и переход на Nginx+FCGI (только не подумайте, я очень люблю Apache за его гибкость в настройке и широкие возможности, другой вопрос, что мало кому эта гибкость фактически нужна и мало кто способен его грамотно, качественно и полноценно настроить... Nginx в этом плане будет куда попроще). Почему FCGI? По тому, что другой приемлемый способ взаимодействия Nginx"а с PHP мне не известен. Настройка FCGI-пула - обязательна.
4. OpCache - с версии 5.5 встроено "искаропки", к включению и настройке - настоятельно рекомендуется. Я не знаю, как обстоят дела с CMS и используете ли Вы CMS на сайте, но из моей практики, скорость работы PHP-фреймворков возрастает в среднем 8-20 раз.
5. HHVM , как альтернатива
6. Проверка:
а) Того, что дело действительно в PHP. В частности, стоит собрать все логи сервера, например, сколько длились запросы, в БД, их количество и так далее.
б) Проверка скорости работы дисковой подсистемы... Не буду "тыкать пальцем", но одно время я арендовал довольно большое кол-во VPS"ок у одного популярного хостера, и в какой-то момент, я заметил, что средняя скорость работы дисковой подсистемы - 1.4Кбайт/сек., при этом "отказы" (аки "невозможно записать блок") были примерно в 50% случаев... это продлилось не очень долго, но и через несколько месяцев, у этого же хостера, тарифы с "обычным HDD", почему-то обладали более быстрой дисковой подсистемой, нежели тарифы с "быстрыми SSD"... можно сделать выводы...
в) Проверить реальную скорость работы процессора, не редко она отличается от завяленной достаточно сильно.

P.S. Если Вы сформулируете вопрос(ы) более точно - я смогу дать более точные рекомендации, если конечно они Вам нужны:)

P.P.S. Есть вариант решения проблемы вообще "в лоб", самый наверное сложный и пожалуй самый производительный в ряде случаев. Это Varnish + тонкая настройка оного, позволяет выдавать большую часть страниц из кэша (оперативной памяти) за наносекунды, иногда позволяет обслуживать очень много тысяч запросов в минуту, при этом, это не просто кэширование кода или что-то подобное... это кэширование целиком страниц и/или ответов сервера. Среди прочего - позволяет "не трогать бэкенд вообще", т.е. при запросе страницы, может не быть ни обращений к БД, ни выполнения того же PHP (или любого другого) кода, на стороне сервера. Требует довольно тонкой настройки, не очень подходит для сайтов "на CMS", для сайтов на фреймворках - требует изначально корректного подхода к разработке и продумывания того, что и как будет/должно кэшироваться. При некорректном подходе - наиболее вероятный результат - работать будет, но не так быстро как хотелось бы, а часть сайта вообще может перестать нормально функционировать. Есть так же другие решения, но с учётом довольно общих формулировок вопроса - говорить о них довольно сложно.

Ах, да, забыл важную деталь... Почему "хостинги" используют Apache и не откажутся от него (совсем)? В большей степени по тому, что Apache позволяет делегировать часть настроек пользователю через.htaccess. При этом, для статики не редко стоит всё тот же Nginx, который, как Вы понимаете, подобным образом делегировать часть настроек пользователю не позволяет, в виду чего для этих задач не подходит и не "буксует" на этом (в отличии от Apache"а). В т.ч. и по этому, мы на 99% отказались от "хостингов" (по причине наличие Apache"а, и невозможности от него избавиться или самостоятельно настроить, и как следствие "тормозов" которые приходят вместе с подобным подходом).

В этой статье я хочу дать несколько советов, следуя которым можно значительно ускорить обработку 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 позволит вам достичь этих высот.

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

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

Что касается неподвластных явлений, то я имею в виду скорость интернет-соединения, загруженность сервера, настройки ОС на клиентской машине, мощность железа ПК пользователя. На все эти составляющие вы не сможете повлиять. И в итоге получается, что проведенная оптимизация 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. Но для его рассмотрения потребуется отдельная статья. А на сегодня все. До следующей встречи!

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

1. Объединение CSS файлов с помощью PHP .

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

Подготовка

Перед оптимизацией CSS файлов, нам необходимы стили для работы. Давайте создадим несколько файлов с стилями:

// main.css
// CSS для примера

body {
width: 800px;
margin: 0 auto;
color: grey;
}

#wrapper {
margin-top: 30px;
background: url(../images/cats.png);
}
// typography.css
// CSS для примера

body {
font-family: Arial, san-serif;
font-weight: bold;
}

strong {
font-size: 120%;
}
// forms.css
// CSS для примера

form {
position: relative;
top: 400px;
z-index: 99;
}

input {
height: 50px;
width: 400px;
}

Нам необходимо извлечь содержание всех файлов и соединить в определенном порядке. Значит наш скрипт должен получить имена таблиц стилей через УРЛ параметры, открыть эти файлы и соединить.

//Определяем переменные
$cssPath = "./css/";
if (isset($_GET["q"])) {
$files = $_GET["q"];
// Получаем массив файлов


foreach ($files as $key => $file) {
}

$cssData = "";
foreach ($files as $file) {

fclose($fileHandle);
}
}
// Скажи браузеру, что у нас CSS файл
if (isset($cssData)) {
echo $cssData;
} else {
}
?>

//Определяем переменные
// --- NOTE: PATHS NEED TRAILING SLASH ---
$cssPath = "./css/";
if (isset($_GET["q"])) {
$files = $_GET["q"];
// Got the array of files!

//Давайте убедимся, что в файловых именах нет страшных символов:) .
foreach ($files as $key => $file) {
$files[$key] = str_replace(array("/", "\\", "."), "", $file);
}

Этот код устанавливает путь к папке со стилями и проверяет на наличие файлов. Путь к папке должен иметь слеши вначале и в конце названия папки, так как в противном случае у нас будет масса ошибок. Далее мы проверяем каждое имя файла и удаляем точки и/или слеши.

$cssData = "";
foreach ($files as $file) {
$cssFileName = $cssPath . $file . ".css";
$fileHandle = fopen($cssFileName, "r");

$cssData .= "\n" . fread($fileHandle, filesize($cssFileName));
fclose($fileHandle);
}
}

Теперь нам необходимо создать общую таблицу стилей из файлов. Для этого мы запускаем цикл, который просматривает массив файлов, открывает каждый файл и соединяет в единый файл. "\n" добавляет новую строку для порядка и чистоты. Функция filesize() используется для того, чтобы узнать длину файла и передать fread().

// Скажи браузеру, что у нас CSS файл
header("Content-type: text/css");
if (isset($cssData)) {
echo $cssData;
echo "\n\n// Generated: " . date("r");
} else {
echo "// Files not avalable or no files specified.";
}
?>

Последняя часть кода передает все стили браузеру. Это значит, что нам необходимо сказать PHP, что мы передаем CSS информацию и что PHP должно проинформировать об этом браузер. Мы делаем это с помощью функции header(), и устанавливаем Content-type: text/css. Далее мы передаем CSS клиенту. Но перед этим проверяем наличие CSS стилей в файле. Если их нет, значит это означает, что названия CSS файлов не были переданы. Если же у нас есть файлы мы их передаем и добавляем сообщение о генерации.

Проверка

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

Теперь загрузите все файлы в корень сайта и обратитесь к файлу index.php через браузер. Вас должна встретить фраза "Files not available or no files specified" (файлы не доступны или не указаны). Это означет, что мы не дали названия файлов, которые необходимо соединить. Однако, хорошая новость - что все работает без ошибок. Давайте попробуем напечатать в браузере "index.php?q=main". У Вас на экране появится содержание файла main.css. Если нам необходимо извлечь несколько файлов и объединить их, нам необходимо отправить следющий запрос "index.php?q=main&q=forms". Как Вы видите мы можем повторять "q=" сколько угодно раз. Вы можете объединить в один файл хоть 50 таблиц стилей.

Заключение

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

Небольшое предупреждение: если Вы поместите файл index.php в любую папку (не в папку с CSS), тогда Вам необходимо прописывать относительные пути к фоновым изображениям так, как будто index.php является Вашей таблицей стилей. Так будет думать браузер.

2. Удаление пустых строк из HTML и CSS

Многие из нас используют большое количество пустых строк при написании кода. Хорошие новости - пустые строки в PHP не передаются браузеру. Однако, передаются в HTML.

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

Подготовка

Ниже представлены коды для HTML и CSS файлов.

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


Hey a Page!






Lorem Ipsum dol...



body {
min-height: 800px;
background: black;
font-size: 18px;
}
#wrapper {
width: 960px;
margin: 20px auto;
padding: 15px;
}
#header h1 {
text-indent: -99999em;
background: url(../images/header.png);

Display: block;
width: 100%;
height: 48px;
}
#mainbody {
font-weight: bold;
}

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

$fileDirectory = "";
$file = $_GET["q"];
$ext = $nameExplode;

//Проверка для хакеров
die("Hackers...!");
} else {
//Начнем


//Чудеса регулярных выражений

fclose($handle);
//Выводим данные
if ($ext == "css") {
header("Content-type: text/css");
}
echo $newData;
}
?>

Более подробно о каждой части кода

Мы получаем имя файла и проверяем его тип. Далее извлекаем все данные и убираем пробелы и пустые строки. Этот метод наиболее примитивный и не удалит все пустые строки, но он справится с большинством. И это всего несколько строк кода.

$fileDirectory = "";
$file = $_GET["q"];
$nameExplode = explode(".", $file);
$ext = $nameExplode;
$fileName = $fileDirectory . $file;

Этот код задает необходимые переменные. Снова, мы передаем все данные через "q". Тут также определяется папка для файлов.

If ($ext != "css" AND $ext != "htm" AND $ext != "html") {
//Проверка для хакеров
die("Hackers...!");
} else {

Тут мы проверяем действительно ли файл является CSS или HTML.

//Начнем
$handle = fopen($fileName, "r");
$fileData = fread($handle, filesize($fileName));
//Чудеса регулярных выражений
$newData = preg_replace("/\s+/", " ", $fileData);
fclose($handle);
//Выводим данные
if ($ext == "css") {

header("Content-type: text/css");
}
echo $newData;
}
?>

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

Работает ли это?

Если Вы в браузере напечатаете "index.php?q=css.css", то увидите одну линию CSS. Значит все работает! Если откроете исходный код страницы, то увидите такую же картину. С помощью данного метода мы уменьшили 314 символьный CSS файл до 277 символов. HTML файл с 528 до 448 символов. Неплохо для 15 строк кода.

Заключение

Это отличный пример того, как мы можем сделать много используя несколько строк кода. Если Вы взгляните на исходный код таких сайтов как Гугл, то заметите, что там практически нет пустых строк.

3. Кеширование PHP скриптов.

Я покажу Вам как настроить кеширование Ваших скриптов используя пример выше. Цель - ускорить работу сайта. Суть очень проста - данные не будут генерироваться каждый раз при обращении к сайту. Они будут храниться в кеше.

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

Подробнее

$fileDirectory = "";
$file = $_GET["q"];
$nameExplode = explode(".", $file);
$ext = $nameExplode;
$fileName = $fileDirectory . $file;
//-- WE HAVE ENOUGH DATA TO GENERATE A CACHE FILE NAME HERE --
if ($ext != "css" AND $ext != "htm" AND $ext != "html") {
//Check for evil people...
die("Hackers...!");
} else {

//-- WE CAN INTERCEPT AND CHECH FOR THE CACHED VERSION HERE --

//Lets get down to business
$handle = fopen($fileName, "r");
$fileData = fread($handle, filesize($fileName));
//Now for some regex wizardry!
$newData = preg_replace("/\s+/", " ", $fileData);

Fclose($handle);
//Time to output the data.

//-- NOW WE CAN STORE THE NEW DATA IF REQUIRED AND OUTPUT THE DATA --

If ($ext == "css") {
header("Content-type: text/css");
}
echo $newData;
}
?>

$fileDirectory = "";
$file = $_GET["q"];
$nameExplode = explode(".", $file);
$ext = $nameExplode;
$fileName = $fileDirectory . $file;
$cacheName = "./cache/" . $nameExplode . $nameExplode . ".tmp";
if ($ext != "css" AND $ext != "htm" AND $ext != "html") {
//Хакеры
print_r($ext);
die("Hackers...!");
} else {
if (file_exists($cacheName) AND filemtime($cacheName) > (time() - 86400)) {


fclose($cacheHandle);
$isCached = TRUE;
} else {
//Начнем
$handle = fopen($fileName, "r");
$fileData = fread($handle, filesize($fileName));
//Чудеса регулярных выражений
$newData = preg_replace("/\s+/", " ", $fileData);
fclose($handle);
//Кешируем


fclose($cacheHandle);
$isCached = FALSE;
}
//Выводим данные
if ($ext == "css") {
header("Content-type: text/css");
if ($isCached) {

}
} else {
if ($isCached) {
echo "";

}
}
echo $newData;

Объяснение

В данном скрипте была добавлена функция обновления кеша каждые 24 часа. Это удобно. Допустим, если Вы поменяли что-либо на сайте - можете подождать 24 часа или же очистить кеш.

$cacheName = "./cache/" . $nameExplode . $nameExplode . ".tmp";

Этот отрезок кода достает название файлов и их расширения, склеивает их вместе и добавляет в кеш с правильным расширением ".tmp".

If (file_exists($cacheName) AND filemtime($cacheName) > (time() - 86400)) {
$cacheHandle = fopen($cacheName, "r");
$newData = fread($cacheHandle, filesize($cacheName));
fclose($cacheHandle);
$isCached = TRUE;
} else {

Тут мы проверяем наличие сохраненного кеша и был ли он создан в течении последних 24 часов (значение в секундах - можно поменять на любое другое). Если оба условия выполняются, открываем файл и извлекаем содержание, чтобы заменить им результат работы скрипта. Мы также устанавливаем $isCached true для вывода дополнительных сообщений в конце.

//Lets cache!
$cacheHandle = fopen($cacheName, "w+");
fwrite($cacheHandle, $newData);
fclose($cacheHandle);
$isCache = FALSE;
}

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

//Time to output the data.
if ($ext == "css") {
header("Content-type: text/css");
if ($isCached) {
echo "// Retrieved from cache file. \n";
}
} else {
if ($isCached) {
echo "";
}
}

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

Пробуем

Если мы воспользуемся скриптом вновь, мы не увидим изменений до тех пор, пока не обновим страницу. Снизу увидим надпись, что файл взят из кеша.

Заключение

Умение быстро добавить простое кеширование к любому скрипту означает, что вы идете в верном направлении. Скрипт с кешированием существенно снизит нагрузку на любой сервер.

Подводим Итоги

В данном уроке я Вам показал несколько удобных и простых путей ускорения Вашего сайта с помощью PHP.