Как тестировать производительность Python кода с pytest-benchmark

Everett Pompeii

Everett Pompeii


Что такое бенчмаркинг?

Бенчмаркинг — это практика тестирования производительности вашего кода, чтобы увидеть, насколько быстро (задержка) или сколько (пропускная способность) работы он может выполнить. Этот часто упускаемый из виду этап в разработке программного обеспечения является ключевым для создания и поддержания быстрого и производительного кода. Бенчмаркинг предоставляет необходимые метрики, чтобы разработчики могли понять, насколько хорошо их код работает под различными рабочими нагрузками и условиями. По тем же причинам, по которым вы пишете модульные и интеграционные тесты, чтобы предотвратить регрессию функций, вам следует писать тесты производительности, чтобы предотвратить регрессию производительности. Ошибки производительности — это ошибки!

Напишите FizzBuzz на Python

Чтобы написать тесты производительности, нам нужен исходный код для оценки. Для начала мы напишем очень простую программу, FizzBuzz.

Правила для FizzBuzz таковы:

Напишите программу, которая выводит целые числа от 1 до 100 (включительно):

  • Для кратных трём, выводите Fizz
  • Для кратных пяти, выводите Buzz
  • Для кратных и трём и пяти, выводите FizzBuzz
  • Во всех других случаях, выводите число

Есть множество способов написать FizzBuzz. Так что мы выберем мой любимый:

for i in range(1, 101):
if n % 15 == 0:
print("FizzBuzz")
elif n % 3 == 0:
print("Fizz")
elif n % 5 == 0:
print("Buzz")
else:
print(i)
  • Перебирайте числа от 1 до 100, используя диапазон в 101.
  • Для каждого числа вычисляйте остаток от деления для 3 и 5.
  • Если остаток равен 0, значит число является кратным указанного множителя.
    • Если остаток равен 0 для 15, то выводите FizzBuzz.
    • Если остаток равен 0 для 3, то выводите Fizz.
    • Если остаток равен 0 для 5, то выводите Buzz.
  • В противном случае просто выводите число.

Следуйте пошаговому руководству

Чтобы следовать этому пошаговому руководству, вам потребуется установить Python и установить pipenv.

🐰 Исходный код для этого поста доступен на GitHub.

Создайте файл Python с именем game.py и установите его содержимое на вышеуказанную реализацию FizzBuzz.

Затем выполните команду python game.py. Вывод должен выглядеть следующим образом:

$ python game.py
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...
97
98
Fizz
Buzz

🐰 Бум! Вы разбираетесь с кодировочным интервью!

Прежде чем идти дальше, важно обсудить различия между микро-бенчмаркингом и макро-бенчмаркингом.

Микробенчмаркинг vs Макробенчмаркинг

Существует две основные категории бенчмарков программного обеспечения: микробенчмарки и макробенчмарки. Микробенчмарки работают на уровне, аналогичном модульным тестам. Например, бенчмарк для функции, определяющей Fizz, Buzz или FizzBuzz для одного числа, будет микробенчмарком. Макробенчмарки работают на уровне, аналогичном интеграционным тестам. Например, бенчмарк для функции, которая запускает полную игру FizzBuzz, от 1 до 100, будет макробенчмарком.

Вообще, лучше всего тестировать на наименьшем возможном уровне абстракции. В случае бенчмарков это делает их более простыми в поддержке, и помогает уменьшить количество помех в измерениях. Однако, так же как некоторые end-to-end тесты могут быть очень полезными для проверки правильной работы всей системы, макробенчмарки могут быть очень полезными для проверки производительности критически важных мест в вашем программном обеспечении.

Бенчмаркинг в Python

Два популярных варианта для бенчмаркинга в Python это: pytest-benchmark и airspeed velocity (asv)

pytest-benchmark — это мощный инструмент для бенчмаркинга, интегрированный с популярным фреймворком для тестирования pytest. Он позволяет разработчикам измерять и сравнивать производительность их кода, выполняя бенчмарки наряду с модульными тестами. Пользователи могут легко сравнивать результаты своих бенчмарков локально и экспортировать свои результаты в различных форматах, таких как JSON.

