Производительность
Дата обновления перевода 2024-08-01
Производительность
Symfony быстрая сразу после установки. Однако, вы можете сделать её ещё быстрее, если вы оптимизируете ваши сервера и приложения, как объясняется в следующих контрольных списках производительности.
Чек-листы производства
Используйте эти чек-листы, чтобы убедиться, что ваши приложение и сервер сконфигурированы для максимальной производительности:
Чек-лист приложения Symfony:
- Установите APCu Polyfill, если ваш сервер использует APC
- Ограничьте количество локалей, включенных в приложении
Чек-лист сервера производства:
- Сбросьте сервис-контейнер в единый файл
- Используйте кеш байтового кода OPcache
- Сконфигурируйте OPcache для максимальной производительности
- Не проверяйте временные отметки PHP файлов
- Сконфигурируйте кеш реального пути PHP
- Оптимизируйте автозагрузчик Composer
Установите APCu Polyfill, если ваш сервер использует APC
Если ваш сервер производства всё ещё использует наследуемое расширение PHP APC вместо OPcache, установите компоненет APCu Polyfill в вашем приложении, чтобы подключить совместимость с функциями PHP APCu и разблокировать поддержку продвинутых функций Symfony, вроде адаптера кеша APCu.
Ограничьте количество локалей, включенных в приложении
Используйте опцию framework.enabled_locales , чтобы генерировать только файлы переводов, действительно используемых в вашем приложении.
Сбросьте сервис-контейнер в единый файл
Symfony компилирует сервис-контейнер в множество
маленьких файлов по умолчанию. Установит этот параметр как true
. чтобы
компилировать весь контейнер в единый файл, что может улучшить производительность
при использовании "предварительной загрузки классов" в PHP 7.4 или более новых версиях:
1 2 3 4
# config/services.yaml
parameters:
# ...
.container.dumper.inline_factories: true
Tip
Префикс .
обозначает параметр, который используется только во время компиляции контейнера.
Подробнее см. в Параметры конфигурации .
Используйте кеш байтового кода OPcache
OPcache хранит скомпилированные PHP файлы, чтобы избежать их повторной компиляции на каждый запрос. Существуют некоторые доступные кеши байтового кода , но начиная с PHP 5.5, PHP поставляется со встроенным OPcache. Для более старых версий, наиболее используемым кешем байтового кода является APC.
Используйте предварительную загрузку классов OPcache
Начиная с PHP 7.4, OPcache может компилировать и загружать классы при запуске, чтобы сделать их более доступными для всех запросов до перезапуска сервера, что значительно улучшает производительность.
Во время компиляции контейнера (например, при выполнении команды cache:clear
),
Symfony генерирует файл со списком классов для предварительной загрузки в каталоге
var/cache/
. Вместо того, чтобы использовать этот файл напрямую, используйте файл
config/preload.php
, который создается, при использовании Symfony Flex в вашем проекте:
1 2 3 4 5
; php.ini
opcache.preload=/path/to/project/config/preload.php
; required for opcache.preload:
opcache.preload_user=www-data
Если этого файла нет, выполните эту команду, чтобы переустановить рецепт Symfony Flex:
composer recipes:install symfony/framework-bundle --force -v
.
Используйте теги сервиса container.preload и container.no_preload , чтобы определить, какие классы должны и не должны быть предварительно загружены PHP.
Сконфигурируйте OPcache для максимальной производительности
Конфигурация OPcache по умолчанию не подходит для приложений Symfony, поэтому реокмендуется изменить следующие настройки таким образом:
1 2 3 4 5 6
; php.ini
; максимальная память, которую может использовать OPcache для хранения скомпилированных PHP файлов
opcache.memory_consumption=256
; максимальное количество файлов, которое может храниться в кеше
opcache.max_accelerated_files=20000
Не проверяйте временные отметки PHP файлов
На серверах производства, PHP файлы никогда не должны изменяться, кроме случаев развёртывания новой версии приложения. Однако, по умолчанию, OPcache проверяет, не изменили ли файлы своё содержание с тех пор, как они были кешированы. Эта проверка вводит некоторую перенагрузку, которую можно избежать таким образом:
1 2
; php.ini
opcache.validate_timestamps=0
После каждого развёртывания, вам нужно опустощать и регенерировать кеш OPcache. Иначе вы не будете видеть обновления, сделанные в приложении. Учитывая,что в PHP CLI и веб-процессы не имеют общего OPcache, вы не можете очистить веб-сервер OPcache выполнив некотрую команду в вашем терминале. Вот некоторые из возможных решений:
- Перезапустите веб-сервер;
- Вызовите функции
apc_clear_cache()
илиopcache_reset()
через веб-сервер (т.е. имея их в скрипте, который вы выполняете через сеть); - Используйте утилиту cachetool, чтобы контролировать APC и OPcache из CLI.
Сконфигурируйте кеш PHP
Когда относительный путь трансформируется в настоящий и абсолютный, PHP кеширует результат, чтобы улучшить производительность. Приложения, которые открывают много PHP файлов, например, проекты Symfony, должы использновать хотя бы эти значения:
1 2 3 4 5 6
; php.ini
; максимальная память, отведенная под хранение результатов
realpath_cache_size=4096K
; сохранять результаты на 10 минут (600 секунд)
realpath_cache_ttl=600
Note
PHP отключает кеш realpath
, когда включена опция конфигурации open_basedir.
Оптимизируйте автозагрузчик Composer
Загрузчик класса, используемый во время разработки приложения, оптимизирован
для поиска новых и изменённых классов. На серверах производства, PHP файлы
никогда не должны изменяться, разве что развёртывается новая версия приложения.
Поэтому вы можете оптимизировать автозагрузчик Composer, чтобы один раз сканировать
приложение полностью и построить "карту класса", которая является большим массивом
расположений всех классов и хранится в vendor/composer/autoload_classmap.php
.
Выполните эту команду, чтобы сгенерировать карту класса (и также сделать её частью вашего процесса развёртывания):
1
$ composer dump-autoload --no-dev --classmap-authoritative
--no-dev
исключает классы, которые нужны только в окружении разработки (например, зависимостиrequire-dev
и правилаautoload-dev
);--classmap-authoritative
срздает в вашем приложении карту классов для классов, совместимых с PSR-0 и PSR-4, а также предотвращает Composer от сканирования файловой системы на предмет классов, которые не найдены в карте классов. (см: Оптимизация автозагрузчика Composer).
Отключение сброса контейнера как XML в режиме отладки
В режиме отладки , Symfony генерирует XML-файл со всей информацией
сервис-контейнера (сервисы, аргументы и т.д.).
Этот XML-файл используется различными командами отладки, такими как debug:container
и debug:autowiring
.
Когда контейнер становится все больше и больше, увеличивается и размер файла, и время на его генерирование. Если польза от этого XML-файла не перевешивает снижение производительности, вы можете прекратить генерировать файл следующим образом:
1 2 3 4
# config/services.yaml
parameters:
# ...
debug.container.dump: false
Профилирование приложений Symfony
Профилирование с Blackfire
Blackfire - лучший инструмент для профилирования и оптимизации производительности приложений Symfony во время разработки, тестирования и производства. Это коммерческий сервис, который однако предоставляет бесплатные функции, которые вы можете использовать, чтобы найти уязвимые места в ваших проектах.
Профилирование с Symfony Stopwatch
Symfony предоставляет базовый профилировщик производительности в конфигурации окружения разработки. Нажмите на "временную панель" панели инструментов веб-отладки , чтобы увидеть, сколько времени Symfony потратила на задачи вроде запросов в БД и отображения шаблонов.
Вы можете измерить время выполнения и потребление памяти вашего собственного кода, и отобразить результат в профилировщике Symfony благодаря компоненту Stopwatch.
При использовании автомонтирования , добавьте к любому
контроллеру или аргументу сервиса класс Stopwatch,
и Symfony внедрит сервис debug.stopwatch
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
use Symfony\Component\Stopwatch\Stopwatch;
class DataExporter
{
public function __construct(
private Stopwatch $stopwatch,
) {
}
public function export(): void
{
// аргумент является именем "profiling event"
$this->stopwatch->start('export-data');
// ...сделайте что-то, чтобы экспортировать данные...
// сбросьте секундомер, чтобы удалить уже измеренные данные
// $this->stopwatch->reset();
$this->stopwatch->stop('export-data');
}
}
Если запрос вызывает этот сервис во время своего выполнения, вы увидите
новое событие под названием export-data
в профилировщике Symfony.
Методы start()
, stop()
and getEvent()
возвращают объект
StopwatchEvent, который предоставляет
информацию о текущем событии, даже если оно еще выполняется. Этот объект можно
преобразовать в строку для краткого содержания:
1 2
// ...
dump((string) $this->stopwatch->getEvent()); // dumps e.g. '4.50 MiB - 26 ms'
Вы также можете профилировать свой код шаблонов с помощью тега Twig stopwatch :
1 2 3 4 5
{% stopwatch 'render-blog-posts' %}
{% for post in blog_posts %}
{# ... #}
{% endfor %}
{% endstopwatch %}
Категории профилирования
Используйте второй необязательный аргумент метода start()
, чтобы определить
категорию или тег события. Это помогает организовывать события по типу:
1
$this->stopwatch->start('export-data', 'export');
Периоды профилирования
Реальный секундомер имеет не только кнопку старт/стоп, но и кнопку "круг", чтобы
ищмерять каждый отдельный круг. Это именно то, что делает метод lap()
, что
останавливает событие, а затем немедленно перезапускает его:
1 2 3 4 5 6 7 8 9 10 11 12
$this->stopwatch->start('process-data-records', 'export');
foreach ($records as $record) {
// ... сюда идёт какой-то код
$this->stopwatch->lap('process-data-records');
}
$event = $this->stopwatch->stop('process-data-records');
// $event->getDuration(), $event->getMemory(), etc.
// Информация о круге хранится как "периоды" в событии:
// $event->getPeriods();
Разделы профилирования
Разделы - это способ разделять хронометраж профиля по группам. К примеру:
1 2 3 4 5 6 7 8 9 10
$this->stopwatch->openSection();
$this->stopwatch->start('validating-file', 'validation');
$this->stopwatch->stopSection('parsing');
$events = $this->stopwatch->getSectionEvents('parsing');
// позже вы можете повторно открыть раздел, передав его имя методу openSection()
$this->stopwatch->openSection('parsing');
$this->stopwatch->start('processing-file');
$this->stopwatch->stopSection('parsing');
Все события, которые не принадлежат ни к одному именованному разделу, добавляются в
специальный раздел под названием __root__
. Таким образом вы можете получить все
события секундомера, даже если вы не знаете их имен, как показано ниже:
1 2 3
foreach($this->stopwatch->getSectionEvents('__root__') as $event) {
echo (string) $event;
}