Перевод данной статьи подготовлен специально для студентов курса «Backend разработчик на PHP».
В PHP 7.4 добавлена предварительная загрузка — возможность, которая позволяет значительно повысить производительность кода.
О предзагрузке в двух словах.
- Для предварительной загрузки файлов вам потребуется написать отдельный PHP-скрипт.
- Этот скрипт выполняется однократно при запуске сервера.
- Все предварительно загруженные файлы доступны в памяти для всех запросов.
- Изменения, внесенные в исходный файл, не подействуют, пока вы не перезапустите сервер.
Поговорим о новой возможности подробнее.
Больше, чем Opcache
Да, предварительная загрузка основывается на opcache, но это не совсем одно и то же. Opcache берет исходные файлы PHP, компилирует их в опкоды, после чего сохраняет скомпилированные файлы на диск.
Опкоды можно считать низкоуровневым представлением вашего кода, которое легко интерпретируется во время выполнения. Таким образом, opcache позволяет пропустить этап трансляции исходных файлов в то, что, собственно, и необходимо интерпретатору PHP во время выполнения. Заметная экономия!
Тем не менее можно сэкономить еще больше. Скомпилированные при помощи opcash
файлы ничего не знают о других файлах. Если у вас есть класс А, являющийся расширением класса B, их все равно нужно будет связать во время выполнения. Кроме того, opcache проверяет, изменились ли исходные файлы, и при обнаружения изменений аннулирует их кэши.
И тут на помощь приходит предварительная загрузка: она не только компилирует исходные файлы в опкоды, но и связывает зависимые классы, трейты и интерфейсы. Она сохраняет такой «скомпилированный» фрагмент исполняемого кода (то есть кода, который может использовать PHP-интерпретатор) в памяти.
Когда на сервер поступает запрос, тот может использовать части кодовой базы, которая уже была загружена в память, без лишних затрат времени.
О каких же «частях кодовой базы» идет речь?
Предварительная загрузка на практике
Для корректной предварительной загрузки разработчик должен указать серверу, какие файлы нужно загрузить. Это делается с помощью простого PHP-скрипта, так что бояться нечего.
Ничего сложного.
- Вы предоставляете скрипт предварительной загрузки и даете ссылку на него в вашем файле php.ini с помощью
opcache.preload
. - Каждый PHP-файл, который вы хотите предварительно загрузить, нужно передать в
opcache_compile_file()
из скрипта предварительной загрузки.
Допустим, вы хотите предварительно загрузить какой-нибудь фреймворк. Пусть это будет Laravel. В этом случае ваш скрипт должен просмотреть все PHP-файлы в директории vendor/laravel
и добавить их поочередно.
Вот как вы можете подключить этот скрипт в php.ini:
opcache.preload=/path/to/project/preload.php
А вот пример реализации:
$files = /* Массив файлов, которые вы хотите предварительно загрузить */;
foreach ($files as $file) {
opcache_compile_file($file);
}
Вместо opcache_compile_file
вы можете использовать include
. Однако, похоже, здесь не обошлось без бага, так как на момент написания статьи второй вариант не сработал.
Предупреждение о невозможности предварительно загрузить несвязанный класс
Появилось предупреждение Can't preload unlinked class? Дело в том, что перед предзагрузкой файлов нужно также предварительно загрузить их зависимые объекты — интерфейсы, трейты и родительские классы.
Если возникнут какие-либо проблемы с зависимостями классов, то вас предупредят об этом при запуске сервера:
Can't preload unlinked class
IlluminateDatabaseQueryJoinClause:
Unknown parent
IlluminateDatabaseQueryBuilder
Обратите внимание, что opcache_compile_file()
только распарсит файл, но не выполнит его. Это значит, что если у класса есть зависимости, которые не были предварительно загружены, то и сам класс не может быть предварительно загружен.
Это не критично: сервер будет работать, как обычно, но в вашем распоряжении не будет всех файлов, которые вы хотели предварительно загрузить.
Именно поэтому нужно внимательнее подбирать файлы для предварительной загрузки, чтобы избежать проблем с зависимостями. Делать это вручную — задача неблагодарная и трудоемкая, так что разработчики уже трудятся над автоматизированными решениями.
Поддержка composer
Наиболее перспективное автоматизированное решение готовят разработчики composer, который уже используется в большинстве современных PHP-проектов.
Сейчас ребята работают над возможностью настройки предварительной загрузки в composer.json
, которая в свою очередь сгенерирует вместо вас файл предварительной загрузки. Как и сама предварительная загрузка, эта функция все еще находится на стадии разработки. Следить за развитием событий можно здесь.
К счастью, вам не придется вручную настраивать файлы предварительной загрузки, если вы этого не хотите, — composer сможет сделать это за вас.
Требования к серверу
Есть еще два важных момента, о которых должен помнить разработчик при использовании предварительной загрузки.
Вы уже знаете, что нужно создать запись в php.ini
, чтобы предварительная загрузка сработала. Это значит, что если вы используете shared-хостинг, то не сможете настраивать PHP как вздумается.
На практике вам понадобится выделенный (виртуальный) сервер, чтобы оптимизировать предварительно загруженные файлы для отдельного проекта. Имейте это в виду.
Также помните, что вам нужно будет перезапускать сервер (если вы используете php-fpm
, этого достаточно) каждый раз, когда вы захотите перезагрузить файлы в памяти. Для большинства это очевидно, но не будет лишним напомнить.
Производительность
Теперь перейдем к самому важному вопросу: действительно ли предварительная загрузка повышает производительность?
Разумеется! Бен Морел (Ben Morel) поделился результатами сравнительного тестирования, которые можно найти в той же теме по composer, на которую мы ссылались выше.
И еще, что интересно. При желании вы можете предварительно загрузить только так называемые hot classes
— классы, которые часто используются в вашей кодовой базе. Тесты Бена Морела показывают, что загрузка всего около 100 таких классов обеспечивает более высокий рост производительности, чем предварительная загрузка всего сразу. В первом случае производительность повышается на 17 %, во втором — на 13 %.
Разумеется, выбор классов для предварительной загрузки зависит от конкретного проекта. Разумнее всего для начала просто предварительно загрузить как можно больше. Если же вам так важны эти несколько процентов разницы в производительности, то придется контролировать код во время выполнения.
Все эти операции, конечно же, можно автоматизировать, и это, вероятно, будет сделано в будущем.
Сейчас важно, что в composer
будет добавлена поддержка предзагрузки, что избавит от необходимости самостоятельно создавать файлы для нее. Эту функцию очень легко настроить на сервере при условии, что он находится в полном вашем распоряжении.
________________________________________
Будете ли вы использовать предварительную загрузку в новой версии PHP 7.4? Появились мысли или замечания? Напишите мне в Twitter или по электронной почте.
Традиционно ждем ваши комментарии и плюсы, если считаете статью интересной :-)
Автор: MaxRokatansky