airspeed velocity (asv) — это еще один продвинутый инструмент для бенчмаркинга в экосистеме Python. Одним из ключевых преимуществ asv является его способность генерировать детализированные и интерактивные HTML-отчеты, что упрощает визуализацию трендов производительности и выявление регрессий. Кроме того, asv поддерживает относительный непрерывный бенчмаркинг из коробки.

Обе поддерживаются Bencher. Так почему же выбрать pytest-benchmark? pytest-benchmark бесшовно интегрируется с pytest, который является фактическим стандартом для юнит-тестирования в экосистеме Python. Я бы предложил использовать pytest-benchmark для тестирования задержки вашего кода, особенно если вы уже используете pytest. То есть, pytest-benchmark отлично подходит для измерения времени на стеночных часах.

Рефакторинг FizzBuzz

Чтобы протестировать наше приложение FizzBuzz, нам нужно отделить нашу логику от основной программы. Контрольные оболочки не могут протестировать основную программу. Для этого нам нужно внести некоторые изменения.

Давайте реорганизуем нашу логику FizzBuzz в несколько функций:

def play_game(n, should_print):
result = fizz_buzz(n)
if should_print:
print(result)
return result
def fizz_buzz(n):
if n % 15 == 0:
return "FizzBuzz"
elif n % 3 == 0:
return "Fizz"
elif n % 5 == 0:
return "Buzz"
else:
return str(n)
  • play_game: принимает целое число n, вызывает fizz_buzz с этим числом, и если should_print равно True, выводит результат.
  • fizz_buzz: принимает целое число n и выполняет логику Fizz, Buzz, FizzBuzz или просто число, возвращая результат в виде строки.

Затем обновите основное выполнение программы, чтобы оно выглядело так:

game.py
for i in range(1, 101):
play_game(i, True)

Основное выполнение нашей программы перебирает числа от 1 до 100 включительно и вызывает play_game для каждого числа, при этом should_print установлено в True.

Бенчмарк FizzBuzz

Чтобы провести бенчмарк нашего кода, нам нужно создать тестовую функцию, которая будет запускать наш бенчмарк. Внизу файла game.py добавьте следующий код:

def test_game(benchmark):
def run_game():
for i in range(1, 101):
play_game(i, False)
benchmark(run_game)
  • Создайте функцию с именем test_game, которая принимает фикстуру benchmark из pytest-benchmark.
  • Создайте функцию run_game, которая выполняет итерацию от 1 до 100 включительно.
    • Для каждого числа вызывайте play_game, с параметром should_print, установленным в False.
  • Передайте функцию run_game в ранер benchmark.

Теперь нам нужно настроить проект для запуска наших бенчмарков.

Создайте новую виртуальную среду с помощью pipenv:

$ pipenv shell
Creating a Pipfile for this project...
Launching subshell in virtual environment...
source /usr/bencher/.local/share/virtualenvs/test-xnizGmtA/bin/activate

Установите pytest-benchmark в этой новой среде pipenv:

$ pipenv install pytest-benchmark
Creating a Pipfile for this project...
Installing pytest-benchmark...
Resolving pytest-benchmark...
Added pytest-benchmark to Pipfile's [packages] ...
✔ Installation Succeeded
Pipfile.lock not found, creating...
Locking [packages] dependencies...
Building requirements...
Resolving dependencies...
✔ Success!
Locking [dev-packages] dependencies...
Updated Pipfile.lock (be953321071292b6175f231c7e2e835a3cd26169a0d52b7b781b344d65e8cce3)!
Installing dependencies from Pipfile.lock (e8cce3)...

Теперь мы готовы провести бенчмарк нашего кода, запустите pytest game.py:

$ pytest game.py
======================================================= test session starts ========================================================
platform darwin -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /usr/bencher/examples/python/pytest_benchmark
plugins: benchmark-4.0.0
collected 1 item
game.py . [100%]
------------------------------------------------- benchmark: 1 tests -------------------------------------------------
Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
----------------------------------------------------------------------------------------------------------------------
test_game 10.5416 237.7499 10.8307 1.3958 10.7088 0.1248 191;10096 92.3304 57280 1
----------------------------------------------------------------------------------------------------------------------
Legend:
Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
OPS: Operations Per Second, computed as 1 / Mean
======================================================== 1 passed in 1.68s =========================================================

