Como fazer benchmark de código C++ com Google Benchmark

Everett Pompeii

Everett Pompeii


O que é Benchmarking?

Benchmarking é a prática de testar o desempenho do seu código para ver quão rápido (latência) ou quanto (throughput) trabalho ele pode executar. Este passo frequentemente negligenciado no desenvolvimento de software é crucial para criar e manter um código rápido e performático. O benchmarking fornece as métricas necessárias para que os desenvolvedores compreendam o desempenho do seu código sob várias cargas de trabalho e condições. Pelas mesmas razões que você escreve testes unitários e de integração para evitar regressões de funcionalidades, você deve escrever benchmarks para evitar regressões de desempenho. Bugs de desempenho são bugs!

Escreva FizzBuzz em C++

Para escrevermos testes de desempenho, precisamos de algum código-fonte para testar. Para começar, vamos escrever um programa muito simples, FizzBuzz.

As regras para o FizzBuzz são as seguintes:

Escreva um programa que imprima os inteiros de 1 a 100 (inclusive):

  • Para múltiplos de três, imprima Fizz
  • Para múltiplos de cinco, imprima Buzz
  • Para múltiplos de três e cinco, imprima FizzBuzz
  • Para todos os outros, imprima o número

Existem muitas maneiras de escrever o FizzBuzz. Então vamos seguir com o meu favorito:

#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;
}
  • Itere de 1 a 100, incrementando a cada iteração.
  • Para cada número, calcule o módulo (resto da divisão).
  • Se o resto for 0, então o número é um múltiplo do fator dado:
    • Se o resto for 0 para 15, então imprima FizzBuzz.
    • Se o resto for 0 para 3, então imprima Fizz.
    • Se o resto for 0 para 5, então imprima Buzz.
  • Caso contrário, apenas imprima o número.

Siga o Passo a Passo

Para seguir este tutorial passo a passo, você precisará instalar o git, instalar o cmake e instalar o GNU Compiler Collection (GCC) g++.

🐰 O código-fonte deste post está disponível no GitHub.

Crie um arquivo C++ chamado game.cpp, e defina seu conteúdo para a implementação FizzBuzz acima.

Use g++ para criar um executável chamado game e depois executá-lo. A saída deve se parecer com:

$ 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

🐰 Boom! Você está arrasando na entrevista de programação!

Antes de prosseguir, é importante discutir as diferenças entre micro-benchmarking e macro-benchmarking.

Micro-Benchmarking vs Macro-Benchmarking

Existem duas categorias importantes de benchmarks de software: micro-benchmarks e macro-benchmarks. Os micro-benchmarks operam em um nível semelhante aos testes unitários. Por exemplo, um benchmark para uma função que determina Fizz, Buzz, ou FizzBuzz para um número individual seria um micro-benchmark. Os macro-benchmarks operam em um nível semelhante aos testes de integração. Por exemplo, um benchmark para uma função que executa o jogo inteiro de FizzBuzz, de 1 a 100, seria um macro-benchmark.

Em geral, é melhor testar no menor nível de abstração possível. No caso dos benchmarks, isso os torna mais fáceis de manter, e ajuda a reduzir a quantidade de ruído nas medições. No entanto, assim como ter alguns testes de ponta a ponta pode ser muito útil para verificar se todo o sistema se junta conforme esperado, ter macro-benchmarks pode ser muito útil para garantir que os caminhos críticos através do seu software permaneçam com bom desempenho.

Benchmarking em C++

As duas opções populares para benchmarking em C++ são: Google Benchmark e Catch2.

O Google Benchmark é uma biblioteca de benchmarking robusta e versátil para C++ que permite aos desenvolvedores medir o desempenho de seu código com alta precisão. Um de seus principais benefícios é a facilidade de integração em projetos existentes, especialmente aqueles que já usam o GoogleTest. O Google Benchmark fornece métricas de desempenho detalhadas, incluindo a capacidade de medir o tempo de CPU, tempo de parede e uso de memória. Ele suporta uma ampla gama de cenários de benchmarking, desde benchmarks de funções simples até testes complexos e parametrizados.

Catch2 é um framework de testes moderno, apenas com cabeçalho, para C++ que simplifica o processo de escrever e executar testes. Um de seus principais benefícios é sua facilidade de uso, com uma sintaxe que é tanto intuitiva quanto expressiva, permitindo aos desenvolvedores escrever testes de forma rápida e clara. Catch2 suporta uma ampla gama de tipos de teste, incluindo testes unitários, testes de integração, testes no estilo de desenvolvimento orientado a comportamentos (BDD) e recursos básicos de micro-benchmarking.

