Как измерить производительность кода на C++ с помощью Google Benchmark

Everett Pompeii

Everett Pompeii


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

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

Напишите FizzBuzz на C++

In order to write benchmarks, we need some source code to benchmark. To start off we are going to write a very simple program, FizzBuzz.

The rules for FizzBuzz are as follows:

Write a program that prints the integers from 1 to 100 (inclusive):

  • For multiples of three, print Fizz
  • For multiples of five, print Buzz
  • For multiples of both three and five, print FizzBuzz
  • For all others, print the number

There are many ways to write FizzBuzz. So we’ll go with the my favorite:

#include <iostream>
int main()
{
for (int i = 1; i <= 100; i++)
{
if ((i % 15) == 0)
std::cout << "FizzBuzz\n";
else if ((i % 3) == 0)
std::cout << "Fizz\n";
else if ((i % 5) == 0)
std::cout << "Buzz\n";
else
std::cout << i << "\n";
}
return 0;
}
  • Выполняйте итерации от 1 до 100, увеличивая значение на каждом шаге.
  • Для каждого числа вычисляйте модуль (остаток от деления).
  • Если остаток равен 0, значит число кратно заданному множителю:
    • Если остаток равен 0 для 15, выведите FizzBuzz.
    • Если остаток равен 0 для 3, выведите Fizz.
    • Если остаток равен 0 для 5, выведите Buzz.
  • В противном случае просто выведите число.

Следуйте шаг за шагом

Чтобы следовать этому пошаговому руководству, вам нужно установить git, установить cmake и установить GNU Compiler Collection (GCC) g++.

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

Создайте файл на C++ с именем game.cpp, и установите его содержимое на вышеупомянутую реализацию FizzBuzz.

Используйте g++ для создания исполняемого файла с именем game, а затем запустите его. Вывод должен выглядеть так:

$ g++ -std=c++11 game.cpp -o game && ./game
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 тесты могут быть очень полезными для проверки правильной работы всей системы, макробенчмарки могут быть очень полезными для проверки производительности критически важных мест в вашем программном обеспечении.

Бенчмаркинг в C++

Два популярных варианта для бенчмаркинга в C++: Google Benchmark и Catch2.

Google Benchmark — это надежная и универсальная библиотека для бенчмаркинга на C++, позволяющая разработчикам измерять производительность их кода с высокой точностью. Одним из ключевых преимуществ является простота интеграции в существующие проекты, особенно те, которые уже используют GoogleTest. Google Benchmark предоставляет подробные метрики производительности, включая возможность измерять время работы процессора, реальное время и использование памяти. Он поддерживает широкий диапазон сценариев бенчмаркинга — от простых тестов функций до сложных параметризированных тестов.

Catch2 — это современный заголовочный фреймворк для тестирования на C++, который упрощает процесс написания и выполнения тестов. Одним из основных преимуществ является его простота использования: синтаксис как интуитивно понятный, так и выразительный, позволяя разработчикам быстро и ясно писать тесты. Catch2 поддерживает широкий спектр типов тестов, включая модульные тесты, интеграционные тесты, тесты в стиле разработки через поведение (BDD), а также базовые функции микро-бенчмаркинга.

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

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

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

Давайте рефакторим логику FizzBuzz в пару функций в новом файле с именем play_game.cpp:

play_game.cpp
#include <iostream>
#include <string>
std::string fizz_buzz(int n) {
if (n % 15 == 0) {
return "FizzBuzz";
} else if (n % 3 == 0) {
return "Fizz";
} else if (n % 5 == 0) {
return "Buzz";
} else {
return std::to_string(n);
}
}
void play_game(int n, bool should_print) {
std::string result = fizz_buzz(n);
if (should_print) {
std::cout << result << std::endl;
}
}
  • fizz_buzz: Принимает целое число n и выполняет фактическую логику Fizz, Buzz, FizzBuzz или числа, возвращая результат в виде строки.
  • play_game: Принимает целое число n, вызывает fizz_buzz с этим числом и, если should_print равно true, печатает результат.

