Продуктивність
Дата оновлення перекладу 2024-06-10
Продуктивність
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 23 24
use Symfony\Component\Stopwatch\Stopwatch;
class DataExporter
{
private $stopwatch;
public function __construct(Stopwatch $stopwatch)
{
$this->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()
та 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;
}