🐰 Капуста, зажигай ритм! У нас есть первые метрики бенчмарка!

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

Написать FizzBuzzFibonacci на Python

Наши ключевые показатели эффективности (KPI) снизились, поэтому наш продуктовый менеджер (PM) хочет, чтобы мы добавили новую функцию. После множества мозговых штурмов и интервью с пользователям было решено, что классического FizzBuzz недостаточно. Современные дети хотят новую игру, FizzBuzzFibonacci.

Правила для FizzBuzzFibonacci следующие:

Напишите программу, которая выводит целые числа от 1 до 100 (включительно):

  • Для кратных трем, вывод Fizz
  • Для кратных пяти, вывод Buzz
  • Для кратных и трем, и пяти, вывод FizzBuzz
  • Для чисел, которые являются частью последовательности Фибоначчи, вывод только Fibonacci
  • Для всех остальных, вывод самого числа

Последовательность Фибоначчи - это последовательность чисел, в которой каждое следующее число является суммой двух предыдущих. Например, начиная с 0 и 1, следующим числом в последовательности Фибоначчи будет 1. За ним следуют: 2, 3, 5, 8 и так далее. Числа, которые являются частью последовательности Фибоначчи, известны как числа Фибоначчи. Так что нам придется написать функцию, которая определяет числа Фибоначчи.

Есть много способов записать последовательность Фибоначчи и, аналогично, много способов определить число Фибоначчи. Поэтому мы пойдем моим любимым способом:

def is_fibonacci_number(n):
for i in range(n + 1):
previous, current = 0, 1
while current < i:
next_value = previous + current
previous = current
current = next_value
if current == n:
return True
return False
  • Создайте функцию is_fibonacci_number, которая принимает целое число и возвращает булево значение.
  • Итерируйте все числа от 0 до заданного числа n включительно.
  • Инициализируйте последовательность Фибоначчи, начиная с 0 и 1 в качестве previous и current числа, соответственно.
  • Продолжайте итерировать, пока current число меньше текущей итерации i.
  • Добавьте previous и current число, чтобы получить число next_value.
  • Обновите previous число на current число.
  • Обновите current число на next_value число.
  • Как только current станет больше или равен данному числу n, мы выйдем из цикла.
  • Проверьте, равен ли current числу n, и если да, верните True.
  • В противном случае верните False.

Теперь нам нужно обновить нашу функцию fizz_buzz:

def fizz_buzz_fibonacci(n):
if is_fibonacci_number(n):
return "Fibonacci"
elif n % 15 == 0:
return "FizzBuzz"
elif n % 3 == 0:
return "Fizz"
elif n % 5 == 0:
return "Buzz"
else:
return str(n)
  • Переименуйте функцию fizz_buzz в fizz_buzz_fibonacci, чтобы сделать ее более описательной.
  • Вызовите нашу вспомогательную функцию is_fibonacci_number.
  • Если результат из is_fibonacci_number равен True, то верните Fibonacci.
  • Если результат из is_fibonacci_number равен False, тогда выполните ту же логику Fizz, Buzz, FizzBuzz или возврата числа, возвращая результат.

Поскольку мы переименовали fizz_buzz в fizz_buzz_fibonacci, нам также нужно обновить нашу функцию play_game:

def play_game(n, should_print):
result = fizz_buzz_fibonacci(n)
if should_print:
print(result)
return result

И основной код выполнения, и функция test_game могут остаться точно такими же.

Бенчмаркинг FizzBuzzFibonacci

Теперь мы можем снова запустить наш бенчмарк:

$ pytest game.py
======================================================= test session starts ========================================================
platform darwin -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /usr/bencher/examples/python/pytest_benchmark
plugins: benchmark-4.0.0
collected 1 item
game.py . [100%]
--------------------------------------------------- benchmark: 1 tests --------------------------------------------------
Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
-------------------------------------------------------------------------------------------------------------------------
test_game 726.9592 848.2919 735.5682 13.4925 731.4999 4.7078 146;192 1.3595 1299 1
-------------------------------------------------------------------------------------------------------------------------
Legend:
Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
OPS: Operations Per Second, computed as 1 / Mean
======================================================== 1 passed in 1.97s =========================================================

