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

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

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

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

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

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

Ниже перечислены основные действия по повышению производительности для PHP 5 версии:

Действия по оптимизации расхода оперативной памяти:

  1. Анализ результатов работы ваших функций. Перед написанием функции проверьте, не существует ли стандартный аналог.
  2. Освобождение памяти в случае окончания использования больших массивов или объектов только в глобальной области видимости (в локальной области видимости память будет освобождена автоматически). Обратите внимание, что функция unset() удаляет переменную из области видимости и, только в случае отсутствия на объект других ссылок, освобождает занимаемую объектом память. Присвоение переменной значения null всегда уничтожает объект и освобождает занимаемую объектом память, независимо от того, имеются ли ещё ссылки на этот объект. При этом переменная не будет удалена из области видимости, т.е. фактически переменная будет содержать неопределенное (нулевое) значение и, соответственно, занимать память на содержание этой переменной (порядка 72 байт).
  3. Анализ оправданности использования ООП (объектно-ориентированного программирования). Перед написанием объектно-ориентированного кода, задайте себе два вопроса: «нужен ли здесь объектно-ориентированный подход?» и «могу ли я писать объектно-ориентированный код?». Например, определение статической функции внутри класса увеличивает объем памяти, необходимой только для содержания этой функции, на 10-18%. Использование в качестве структуры массива, а не класса, также позволяет сэкономить память. Возможно, будет выгоднее просто вынести функции в отдельный файл, а не реализовывать их в качестве методов класса.
  4. Анализ возможности реализации статической версии метода в классе. Если метод не использует параметр $this , то он должен быть объявлен с использованием ключевого слова static .