Ambos são suportados pela Bencher. Então, por que escolher o Google Benchmark? O Google Benchmark integra-se perfeitamente com o GoogleTest, que é o padrão de fato para testes unitários no ecossistema C++. Eu sugiro usar o Google Benchmark para mensurar a latência do seu código, especialmente se você já estiver usando o GoogleTest. Ou seja, o Google Benchmark é excelente para medir o tempo de execução total.

Refatorar FizzBuzz

Para testar nossa aplicação FizzBuzz, precisamos desacoplar nossa lógica da função main do nosso programa. Ferramentas de benchmark não conseguem avaliar a função main. Para fazer isso, precisamos fazer algumas alterações.

Vamos refatorar a lógica do FizzBuzz em algumas funções dentro de um novo arquivo chamado 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: Recebe um inteiro n e executa a lógica real de Fizz, Buzz, FizzBuzz ou retorna o número como string.
  • play_game: Recebe um inteiro n, chama fizz_buzz com esse número, e se should_print for true, imprime o resultado.

Agora, vamos criar um arquivo de cabeçalho chamado play_game.h e adicionar a declaração da função play_game a ele:

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

Em seguida, atualize a função main em game.cpp para usar a definição da função play_game do arquivo de cabeçalho:

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

A função main do nosso programa itera através dos números de 1 a 100 inclusive e chama play_game para cada número, com should_print definido como true.

Medindo Desempenho do FizzBuzz

Para medir o desempenho do nosso código, precisamos primeiro instalar o Google Benchmark.

Clone a biblioteca:

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

Entre no diretório recém-clonado:

$ cd benchmark

Use o cmake para criar um diretório de build onde será colocado o resultado do build:

$ cmake -E make_directory "build"

Use o cmake para gerar os arquivos de sistema de build e baixar quaisquer dependências:

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

Finalmente, construa a biblioteca:

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

Volte para o diretório pai:

cd ..

Agora vamos criar um novo arquivo chamado 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();
  • Importe as definições de funções de play_game.h.
  • Importe o cabeçalho da biblioteca benchmark do Google.
  • Crie uma função chamada BENCHMARK_game que recebe uma referência para benchmark::State.
  • Itere sobre o objeto benchmark::State.
  • Para cada iteração, itere de 1 a 100, inclusivamente.
    • Chame play_game com o número atual e should_print definido como false.
  • Passe a função BENCHMARK_game para o executor BENCHMARK.
  • Execute o benchmark com BENCHMARK_MAIN.

Agora estamos prontos para medir o desempenho do nosso código:

$ 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

🐰 Vamos lá! Temos nossas primeiras métricas de benchmark!

Finalmente, podemos descansar nossas cabeças de desenvolvedor cansadas… Brincadeira, nossos usuários querem um novo recurso!

Escreva FizzBuzzFibonacci em C++

Nossos Indicadores Chave de Performance (KPIs) caíram, então nosso Gerente de Produto (PM) quer que adicionemos um novo recurso. Após muito brainstorming e várias entrevistas com usuários, ficou decidido que o bom e velho FizzBuzz não é suficiente. As crianças de hoje querem um novo jogo, FizzBuzzFibonacci.

As regras para FizzBuzzFibonacci são as seguintes:

Escreva um programa que imprime os números inteiros de 1 a 100 (inclusive):

  • Para múltiplos de três, imprima Fizz
  • Para múltiplos de cinco, imprima Buzz
  • Para múltiplos de ambos três e cinco, imprima FizzBuzz
  • Para números que fazem parte da sequência de Fibonacci, apenas imprima Fibonacci
  • Para todos os outros, imprima o número

A Sequência de Fibonacci é uma série na qual cada número é a soma dos dois números precedentes. Por exemplo, começando com 0 e 1 o próximo número na sequência de Fibonacci seria 1. Seguido por: 2, 3, 5, 8 e assim por diante. Números que fazem parte da Sequência de Fibonacci são conhecidos como números de Fibonacci. Então, teremos que escrever uma função que detecte números de Fibonacci.

Existem muitas maneiras de escrever a sequência de Fibonacci e, da mesma forma, muitas maneiras de detectar um número de Fibonacci. Então, vamos com a minha favorita:

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;
}
  • Crie uma função chamada is_fibonacci_number que recebe um inteiro e retorna um booleano.
  • Itere para todos os números de 0 até nosso número dado n inclusive.
  • Inicialize nossa sequência de Fibonacci começando com 0 e 1 como os números anterior e atual, respectivamente.
  • Itere enquanto o número atual for menor que a iteração atual i.
  • Adicione o número anterior e atual para obter o número seguinte.
  • Atualize o número anterior para o número atual.
  • Atualize o número atual para o número seguinte.
  • Assim que atual for maior ou igual ao número dado n, sairemos do loop.
  • Verifique se o número atual é igual ao número dado n e, se for, retorne true.
  • Caso contrário, retorne false.

Agora precisaremos atualizar nossa função 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);
}
}
  • Renomeie a função fizz_buzz para fizz_buzz_fibonacci para torná-la mais descritiva.
  • Chame nossa função auxiliar is_fibonacci_number.
  • Se o resultado de is_fibonacci_number for true, então retorne Fibonacci.
  • Se o resultado de is_fibonacci_number for false, então execute a mesma lógica de Fizz, Buzz, FizzBuzz ou número, retornando o resultado.

Como renomeamos fizz_buzz para fizz_buzz_fibonacci, também precisamos atualizar nossa função 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;
}
}

Tanto nossa função main quanto a função BENCHMARK_game podem permanecer exatamente iguais.

Benchmarking FizzBuzzFibonacci

Agora podemos executar novamente nosso benchmark:

$ 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

Voltando no histórico do terminal, podemos fazer uma comparação visual entre o desempenho dos nossos jogos FizzBuzz e FizzBuzzFibonacci: 1698 ns vs 56190 ns. Seus números podem ser um pouco diferentes dos meus. No entanto, a diferença entre os dois jogos provavelmente está na faixa de 50x. Isso me parece bom! Especialmente para adicionar uma funcionalidade com um nome tão pomposo quanto Fibonacci ao nosso jogo. As crianças vão adorar!

Expandir FizzBuzzFibonacci em C++

Nosso jogo é um sucesso! As crianças realmente adoram jogar FizzBuzzFibonacci. Tanto que nossos executivos decidiram que querem uma sequência. Mas este é o mundo moderno, precisamos de Receita Recorrente Anual (ARR) e não de compras únicas! A nova visão para o nosso jogo é que ele seja infinito, sem mais viver entre os limites de 1 e 100 (mesmo que sejam inclusivos). Não, estamos indo para novas fronteiras!

As regras para o Open World FizzBuzzFibonacci são as seguintes:

Escreva um programa que aceite qualquer número inteiro positivo e imprima:

  • Para múltiplos de três, imprima Fizz
  • Para múltiplos de cinco, imprima Buzz
  • Para múltiplos de ambos três e cinco, imprima FizzBuzz
  • Para números que fazem parte da sequência de Fibonacci, apenas imprima Fibonacci
  • Para todos os outros, imprima o número

Para que nosso jogo funcione para qualquer número, precisaremos aceitar um argumento de linha de comando. Atualize a função main para ficar assim:

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;
}
  • Atualize a função main para aceitar argc e argv.
  • Pegue o primeiro argumento passado para nosso jogo e verifique se é um dígito.
    • Se for, analise o primeiro argumento como um inteiro, i.
    • Jogue nosso jogo com o inteiro recém-analisado i.
  • Se a análise falhar ou nenhum argumento for passado, padrão é solicitar uma entrada válida.

Agora podemos jogar nosso jogo com qualquer número! Recomplie nosso executável game e depois execute o executável seguido de um inteiro para jogar nosso jogo:

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

E se omitirmos ou fornecermos um número inválido:

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

Uau, esse foi um teste completo! CI aprovado. Nossos chefes estão emocionados. Vamos lançar! 🚀

O Fim


SpongeBob SquarePants Três Semanas Depois
Meme Está Tudo Bem

🐰 … o fim da sua carreira talvez?


Brincadeira! Tudo está pegando fogo! 🔥

Bem, a princípio, tudo parecia estar indo bem. Então, às 02:07 da madrugada de sábado, meu pager disparou:

📟 Seu jogo está pegando fogo! 🔥

Após sair da cama às pressas, tentei descobrir o que estava acontecendo. Eu tentei pesquisar nos logs, mas era difícil porque tudo continuava travando. Finalmente, encontrei o problema. As crianças! Elas adoravam tanto nosso jogo que jogavam até chegar a um milhão! Num lampejo de brilhantismo, adicionei dois novos benchmarks:

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);
  • Um micro-benchmark BENCHMARK_game_100 para jogar com o número cem (100)
  • Um micro-benchmark BENCHMARK_game_1_000_000 para jogar com o número um milhão (1_000_000)

Quando executei, obtive isso:

$ 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

Espere por isso… espere por isso…

BENCHMARK_game_1_000_000 110879642 ns 43628118 ns 17

O quê! 1,249 ns x 10,000 deveria ser 12,490,000 ns e não 110,879,642 ns 🤯 Embora meu código da sequência de Fibonacci esteja funcionalmente correto, devo ter um bug de desempenho em algum lugar.

Corrigir FizzBuzzFibonacci em C++

Vamos dar outra olhada na função 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;
}

Agora que estou pensando sobre desempenho, percebo que tenho um loop desnecessário e extra. Podemos eliminar completamente o loop for (int i = 0; i <= n; ++i) e apenas comparar o valor current com o número fornecido (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;
}
  • Atualize nossa função is_fibonacci_number.
  • Inicialize nossa sequência de Fibonacci começando com 0 e 1 como os números previous e current respectivamente.
  • Itere enquanto o número current for menor que o número fornecido n.
  • Some o número previous e o número current para obter o próximo número.
  • Atualize o número previous para o número current.
  • Atualize o número current para o próximo número.
  • Assim que current for maior ou igual ao número fornecido n, sairemos do loop.
  • Verifique se o número current é igual ao número fornecido n e retorne esse resultado.

Agora, vamos rodar esses benchmarks novamente e ver como fomos:

$ 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

Oh, uau! Nosso benchmark BENCHMARK_game voltou para próximo do original FizzBuzz. Gostaria de lembrar exatamente qual era aquela pontuação. Já se passaram três semanas entretanto. Meu histórico do terminal não vai tão atrás, e o Google Benchmark não armazena seus resultados. Mas acho que está próximo!

O benchmark BENCHMARK_game_100 caiu quase 50x para 34.4 ns. E o benchmark BENCHMARK_game_1_000_000 caiu mais de 1.500.000x! De 110,879,642 ns para 61.6 ns!

🐰 Ei, pelo menos pegamos esse bug de desempenho antes que chegasse à produção… ah, certo. Deixa pra lá…

Detecte Regressões de Desempenho em CI

Os executivos não ficaram felizes com a enxurrada de críticas negativas que nosso jogo recebeu devido ao meu pequeno bug de desempenho. Eles me disseram para não deixar isso acontecer de novo, e quando perguntei como, eles simplesmente me disseram para não fazê-lo novamente. Como eu deveria gerenciar isso‽

Felizmente, encontrei esta incrível ferramenta open source chamada Bencher. Existe um nível gratuito super generoso, então posso apenas usar Bencher Cloud para meus projetos pessoais. E no trabalho, onde tudo precisa estar em nossa nuvem privada, comecei a usar Bencher Auto-Hospedado.

Bencher tem adaptadores integrados, por isso é fácil de integrar ao CI. Após seguir o guia Rápido Início, consegui executar meus benchmarks e rastreá-los com o 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

Usando este incrível dispositivo de viagem no tempo que um simpático coelho me deu, consegui voltar ao passado e reviver o que teria acontecido se estivéssemos usando o Bencher desde o início. Você pode ver onde fizemos pela primeira vez o push da implementação bugada de FizzBuzzFibonacci. Imediatamente recebi falhas no CI como um comentário na minha solicitação de pull. No mesmo dia, corrigi o bug de desempenho, eliminando aquele loop extra e desnecessário. Sem incêndios. Apenas usuários felizes.

Bencher: Benchmarking Contínuo

🐰 Bencher

Bencher é um conjunto de ferramentas de benchmarking contínuas. Já teve algum impacto de regressão de desempenho nos seus usuários? Bencher poderia ter prevenido isso. Bencher permite que você detecte e previna regressões de desempenho antes que cheguem à produção.

  • Execute: Execute seus benchmarks localmente ou no CI usando suas ferramentas de benchmarking favoritas. O CLI bencher simplesmente envolve seu harness de benchmark existente e armazena seus resultados.
  • Rastreie: Acompanhe os resultados de seus benchmarks ao longo do tempo. Monitore, consulte e faça gráficos dos resultados usando o console web do Bencher baseado na branch de origem, testbed e medida.
  • Capture: Capture regressões de desempenho no CI. Bencher usa análises personalizáveis e de última geração para detectar regressões de desempenho antes que elas cheguem à produção.

Pelos mesmos motivos que os testes de unidade são executados no CI para prevenir regressões de funcionalidades, benchmarks deveriam ser executados no CI com o Bencher para prevenir regressões de desempenho. Bugs de desempenho são bugs!

Comece a capturar regressões de desempenho no CI — experimente o Bencher Cloud gratuitamente.

🤖 Este documento foi gerado automaticamente pelo OpenAI GPT-4. Pode não ser preciso e pode conter erros. Se você encontrar algum erro, abra um problema no GitHub.