[Перевод] Предварительная загрузка в PHP 7.4

Перевод данной статьи подготовлен специально для студентов курса «Backend разработчик на PHP».

xayevs76f7sr7gyuummrtetx4ym.png

В 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 или по электронной почте.

Традиционно ждем ваши комментарии и плюсы, если считаете статью интересной :-)

© Habrahabr.ru