Действия по увеличению скорости исполнения кода:

  1. Анализ оптимизированности SQL-запросов. В большинстве проектов именно оптимизация SQL-запросов дает наибольшее увеличение производительности.
  2. Использование буферизации вывода, всевозможных кеширующих модулей, позволяет увеличить производительность на 25%-100%.
  3. Использование более коротких коротких имен для переменных, функций, констант и классов может повысить производительность до 20%. В то же время не забывайте о дальнейшей поддержке кода, говорящее имя функции намного удобнее при модификациях кода.
  4. Проверка существования переменной (функция isset() ) перед обращением к ней. Подавление ошибки, возникающей при обращении к несуществующей переменной, путем использования @ сильно снижает производительность.
  5. Использование "одинарных кавычек" позволяет интерпретировать код быстрее, т.к. в случае "двойных кавычек" внутри строки ведется поиск переменных
  6. Анализ возможности выноса «лишних» функций из цикла. Например, замена функции count() на переменную, вычисленную до начала цикла и содержащую результат этой функций, в выражении for($i=0; $i повысит производительность этого цикла. В противном случае функция count() будет вызываться и выполняться на каждой итерации цикла.
  7. Использование оператора case вместо множественного использования конструкции if...else .
  8. Использование явного обращения к полям массива. Обращение вида $array["id"] выполняется в 7 раз быстрее, чем обращение $array . Кроме того, это защищает от ошибок при дальнейшей поддержке скрипта, т.к. в один прекрасный день может появиться константа с именем id .
  9. Использование дополнительной переменной, содержащей ссылку на конечный массив, при обработке многомерных массивов в цикле. Для ускорения цикла for($i = 0; $i < 5; $i++) $a["b"]["c"][$i] = func($i); , до начала цикла возможно записать следующую инструкцию $item =p$a["b"]["c"] и переписать цикл так: for($i = 0; $i < 5; $i++) $ref[$i] = $i; .
  10. Использование модулей Apache mod_gzip и mod_deflate позволяет сократить трафик, за счет чего увеличится скорость загрузки страниц.

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

  1. Использование ++$i вместо $i++ в циклах дает прирост производительности в 6%.
  2. Использование "двойных кавычек" для конкатенации (склеивания) переменных. Инструкция вида $s="$s1$s2$s3" интерпретируется быстрее, чем $s=$s1.$s2.$s3 . Это утверждение справедливо только для трех и более переменных.
  3. Использование полных путей в инструкциях include и require позволит тратить меньшее время на поиск системой реального пути.
  4. Закрытие открытых коннектов к базе данных после того как необходимость в них отпадает. В то же время не следует много раз подключаться к одной и той же базе данных.
  5. Анализ возможности замены include() и include_once() на require() и require_once() соответственно.
  6. Использование HTML-вставок в код, вместо вывода значительного объема oстатическихo строк (не содержащих результатов работы кода). Вообще, скорость выдачи статической страницы (HTML), в несколько раз быстрее выдачи страницы написанной на PHP. Но здесь не стоит увлекаться, т.к. ввод в интерпретатора в режим обработки PHP и вывод из него также нагружают сервер.
  7. Анализ возможности замены функций preg_replace и str_replace в некоторых случаях. Функция str_replace работает быстрее, чем preg_replace , и в тоже время функция strtr быстрее функции str_replace . Также, использование строковых функций strncasecmp , strpbrk и stripos более оптимально, чем использование регулярных выражений. Однако, вместо вложенности этих функций, следует использовать именно функции регулярных выражений.
  8. Использование явной инициализации переменных. Например, инкремент неинициализироанной переменной в 9-10 раз медленнее, чем предварительно инициализированной. Кроме того, при явной инициализации переменных возникает меньше ошибок.
  9. В качестве заключения хотелось бы отметить, что использование конструкции echo , вместо функции print , не дает ощутимого роста производительности.
  • Для определения времени начала исполнения скрипта, вместо функций, возвращающих текущее время, предпочтительнее использование $_SERVER["REQUEST_TIME"] .
  • Используйте профайлер для определения критических участков кода.

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

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

Всем доброго времени суток.

  1. Если метод может быть статическим, объявляйте его статическим.
  2. echo быстрее, чем print.
  3. Передавайте в echo несколько параметров, вместо того, чтобы использовать конкатенацию строк.
  4. Устанавливайте максимальное количество проходов ваших циклов for до цикла, а не во время его выполнения.
  5. Удаляйте свои переменные для освобождения памяти, тем более, если это большие массивы.
  6. Остерегайтесь магических методов, таких как __set, __get, __autoload.
  7. require_once дорого обходится.
  8. Указывайте полные пути в конструкциях include/require, меньше времени будет тратится на поиск файла.
  9. Если вам необходимо определить время, когда скрипт был запущен, используйте $_SERVER[’REQUEST_TIME’] вместо time().
  10. Старайтесь использовать strncasecmp, strpbrk и stripos вместо регулярных выражений.
  11. str_replace быстрее, чем preg_replace, но strtr быстрее, чем str_replace.
  12. Если функция, как и функции замены строк, может принимать в аргументы как массивы, так и одиночные символы, и если ваш список аргументов не слишком длинный, подумайте над тем, чтобы записать несколько одинаковых выражений замены, проходя один символ за раз, вместо одной строки кода, которая принимает массив как аргумент поиска и замены
  13. Лучше выбирать утверждения при помощи конструкции else if, чем использовать несколько конструкций if.
  14. Подавление ошибок при использовании @ работает очень медленно.
  15. Используйте модуль Apache mod_deflate.
  16. Закрывайте свои соединения с БД, когда закончите работать с ними.
  17. $row["id"] в семь раз быстрее, чем $row.
  18. Сообщения об ошибках дорого стоят
  19. Не используйте функции внутри условия цикла for, например как здесь: for ($x=0; $x < count($array); $x). В данном случае функция count() будет вызываться с каждым проходом цикла.
  20. Инкремент локальной переменной в методе - самый быстрый. Почти также работает инкремент локальной переменной в функции.
  21. Инкремент глобальной переменной в два раза медленее, чем локальной.
  22. Инкремент свойства объекта (т.е. $this->prop++) в три раза медленнее, чем локальной переменной.
  23. Инкремент неопределённой переменной в 9-10 раз медленнее, чем заранее инициализированной.
  24. Объявление глобальной переменной, без использования её в функции, также замедляет работу (примерно на ту же величину, что и инкремент локальной переменной). Вероятно, PHP осуществляет проверку на существование переменной.
  25. Скорость вызов метода, судя по всему, не зависит от количества методов, определённых в классе. Я добавил 10 методов в тестовый класс (до и после тестового метода), без изменения производительности.
  26. Методы в производных классах работают быстрее, чем они же, определённые в базовом классе.
  27. Вызов функции с одним параметром и пустым телом функции в среднем равняется 7-8 инкрементам локальной переменной ($localvar++). Вызов похожего метода, разумеется, около 15 инкрементов.
  28. Ваши строки, определённые при помощи ", а не ", будут интерпретироваться чуть быстрее, т.к. PHP ищет переменные внутри "..", но не "...". Конечно, вы можете использовать это только тогда, когда в вашей строке нет переменных.
  29. Строки, разделённые запятыми, выводятся быстрее, чем строки, разделённые точкой. Примечание: это работает только с функцией echo, которая может принимать несколько строк в качестве аргументов.
  30. PHP-скрипты будут обрабатываться, как минимум, в 2-10 раз медленнее, чем статические HTML-страницы. Попробуйте использовать больше статических HTML-страниц и меньше скриптов.
  31. Ваши PHP-скрипты перекомпилируются каждый раз, если скрипты не кэшируются. Кэширование скриптов обычно увеличивает производительность на 25-100% за счёт удаления времени на компиляцию.
  32. Кэшируйте, насколько это возможно. Используйте memcached — это высокопроизводительная система кэширования объектов в памяти, которая повышает скорость динамических веб-приложений за счёт облегчения загрузки БД. Кэшированный микрокод полезен тем, что позволяет вашему скрипту не компилироваться снова для каждого запроса.
  33. При работе со строками, когда вам понадобится убедиться в том, что строка определённой длины, вы, разумеется, захотите использовать функцию strlen(). Эта функция работает очень быстро, ведь она не выполняет каких-либо вычислений, а лишь возвращает уже известную длину строки, доступную в zval-структуре (внутренняя структура C, используемая при работе с переменными в PHP). Однако потому, что strlen() — функция, она будет работать медленно за счёт вызова некоторых операций, таких как приведение строки в нижний регистр и поиска в хэш-таблице, только после которых будут выполнены основные действия функции. В некоторых случаях вы сможете ускорить свой код за счёт использования хитрости с isset().
    Было: if (strlen($foo) < 5) { echo «Foo is too short»; }
    Стало: if (!isset($foo{5})) { echo «Foo is too short»; }
    Вызов isset() быстрее, чем strlen() потому, что, в отличие от strlen(), isset() - не функция, а языковая конструкция. За счёт этого isset() не имеет практически никаких накладных расходов на определение длины строки.
  34. Инкремент или декремент переменной при помощи $i++ происходит немного медленнее, чем ++$i. Это особая специфика PHP, и не нужно таким образом модифицировать свой C и Java-код думая, что он будет работать быстрее, этого не произойдёт. ++$i будет быстрее в PHP потому, что вместо четырёх команд, как в случае с $i++, вам понадобится только три. Пост-инкремент обычно используется при создании временных переменных, которые затем увеличиваются. В то время, как пре-инкремент увеличивает значение оригинальной переменной. Это один из способов оптимизации PHP-кода в байт-код утилитой Zend Optimizer. Тем не менее, это хорошая идея, поскольку не все байткод-оптимизаторы оптимизируют это, также остаётся немало скриптов, работающих без оптимизации в байткод.
  35. Не всё должно быть ООП, часто это излишне, поскольку каждый метод и объект занимает много памяти.
  36. Не определяйте каждую структуру данных как класс, массивы бывают очень полезны
  37. Не слишком разбивайте методы. Думайте, что вы действительно будете повторно использовать.
  38. Вы всегда можете разбить код на методы позже, по необходимости.
  39. Используйте бесчисленное количество предопределённых функций.
  40. Если в вашем коде есть функции, выполняющиеся очень долго, обдумайте их написание на C в виде расширения
  41. Профилируйте свой код. Профилирование покажет вам, как много времени выполняются части вашего кода.
  42. mod_gzip — модуль Apache, который позволяет сжимать ваши данные на лету и может уменьшить объем передаваемых данных до 80%.

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


Как без особых усилий заставить PHP -код работать на порядок быстрее ? Перед тем как задаваться вопросами кеширования и масштабирования стоит попробовать оптимизировать код. Есть ряд несложных правил:

Еще про оптимизацию....

При вставке кусков PHP-кода в HTML страницы всегда используйте полные открывающие и закрывающие скобки ! Это обезопасит Вас от вариаций настройки php.ini short_open_tag на разных серверах и возможно сэкономит много времени при переносе или загрузке проектов на разные сервера.

Старайтесь использовать функцию вывода echo вместо printf и sprintf там где возможно. Нет надобности использовать эти функции, так как они выполняются медленней потому, что созданы для интерпретации и вывода строки с ее обработкой, подстановкой значений, в отформатированном виде. О чем и говорит буква f в конце названия этих 2-х функций.

Sprintf("мама"); printf("папа");

Echo "мама"; echo "папа";

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

Лучший вариант(самый быстрый)

Echo "Вес равен: ".$weight;

Худший вариант(медленный):

Echo "Вес равен: $weight";

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

$i = 0; if ($i != 0) { //Не равно } else { //Равно }

$i = 0; if ($i > 0) { //Не равно } else { //Равно } Нужно также учитывать, что если строка принимает только пустое значения, либо пользовательские строковые данные, то вместо сравнения строки со строкой, для выявления ее пустоты, так же можно использовать сравнение с нулем, которые выполнится быстрее.

Для проверки строки на пустоту используйте функцию trim($str) ; Она не только проверит заполнена ли строка, но также обрежет несущественные символы - пробелы (табуляции, white-spaces) и вернет положительное значение, в случае если в строке ей действительно какие то значимые символы.

If ($str != "") { //обработка строки }

If (trim($str)) { //обработка строки }

Для получения данных из форм методом Get и Post лучше использовать следующий минимальный набор самописных функций:

GetParam ($array, $value, $default = "") { return (isset($array[$value])) ? $array[$value] : $default; } GetParamSafe ($array, $value, $default = "") { return (isset($array[$value])) ? addslashes($array[$value]) : $default; }

Функция GetParam($_POST, "myvar", "empty") к примеру коректно получит данные из $_POST["myvar"], и в случае если $_POST переменная не существует вернет значение по умолчанию, без всяких Waring и Notice. Фунция GetParamSafe($_POST, "myvar", "empty") делает ту же операцию, только возвращает экранированную переменную. Для защиты от SQL инъекций к примеру. А данная конструкция позволяет получить целочисленное число из $_POST.

Intval(GetParam($_POST, "myvar", "empty")):

В случае если в массиве $_POST лежало совсем не число функия вернет 0;

Для простого сравнения строк не используйте preg_match() или preg_match_all() . Используйте strstr() и strpos() .

При получении строк из базы данных (MySQL к примеру) старайтесь использовать функцию mysql_fetch_object . К примеру при изменении кода запроса с

$query = "SELECT field7, field3 FROM mytable WHERE id = 5" на $query = "SELECT * FROM mytable WHERE id = 5" код вывода строки полученной из этих запросов $row = mysql_fetch_array(mysql_query($query)); echo $row."-->".$row; //перестанет работать, в то время, как $row = mysql_fetch_object(mysql_query($query)); echo $row->field7."-->".$row->field3; // останется работоспособным.

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

При формировании больших запросов вставки данных в БД через insert все строчки старайтесь поместить в один-три insert"а. Выполнение каждой строчки отдельно не только загрузит сервер БД, но и задержит работу Вашего скрипта.

В случае если необходимо в разных местах (разных классах) одной системы использовать одни и те же сложно вычисляемые данные (например которые достаются из БД через запрос с последющей обработкой строк), старайтесь их вычислять единожды, хранить глобально для всей системы и передавать в класс(скрипт) один раз, непосредственно при создании класса (подключении скрипта)

При больших нагрузках на Web-сервер задумайтесь над использованием стандартных решений для включения кэша(кэш-технологии). Например бесплатный PHP класс JCache_Lite_Function.

При проектировании/разработке больших систем отдавайте предпочтение Объектно-Ориентированному программированию с использование шаблонов проектирования. Наиболее частые шаблоны: MVC, PageController, BodyHandler, Fabric...


Читать дальше: