Перевод данной статьи подготовлен специально для студентов курса «Backend разработчик на PHP».
В PHP 7.4 добавлена предварительная загрузка — возможность, которая позволяет значительно повысить производительность кода.
О предзагрузке в двух словах.
Да, предварительная загрузка основывается на opcache, но это не совсем одно и то же. Opcache берет исходные файлы PHP, компилирует их в опкоды, после чего сохраняет скомпилированные файлы на диск.
Опкоды можно считать низкоуровневым представлением вашего кода, которое легко интерпретируется во время выполнения. Таким образом, opcache позволяет пропустить этап трансляции исходных файлов в то, что, собственно, и необходимо интерпретатору PHP во время выполнения. Заметная экономия!
Тем не менее можно сэкономить еще больше. Скомпилированные при помощи
И тут на помощь приходит предварительная загрузка: она не только компилирует исходные файлы в опкоды, но и связывает зависимые классы, трейты и интерфейсы. Она сохраняет такой «скомпилированный» фрагмент исполняемого кода (то есть кода, который может использовать PHP-интерпретатор) в памяти.
Когда на сервер поступает запрос, тот может использовать части кодовой базы, которая уже была загружена в память, без лишних затрат времени.
Для корректной предварительной загрузки разработчик должен указать серверу, какие файлы нужно загрузить. Это делается с помощью простого PHP-скрипта, так что бояться нечего.
Ничего сложного.
Допустим, вы хотите предварительно загрузить какой-нибудь фреймворк. Пусть это будет Laravel. В этом случае ваш скрипт должен просмотреть все PHP-файлы в директории
Вот как вы можете подключить этот скрипт в php.ini:
А вот пример реализации:
Вместо
Появилось предупреждение Can't preload unlinked class? Дело в том, что перед предзагрузкой файлов нужно также предварительно загрузить их зависимые объекты — интерфейсы, трейты и родительские классы.
Если возникнут какие-либо проблемы с зависимостями классов, то вас предупредят об этом при запуске сервера:
Обратите внимание, что
Это не критично: сервер будет работать, как обычно, но в вашем распоряжении не будет всех файлов, которые вы хотели предварительно загрузить.
Именно поэтому нужно внимательнее подбирать файлы для предварительной загрузки, чтобы избежать проблем с зависимостями. Делать это вручную — задача неблагодарная и трудоемкая, так что разработчики уже трудятся над автоматизированными решениями.
Наиболее перспективное автоматизированное решение готовят разработчики composer, который уже используется в большинстве современных PHP-проектов.
Сейчас ребята работают над возможностью настройки предварительной загрузки в
К счастью, вам не придется вручную настраивать файлы предварительной загрузки, если вы этого не хотите, — composer сможет сделать это за вас.
Есть еще два важных момента, о которых должен помнить разработчик при использовании предварительной загрузки.
Вы уже знаете, что нужно создать запись в
На практике вам понадобится выделенный (виртуальный) сервер, чтобы оптимизировать предварительно загруженные файлы для отдельного проекта. Имейте это в виду.
Также помните, что вам нужно будет перезапускать сервер (если вы используете
Теперь перейдем к самому важному вопросу: действительно ли предварительная загрузка повышает производительность?
Разумеется! Бен Морел (Ben Morel) поделился результатами сравнительного тестирования, которые можно найти в той же теме по composer, на которую мы ссылались выше.
И еще, что интересно. При желании вы можете предварительно загрузить только так называемые
Разумеется, выбор классов для предварительной загрузки зависит от конкретного проекта. Разумнее всего для начала просто предварительно загрузить как можно больше. Если же вам так важны эти несколько процентов разницы в производительности, то придется контролировать код во время выполнения.
Все эти операции, конечно же, можно автоматизировать, и это, вероятно, будет сделано в будущем.
Сейчас важно, что в
Будете ли вы использовать предварительную загрузку в новой версии PHP 7.4? Появились мысли или замечания? Напишите мне в Twitter или по электронной почте.
Традиционно ждем ваши комментарии и плюсы, если считаете статью интересной :-)
В 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
Illuminate\Database\Query\JoinClause:
Unknown parent
Illuminate\Database\Query\Builder
Обратите внимание, что
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 или по электронной почте.
Традиционно ждем ваши комментарии и плюсы, если считаете статью интересной :-)