Прокручивая назад историю терминала, мы можем визуально сравнить производительность наших игр FizzBuzz и FizzBuzzFibonacci: 10.8307 us против 735.5682 us. Ваши числа будут немного отличаться от моих. Однако разница между двумя играми, вероятно, будет около 50x. По-моему, это здорово! Особенно для добавления такой звучной функции, как Фибоначчи, в нашу игру. Дети это оценят!

Расширение FizzBuzzFibonacci на Python

Наша игра стала хитом! Дети действительно любят играть в FizzBuzzFibonacci. Настолько, что руководство решило, что им нужен сиквел. Но это современный мир, нам нужен Ежегодный Повторяющийся Доход (ARR), а не разовые покупки! Новая концепция нашей игры заключается в том, что она будет открытой, больше никаких ограничений от 1 до 100 (даже если это включительно). Нет, мы нацелены на новые горизонты!

Правила для Open World FizzBuzzFibonacci следующие:

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

  • Для кратных трем, выводит Fizz
  • Для кратных пяти, выводит Buzz
  • Для кратных и трем, и пяти, выводит FizzBuzz
  • Для чисел, которые являются частью последовательности Фибоначчи, выводит только Fibonacci
  • Для всех остальных чисел, выводит само число

Чтобы наша игра работала для любого числа, нам нужно принимать аргумент командной строки. Обновите основное выполнение, чтобы оно выглядело так:

game.py
import sys
args = sys.argv
if len(args) > 1 and args[1].isdigit():
i = int(args[1])
play_game(i, True)
else:
print("Please, enter a positive integer to play...")
  • Импортируйте пакет sys.
  • Соберите все аргументы (args), переданные нашей игре из командной строки.
  • Получите первый аргумент, переданный нашей игре, и проверьте, является ли он цифрой.
    • Если да, преобразуйте первый аргумент в целое число, i.
    • Сыграйте в нашу игру с ново преобразованным целым числом i.
  • Если преобразование не удалось или аргумент не передан, по умолчанию запросите допустимый ввод.

Теперь мы можем играть в нашу игру с любым числом! Запустите python game.py, а затем введите целое число, чтобы сыграть в нашу игру:

$ python game.py 9
Fizz
$ python game.py 10
Buzz
$ python game.py 13
Fibonacci

И если мы пропустим или предоставим недопустимое число:

$ python game.py
Please, enter a positive integer to play...
$ python game.py bad
Please, enter a positive integer to play...

Вау, это было тщательное тестирование! CI прошел. Наши боссы в восторге. Давайте выпустим это! 🚀

Конец


SpongeBob SquarePants Three Weeks Later
This is Fine meme

🐰 … конец вашей карьеры, может быть?


Шутка ли, всё в огне! 🔥

Сначала казалось, что все идет нормально. Но в 02:07 утра в субботу мой пейджер прозвучал:

📟 Ваша игра в огне! 🔥

Выпрыгнув из кровати, я пытался понять, что происходит. Я попытался пройтись по логам, но это было сложно, потому что все постоянно вылетало. Наконец, я нашёл проблему. Дети! Им настолько понравилась наша игра, что они играли в нее аж до миллиона! В свете гениального озарения, я добавил два новых бенчмарка:

def test_game_100(benchmark):
def run_game():
play_game(100, False)
benchmark(run_game)
def test_game_1_000_000(benchmark):
def run_game():
play_game(1_000_000, False)
benchmark(run_game)
  • Микротест производительности test_game_100 для игры с числом сто (100)
  • Микротест производительности test_game_1_000_000 для игры с числом один миллион (1_000_000)

Когда я его запустил, получилось следующее:

$ pytest game.py
======================================================= test session starts ========================================================
platform darwin -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /usr/bencher/examples/python/pytest_benchmark
plugins: benchmark-4.0.0
collected 3 items
game.py ... [100%]

Ждите… ждите…

-------------------------------------------------------------------------------------------------- benchmark: 3 tests --------------------------------------------------------------------------------------------------
Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_game_100 15.4166 (1.0) 112.8749 (1.0) 15.8470 (1.0) 1.1725 (1.0) 15.6672 (1.0) 0.1672 (1.0) 1276;7201 63,103.3078 (1.0) 58970 1
test_game 727.0002 (47.16) 1,074.3327 (9.52) 754.3231 (47.60) 33.2047 (28.32) 748.9999 (47.81) 33.7283 (201.76) 134;54 1,325.6918 (0.02) 1319 1
test_game_1_000_000 565,232.3328 (>1000.0) 579,829.1252 (>1000.0) 571,684.6334 (>1000.0) 6,365.1577 (>1000.0) 568,294.3747 (>1000.0) 10,454.0113 (>1000.0) 2;0 1.7492 (0.00) 5 1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Legend:
Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
OPS: Operations Per Second, computed as 1 / Mean
======================================================== 3 passed in 7.01s =========================================================

Что! 15.8470 us x 1,000 должно быть 15,847.0 us, а не 571,684.6334 us 🤯 Хотя моя функция для последовательности Фибоначчи работает корректно, где-то там, должно быть, есть ошибка производительности.

Исправляем FizzBuzzFibonacci на Python

Давайте еще раз взглянем на функцию is_fibonacci_number:

def is_fibonacci_number(n):
for i in range(n + 1):
previous, current = 0, 1
while current < i:
next_value = previous + current
previous = current
current = next_value
if current == n:
return True
return False

Теперь, когда я задумался о производительности, я понимаю, что у меня есть лишний, ненужный цикл. Мы можем полностью избавиться от цикла for i in range(n + 1): и просто сравнить значение current с заданным числом (n) 🤦

def is_fibonacci_number(n):
previous, current = 0, 1
while current < n:
next_value = previous + current
previous = current
current = next_value
return current == n
  • Обновите нашу функцию is_fibonacci_number.
  • Инициализируйте последовательность Фибоначчи, начиная с 0 и 1 как previous и current числа соответственно.
  • Итерация продолжается, пока число current меньше, чем заданное число n.
  • Сложите previous и current числа, чтобы получить число next_value.
  • Обновите число previous до значения current.
  • Обновите число current до значения next_value.
  • Как только current станет больше или равно заданному числу n, мы выйдем из цикла.
  • Проверьте, равно ли число current заданному числу n, и верните этот результат.

Давайте теперь снова запустим эти тесты и посмотрим, как у нас дела:

$ pytest game.py
======================================================= test session starts ========================================================
platform darwin -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /usr/bencher/examples/python/pytest_benchmark
plugins: benchmark-4.0.0
collected 3 items
game.py ... [100%]
------------------------------------------------------------------------------------------------ benchmark: 3 tests ------------------------------------------------------------------------------------------------
Name (time in ns) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_game_100 309.8685 (1.0) 40,197.8614 (2.38) 322.0815 (1.0) 101.7570 (1.0) 320.2877 (1.0) 5.1805 (1.0) 321;12616 3,104.8046 (1.0) 195120 16
test_game_1_000_000 724.9881 (2.34) 16,912.4920 (1.0) 753.1445 (2.34) 121.0458 (1.19) 741.7053 (2.32) 12.4797 (2.41) 656;13698 1,327.7664 (0.43) 123073 10
test_game 26,958.9946 (87.00) 129,667.1107 (7.67) 27,448.7719 (85.22) 1,555.0003 (15.28) 27,291.9424 (85.21) 165.7754 (32.00) 479;2372 36.4315 (0.01) 25918 1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Legend:
Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile.
OPS: Operations Per Second, computed as 1 / Mean
======================================================== 3 passed in 3.99s =========================================================

О, вау! Наш тест производительности test_game вернулся почти к тому, что было у оригинального FizzBuzz. Жаль, не могу точно вспомнить, каким был этот результат. Но это было три недели назад. История в терминале не идет так далеко. И pytest-benchmark сохраняет свои результаты только тогда, когда мы об этом просим. Но я думаю, что близко!