Теперь давайте создадим заголовочный файл play_game.h и добавим в него объявление функции play_game:

play_game.h
#ifndef GAME_H
#define GAME_H
#include <string>
void play_game(int n, bool should_print);
#endif // GAME_H

Затем обновим функцию main в game.cpp, чтобы использовать определение функции play_game из заголовочного файла:

game.cpp
#include "play_game.h"
int main()
{
for (int i = 1; i <= 100; i++)
{
play_game(i, true);
}
}

Функция main для нашей программы выполняет итерации по числам от 1 до 100 включительно и вызывает play_game для каждого числа с should_print, установленным в true.

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

Чтобы провести бенчмаркинг нашего кода, нам сначала нужно установить Google Benchmark.

Клонируйте библиотеку:

$ git clone https://github.com/google/benchmark.git

Перейдите в только что клонированный каталог:

$ cd benchmark

Используйте cmake, чтобы создать каталог сборки для размещения выходных данных сборки:

$ cmake -E make_directory "build"

Используйте cmake для генерации файлов системы сборки и загрузки всех зависимостей:

$ cmake -E chdir "build" cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release ../

Наконец, соберите библиотеку:

$ cmake --build "build" --config Release

Вернитесь в родительский каталог:

cd ..

Теперь создадим новый файл с именем benchmark_game.cpp:

benchmark_game.cpp
#include "play_game.h"
#include <benchmark/benchmark.h>
#include <iostream>
static void BENCHMARK_game(benchmark::State &state)
{
for (auto _ : state)
{
for (int i = 1; i <= 100; i++)
{
play_game(i, false);
}
}
}
BENCHMARK(BENCHMARK_game);
BENCHMARK_MAIN();
  • Импортируйте определения функций из play_game.h.
  • Импортируйте заголовок библиотеки Google benchmark.
  • Создайте функцию с именем BENCHMARK_game, которая принимает ссылку на benchmark::State.
  • Итерация по объекту benchmark::State.
  • Для каждой итерации выполните итерацию от 1 до 100 включительно.
    • Вызовите play_game с текущим числом и should_print, установленным в false.
  • Передайте функцию BENCHMARK_game в runner BENCHMARK.
  • Запустите бенчмарк с BENCHMARK_MAIN.

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

$ g++ -std=c++11 -isystem benchmark/include -Lbenchmark/build/src -lbenchmark -lpthread play_game.cpp benchmark_game.cpp -o benchmark_game && ./benchmark_game
2023-10-16T14:00:00-04:00
Running ./benchmark_game
Run on (8 X 24 MHz CPU s)
CPU Caches:
L1 Data 64 KiB
L1 Instruction 128 KiB
L2 Unified 4096 KiB (x8)
Load Average: 5.55, 4.62, 4.69
---------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------
BENCHMARK_game 1698 ns 1688 ns 419979

🐰 Пора свекле вспахать! У нас есть наши первые метрики бенчмаркинга!

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

Написание FizzBuzzFibonacci на C++

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

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

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

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

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

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

play_game.cpp
bool is_fibonacci_number(int n)
{
for (int i = 0; i <= n; ++i)
{
int previous = 0, current = 1;
while (current < i)
{
int next = previous + current;
previous = current;
current = next;
}
if (current == n)
{
return true;
}
}
return false;
}
  • Создайте функцию с именем is_fibonacci_number, которая принимает целое число и возвращает значение типа boolean.
  • Итерируйтесь по всем числам от 0 до нашего заданного числа n включительно.
  • Инициализируйте последовательность Фибоначчи, начиная с 0 и 1 в качестве previous и current чисел соответственно.
  • Продолжайте итерацию, пока current число меньше текущей итерации i.
  • Добавьте previous и current числа, чтобы получить следующее число next.
  • Обновите previous число на current число.
  • Обновите current число на следующее число next.
  • Как только current станет больше или равно заданному числу n, мы выйдем из цикла.
  • Проверьте, равно ли current число заданному числу n, и если да, верните true.
  • В противном случае верните false.

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

