Верх страницы
Обложка к записи Измерение производительности в Laravel
Время для прочтения: 1 мин. 11 сек.

Измерение производительности в Laravel

При разработке приложений на Laravel мне часто нужно измерять время выполнения различных блоков кода.

Это может потребоваться при экспериментировании с различными подходами к решению проблемы или оптимизации существующего кода.

Хотя доступны специализированные инструменты профилирования и мониторинга производительности, иногда мне нужно лишь примерное представление о производительности, а не детальный анализ. Именно здесь пригодится класс Illuminate\Support\Benchmark.

Класс Benchmark предоставляет несколько простых способов измерения времени выполнения куска кода.

В этой статье мы рассмотрим, как использовать класс Benchmark в Laravel для измерения производительности различных блоков кода, а также важные моменты, которые следует учитывать при его использовании.

Использование класса Benchmark

Как я упомянул, я довольно часто использую класс Benchmark при разработке новых функций и экспериментировании с различными подходами к решению проблем. Иногда у меня есть идея о том, как реализовать функцию, но я не уверен, будет ли это достаточно эффективно. В этом случае я могу попробовать несколько различных реализаций и использовать класс Benchmark для измерения их времени выполнения. Это позволяет мне принять более обоснованное решение о том, какой подход использовать, основываясь на реальных цифрах, а не просто на интуиции.

Иногда результаты показывают, что различия между реализациями незначительны и вряд ли окажут существенное влияние на общую производительность. В других случаях результаты могут быть довольно неожиданными, выявляя, что один подход значительно быстрее другого. В таких случаях использование класса Benchmark может быть невероятно полезным для выявления потенциальных узких мест до того, как они станут проблемой в продуктиве.

Вы можете взаимодействовать с классом Benchmark тремя различными способами:

  • Benchmark::measure() — Измеряет время выполнения и возвращает его в виде значения float (в миллисекундах).
  • Benchmark::value() — Измеряет время выполнения и возвращает как результат кода, так и время выполнения в виде значения float (в миллисекундах).
  • Benchmark::dd() — Измеряет время выполнения и выводит его с помощью dd() в виде отформатированной строки (в миллисекундах).

Давайте рассмотрим каждый из этих методов. Мы будем использовать простые примеры (вдохновленные примерами из документации Laravel), которые хорошо демонстрируют класс Illuminate\Support\Benchmark.

Использование «Benchmark::measure()»

Метод Benchmark::measure() принимает замыкание (или массив closures, о котором мы поговорим позже) и возвращает время выполнения в виде значения float (в миллисекундах).

Например:

use App\Models\User;
use Illuminate\Support\Benchmark;

$executionTime = Benchmark::measure(fn () => User::find(1));

// $executionTime будет: 0.271625

// Это означает, что код выполнялся примерно 0.27 миллисекунды.

Он также принимает второй аргумент iterations, который указывает, сколько раз запустить измеренный код. Возвращаемое значение будет средним временем выполнения всех итераций, что очень полезно для получения более точного измерения, поскольку отдельные запуски могут быть затронуты такими факторами, как нагрузка на ЦПУ и использование памяти.

Вы можете указать количество итераций следующим образом:

use App\Models\User;
use Illuminate\Support\Benchmark;

$executionTime = Benchmark::measure(
    benchmarkables: fn () => User::find(1),
    iterations: 10,
);

// $executionTime будет: 0.271625

// Это означает, что код выполнялся в среднем примерно 0.27 миллисекунды.

Вы также можете передать массив closures для тестирования нескольких кусков кода одновременно. Это очень удобно для сравнения производительности различных реализаций рядом. Например:

use App\Models\User;
use Illuminate\Support\Benchmark;

$executionTime = Benchmark::measure(
    benchmarkables: [
        fn () => User::count(),
        fn () => User::all()->count(),
    ],
    iterations: 10,
);

// $executionTime будет: [0.5, 20.0]

// Это означает, что первый closure выполнялся примерно 0.5 миллисекунды,
// а второй closure выполнялся примерно 20.0 миллисекунды.

Использование «Benchmark::value()»

Метод Benchmark::value() похож на Benchmark::measure(), но он также возвращает результат измеренного кода вместе с временем выполнения.

Он принимает closure и возвращает массив, содержащий два элемента:

  • Возвращаемое значение closure.
  • Время выполнения в виде значения float (в миллисекундах).

Вот пример:

use App\Models\User;
use Illuminate\Support\Benchmark;

[$user, $executionTime] = Benchmark::value(fn () => User::find(1));

// $user будет моделью App\Models\User с ID 1.
// $executionTime будет: 0.271625
// Это означает, что код выполнялся примерно 0.27 миллисекунды.

В отличие от методов Benchmark::measure() и Benchmark::dd(), Benchmark::value() не поддерживает передачу массива closures или запуск нескольких итераций. Он предназначен для измерения одного куска кода и возврата его результата вместе с временем выполнения. Это отличный вариант, когда вы хотите измерить производительность конкретной операции и также использовать ее результат в своем коде без нарушения потока.

Использование «Benchmark::dd()»

Метод Benchmark::dd() — это быстрый и легкий способ измерить время выполнения куска кода и вывести результат с помощью функции dd() в Laravel.

Это работает почти так же, как метод Benchmark::measure(), но вместо возврата времени выполнения он выводит его прямо на экран.

Важно: Этот метод возвращает время выполнения в виде отформатированной строки (в миллисекундах), а не как сырое значение float; например, "0.270ms" вместо 0.270625.

Давайте посмотрим пример того, как это использовать:

use App\Models\User;
use Illuminate\Support\Benchmark;

Benchmark::dd(fn () => User::find(1));