Тест производительности test_game_100 упал почти в 50 раз до 322.0815 ns. И тест производительности test_game_1_000_000 уменьшился более чем в 500,000 раз! С 571,684,633.4 ns до 753.1445 ns!

🐰 Ну, по крайней мере, мы поймали эту ошибку производительности до того, как она попала в продакшн… о, точно. Неважно…

Отслеживание регрессий производительности в CI

Руководители были недовольны потоком отрицательных отзывов, которые наша игра получила из-за моей ошибки в производительности. Они сказали мне, чтобы это больше не происходило, и когда я спросил как, они просто сказали мне больше этого не делать. Как мне это контролировать‽

К счастью, я нашел этот замечательный инструмент с открытым исходным кодом под названием Bencher. У него есть очень щедрый бесплатный уровень, поэтому я могу использовать Bencher Cloud для своих личных проектов. А на работе, где все должно быть в нашем приватном облаке, я начал использовать Самостоятельный хостинг Bencher.

У Bencher есть встроенные адаптеры, поэтому их легко интегрировать в CI. После прочтения руководства по быстрому старту, я могу запускать свои бенчмарки и отслеживать их с помощью Bencher.

$ bencher run --adapter python_pytest --file results.json "pytest --benchmark-json results.json game.py"
======================================================= test session starts ========================================================
platform darwin -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /usr/bencher/examples/python/pytest_benchmark
plugins: benchmark-4.0.0
collected 3 items
game.py ...
...
View results:
- test_game (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=077449e5-5b45-4c00-bdfb-3a277413180d&start_time=1697224006000&end_time=1699816009000&upper_boundary=true
- test_game_100 (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=96508869-4fa2-44ac-8e60-b635b83a17b7&start_time=1697224006000&end_time=1699816009000&upper_boundary=true
- test_game_1_000_000 (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=ff014217-4570-42ea-8813-6ed0284500a4&start_time=1697224006000&end_time=1699816009000&upper_boundary=true

Используя это замечательное устройство для путешествий во времени, которое мне дал милый кролик, Я смог вернуться в прошлое и повторить то, что бы произошло, если бы мы использовали Bencher с самого начала. Вы можете увидеть, где мы впервые внесли ошибочную реализацию FizzBuzzFibonacci. Я немедленно получил ошибки в CI в виде комментария к моему запросу на вытягивание. В тот же день я исправил ошибку производительности, устранив не нужный, лишний цикл. Никаких пожаров. Только довольные пользователи.

Bencher: Непрерывное тестирование производительности

🐰 Bencher

Bencher - это набор инструментов для непрерывного тестирования производительности. Когда-нибудь регрессия производительности влияла на ваших пользователей? Bencher мог бы предотвратить это. Bencher позволяет вам обнаруживать и предотвращать регрессии производительности до того, как они попадут в продакшн.

  • Запустить: Запустите свои тесты производительности локально или в CI, используя ваши любимые инструменты для этого. CLI bencher просто оборачивает ваш существующий аппарат тестирования и сохраняет его результаты.
  • Отслеживать: Отслеживайте результаты ваших тестов производительности со временем. Мониторите, запрашивайте и строите графики результатов с помощью веб-консоли Bencher на основе ветки исходного кода, испытательного стенда и меры.
  • Поймать: Отлавливайте регрессии производительности в CI. Bencher использует инструменты аналитики, работающие по последнему слову техники, чтобы обнаружить регрессии производительности, прежде чем они попадут в продакшн.

По тем же причинам, по которым модульные тесты запускаются в CI, чтобы предотвратить регрессии функций, тесты производительности должны быть запущены в CI с Bencher, чтобы предотвратить регрессии производительности. Ошибки производительности – это тоже ошибки!

Начните отлавливать регрессии производительности в CI — попробуйте Bencher Cloud бесплатно.

🤖 Этот документ был автоматически создан OpenAI GPT-4. Оно может быть неточным и содержать ошибки. Если вы обнаружите какие-либо ошибки, откройте проблему на GitHub.