play_game.cpp
std::string fizz_buzz_fibonacci(int n)
{
if (is_fibonacci_number(n))
{
return "Fibonacci";
}
else if (n % 15 == 0)
{
return "FizzBuzz";
}
else if (n % 3 == 0)
{
return "Fizz";
}
else if (n % 5 == 0)
{
return "Buzz";
}
else
{
return std::to_string(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:

play_game.cpp
void play_game(int n, bool should_print) {
std::string result = fizz_buzz_fibonacci(n);
if (should_print) {
std::cout << result << std::endl;
}
}

Как основная функция, так и функция BENCHMARK_game могут оставаться точно такими же.

Тест производительности FizzBuzzFibonacci

Теперь мы можем снова запустить наш тест производительности:

$ g++ -std=c++11 -isystem benchmark/include -Lbenchmark/build/src -lbenchmark -lpthread play_game.cpp benchmark_game.cpp -o benchmark_game && ./benchmark_game
2023-10-16T15:00:00-04:00
Running ./benchmark_game
Run on (8 X 24 MHz CPU s)
CPU Caches:
L1 Data 64 KiB
L1 Instruction 128 KiB
L2 Unified 4096 KiB (x8)
Load Average: 4.34, 5.75, 4.71
---------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------
BENCHMARK_game 56190 ns 56054 ns 12280

Прокручивая историю терминала назад, мы можем примерно сравнить производительность наших игр FizzBuzz и FizzBuzzFibonacci: 1698 ns vs 56190 ns. Ваши цифры будут немного отличаться от моих. Тем не менее, разница между двумя играми, вероятно, будет в диапазоне 50 раз. Это кажется мне хорошим результатом! Особенно для добавления функции с таким впечатляющим названием, как Фибоначчи, в нашу игру. Детям это понравится!

Расширение FizzBuzzFibonacci на C++

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

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

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

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

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

game.cpp
#include "play_game.h"
#include <iostream>
#include <cstdlib>
int main(int argc, char *argv[])
{
if (argc > 1 && std::isdigit(argv[1][0]))
{
int i = std::atoi(argv[1]);
play_game(i, true);
}
else
{
std::cout << "Please, enter a positive integer to play..." << std::endl;
}
return 0;
}
  • Обновите функцию main для принятия argc и argv.
  • Получите первый аргумент, переданный нашей игре, и проверьте, является ли он цифрой.
    • Если да, преобразуйте первый аргумент в целое число, i.
    • Сыграйте в нашу игру с вновь преобразованным целым числом i.
  • Если преобразование не удалось или аргумент не передан, по умолчанию запрашивайте правильный ввод.

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

$ g++ -std=c++11 game.cpp play_game.cpp -o game
$ ./game 9
Fizz
$ ./game 10
Buzz
$ ./game 13
Fibonacci

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

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

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

Конец


SpongeBob SquarePants Three Weeks Later
This is Fine meme

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


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

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

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

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

benchmark_game.cpp
static void BENCHMARK_game_100(benchmark::State &state)
{
for (auto _ : state)
{
play_game(100, false);
}
}
static void BENCHMARK_game_1_000_000(benchmark::State &state)
{
for (auto _ : state)
{
play_game(1000000, false);
}
}
BENCHMARK(BENCHMARK_game_100);
BENCHMARK(BENCHMARK_game_1_000_000);
  • Микротест BENCHMARK_game_100 для игры с числом сто (100)
  • Микротест BENCHMARK_game_1_000_000 для игры с числом миллион (1_000_000)

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

$ g++ -std=c++11 -isystem benchmark/include -Lbenchmark/build/src -lbenchmark -lpthread play_game.cpp benchmark_game.cpp -o benchmark_game && ./benchmark_game
2023-11-04T03:00:00-04:00
Running ./benchmark_game
Run on (8 X 24 MHz CPU s)
CPU Caches:
L1 Data 64 KiB
L1 Instruction 128 KiB
L2 Unified 4096 KiB (x8)
Load Average: 4.98, 5.75, 4.96
-------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------
BENCHMARK_game 75547 ns 59280 ns 12560
BENCHMARK_game_100 1249 ns 1243 ns 564689

Ждём… ждём…

BENCHMARK_game_1_000_000 110879642 ns 43628118 ns 17

Что! 1,249 нс x 10,000 должно быть 12,490,000 нс, а не 110,879,642 нс 🤯 Хотя моя функция для последовательности Фибоначчи работает корректно, у меня должна быть ошибка в производительности.

Исправление FizzBuzzFibonacci на C++

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

play_game.cpp
bool is_fibonacci_number(int n)
{
for (int i = 0; i <= n; ++i)
{
int previous = 0, current = 1;
while (current < i)
{
int next = previous + current;
previous = current;
current = next;
}
if (current == n)
{
return true;
}
}
return false;
}

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

play_game.cpp
bool is_fibonacci_number(int n)
{
int previous = 0, current = 1;
while (current < n)
{
int next = previous + current;
previous = current;
current = next;
}
return current == n;
}
  • Обновите нашу функцию is_fibonacci_number.
  • Инициализируйте последовательность Фибоначчи, начиная с 0 и 1 в качестве предыдущего и текущего чисел соответственно.
  • Итерация продолжается, пока current меньше заданного числа n.
  • Сложите previous и current, чтобы получить следующее число.
  • Обновите предыдущее число на текущее число.
  • Обновите текущее число на следующее число.
  • Как только current станет больше или равен заданному числу n, мы выйдем из цикла.
  • Проверьте, равно ли текущее число заданному числу n, и верните этот результат.

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

$ g++ -std=c++11 -isystem benchmark/include -Lbenchmark/build/src -lbenchmark -lpthread play_game.cpp benchmark_game.cpp -o benchmark_game && ./benchmark_game
2023-11-04T05:00:00-04:00
Running ./benchmark_game
Run on (8 X 24 MHz CPU s)
CPU Caches:
L1 Data 64 KiB
L1 Instruction 128 KiB
L2 Unified 4096 KiB (x8)
Load Average: 4.69, 5.02, 4.78
-------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------
BENCHMARK_game 2914 ns 2913 ns 242382
BENCHMARK_game_100 34.4 ns 34.3 ns 20322076
BENCHMARK_game_1_000_000 61.6 ns 61.6 ns 11346874

О, вау! Наш тест BENCHMARK_game снова вернулся к тому, где он был для оригинального FizzBuzz. Жаль, что я не помню точно, какой там был результат. Прошло три недели. Мой терминал не хранит такие старые результаты, и Google Benchmark их не сохраняет. Но я думаю, результат близок!

Тест BENCHMARK_game_100 уменьшился почти в 50 раз до 34.4 нс. А тест BENCHMARK_game_1_000_000 уменьшился более чем в 1,500,000 раз! С 110,879,642 нс до 61.6 нс!

🐰 Как минимум, мы нашли эту ошибку производительности до того, как она попала в продакшн… хотя, неважно…

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

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

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

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

$ g++ -std=c++11 -isystem benchmark/include -Lbenchmark/build/src -lbenchmark -lpthread play_game.cpp benchmark_game.cpp -o benchmark_game
$ bencher run --adapter cpp_google "./benchmark_game --benchmark_format=json"
{
"context": {
"date": "2023-10-16T16:00:00-04:00",
"host_name": "bencher",
"executable": "./benchmark_game",
"num_cpus": 8,
"mhz_per_cpu": 24,
"cpu_scaling_enabled": false,
...
View results:
- BENCHMARK_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
- BENCHMARK_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
- BENCHMARK_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.