// Будет выведено: "0.270ms"

Подобно методу Benchmark::measure(), вы также можете указать количество итераций для запуска измеренного кода, и будет возвращено среднее время выполнения:

use App\Models\User;
use Illuminate\Support\Benchmark;

Benchmark::dd(
    benchmarkables: fn () => User::find(1),
    iterations: 10,
);

// Будет выведено: "0.270ms"

Вы также можете передать массив closures для тестирования нескольких кусков кода одновременно:

use App\Models\User;
use Illuminate\Support\Benchmark;

Benchmark::dd(
    benchmarkables: [
        fn () => User::count(),
        fn () => User::all()->count(),
    ],
    iterations: 10,
);

// Будет выведено: ["0.5ms", "20.0ms"]

Не требуются внешние инструменты

Одно из преимуществ использования класса Illuminate\Support\Benchmark состоит в том, что вам не нужно настраивать какие-либо внешние инструменты или сервисы для начала работы. Вы можете использовать его прямо в вашем приложении Laravel, что облегчает его интеграцию в существующий рабочий процесс.

Как фрилансера Laravel-разработчика, меня часто приглашают для работы над существующими проектами. Иногда клиент может не хотеть отправлять данные на внешний сервис по причинам конфиденциальности или безопасности. В других случаях они могут не иметь бюджета для оплаты специализированного инструмента профилирования. Аналогично, может быть сложно заставить приложение работать с внешним инструментом из-за ограничений инфраструктуры или сетевых проблем, или из-за необходимости установки/обновления пакетов Composer или расширений PHP. Или, возможно, вы просто хотите быстро измерить производительность куска кода без хлопот по настройке внешнего инструмента.

Итак, в этих случаях класс Benchmark — отличная альтернатива.

Важные моменты

Хотя класс Benchmark — это удобный инструмент для измерения производительности, необходимо помнить о нескольких важных моментах при его использовании.

Класс не замена для специализированных инструментов профилирования

Важно помнить, что класс Benchmark измеряет только время выполнения и должен использоваться как руководство, а не как абсолют. Он не предоставляет другие важные метрики производительности, такие как использование памяти, количество запросов базы данных и так далее. По этим причинам это не полноценная замена надлежащему профилированию и мониторингу производительности с помощью инструментов, таких как Inspector, Laravel Nightwatch или Blackfire. Поэтому, если вы ищете более глубокий анализ производительности, я рекомендую использовать один из этих инструментов.

Однако это не означает, что класс Benchmark не полезен. Это все еще фантастический инструмент, который может помочь вам быстро экспериментировать с различными подходами и оптимизациями.

Локальное и тестирование в продуктиве

Еще один ключевой момент, который нужно помнить, — это то, что независимо от того, насколько близко вы приблизили вашу локальную среду к продуктиву, всегда будут различия, которые могут влиять на производительность. Такие факторы, как инфраструктура, возможности оборудования, сетевая конфигурация и задержка, размер набора данных и паттерны трафика, могут все влиять на производительность способами, которые трудно воспроизвести локально. Поэтому всегда рассматривайте результаты как руководство, а не как абсолют при локальном тестировании. В противном случае вы можете принять решение на основе неточных данных.

Если вы не хотите или не можете использовать внешний инструмент мониторинга производительности, класс Benchmark — это все еще отличный способ быстро измерить производительность в продуктовой-среде. Например, вы можете использовать его для измерения производительности новой функции или оптимизации после развертывания ее в продуктиве, и зарегистрировать результаты для последующего анализа. Таким образом, вы можете получить лучшее представление о том, как ваши изменения работают в реальном мире. Но помните, что результаты не будут такими надежными или детальными, как надлежащий инструмент профилирования.

Запуск бенчмарков несколько раз

В дополнение к тому, чтобы убедиться, что бенчмарки запускаются несколько раз за один запуск (через параметр iterations), я также люблю запускать весь бенчмарк несколько раз.

Это помогает учесть любую вариативность производительности, вызванную внешними факторами. Например, ваша машина могла запускать другие несвязанные процессы в фоне, пока вы запускали бенчмарк, что могло замедлить ее работу.

Для примера, давайте скажем, что вы запускаете следующий код локально:

use App\Models\User;
use Illuminate\Support\Benchmark;

$executionTime = Benchmark::measure(
    benchmarkables: fn () => User::find(1),
    iterations: 10,
);

Это означает, что измеренный код будет запущен 10 раз, и будет возвращено среднее время выполнения. Но если ваша машина делала что-то ресурсоемкое в фоне, пока все эти 10 closures выполнялись, все запуски могут выглядеть медленнее, чем они на самом деле. Поэтому я люблю подождать примерно 30 секунд после запуска бенчмарков, а затем запустить их снова. Я могу делать это 2 или 3 раза всего.

В большинстве случаев результаты будут похожи на все запуски. Но в прошлом у меня были случаи, когда первый запуск был значительно медленнее, чем последующие запуски, особенно на моей старой машине, которая не имела столько вычислительной мощности или памяти, как моя текущая. Если бы я принял результаты только из первого запуска, я мог бы принять решение на основе неточных данных.

Заключение

В этой статье мы рассмотрели, как использовать класс Illuminate\Support\Benchmark в Laravel для измерения производительности различных кусков кода. Мы также обсудили некоторые важные моменты для его использования.

Продолжайте создавать отличные вещи!

Комментарии
Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Предыдущая запись
Следующая запись

Давайте дружить
в Telegram

Авторский блог вашего покорного слуги в Telegram про web, программирование, алгоритмы, инструменты разработчика, WordPress, Joomla, Opencart, Symfony, Laravel, Moonshine, фильмы и сериалы