Como fazer benchmark de código Python com pytest-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 Python

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:

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)
  • Itere de 1 a 100, usando um intervalo de 101.
  • Para cada número, calcule o módulo (resto após divisão) para ambos 3 e 5.
  • 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 Passo a Passo

Para seguir este tutorial passo a passo, você precisará instalar Python e instalar o pipenv.

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

Crie um arquivo Python chamado game.py, e defina seu conteúdo com a implementação FizzBuzz acima.

Em seguida, execute python game.py. A saída deve ser similar a:

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

🐰 Boom! Você está detonando 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 Python

As duas opções populares para benchmarking em Python são: pytest-benchmark e airspeed velocity (asv)

pytest-benchmark é uma ferramenta de benchmarking poderosa integrada ao popular framework de testes pytest. Ela permite que os desenvolvedores meçam e comparem o desempenho de seu código executando benchmarks junto com seus testes de unidade. Os usuários podem facilmente comparar seus resultados de benchmark localmente e exportá-los em vários formatos, como JSON.

airspeed velocity (asv) é outra ferramenta de benchmarking avançada no ecossistema Python. Um dos principais benefícios do asv é sua capacidade de gerar relatórios HTML detalhados e interativos, o que facilita a visualização das tendências de desempenho e a identificação de regressões. Além disso, o asv suporta Benchmarking Contínuo Relativo nativamente.

Ambos são suportados pelo Bencher. Então por que escolher pytest-benchmark? pytest-benchmark integra-se perfeitamente com pytest, que é o padrão de fato para testes unitários no ecossistema Python. Recomendo usar pytest-benchmark para medir a latência do seu código, especialmente se você já estiver utilizando pytest. Ou seja, pytest-benchmark é ótimo para medir o tempo de relógio de parede.

Refatorar FizzBuzz

Para testar nossa aplicação FizzBuzz, precisamos desacoplar nossa lógica da execução principal do programa. Os ambientes de benchmarking não conseguem avaliar a execução principal. Para fazer isso, precisamos fazer algumas mudanças.

Vamos refatorar nossa lógica do FizzBuzz em um par de funções:

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: Recebe um número inteiro n, chama fizz_buzz com esse número, e se should_print for True, imprime o resultado.
  • fizz_buzz: Recebe um número inteiro n e executa a lógica de Fizz, Buzz, FizzBuzz ou número, retornando o resultado como uma string.

Então, atualize a execução principal para ficar assim:

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

A execução principal 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.

Avaliando FizzBuzz

Para avaliar nosso código, precisamos criar uma função de teste que execute nosso benchmark. No final do game.py, adicione o seguinte código:

def test_game(benchmark):
def run_game():
for i in range(1, 101):
play_game(i, False)
benchmark(run_game)
  • Crie uma função chamada test_game que receba um fixture benchmark do pytest-benchmark.
  • Crie uma função run_game que itere de 1 a 100 inclusivamente.
    • Para cada número, chame play_game, com should_print definido como False.
  • Passe a função run_game para o executor benchmark.

Agora precisamos configurar nosso projeto para executar nossos benchmarks.

Crie um novo ambiente virtual com pipenv:

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

Instale pytest-benchmark dentro desse novo ambiente 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)...

Agora estamos prontos para avaliar nosso código, execute 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 =========================================================

🐰 Vamos agitar o som! Temos nossas primeiras métricas de benchmark!

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

Escreva FizzBuzzFibonacci em Python

Nossos Indicadores-Chave de Desempenho (KPIs) estão em queda, então nosso Gerente de Produto (PM) quer que adicionemos um novo recurso. Após muitas sessões de brainstorming e várias entrevistas com usuários, foi decidido que o bom e velho FizzBuzz não é suficiente. Hoje em dia, as crianças querem um novo jogo, o 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:

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
  • Crie uma função chamada is_fibonacci_number que recebe um inteiro e retorna um valor booleano.
  • Itere para todos os números de 0 até o nosso número 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 corrente i.
  • Some os números anterior e atual para obter o número next_value.
  • Atualize o número anterior para o número atual.
  • Atualize o número atual para o número next_value.
  • Uma vez que atual for maior ou igual ao número dado n, o loop será encerrado.
  • Verifique se o número atual é igual ao número dado n e se for, retorne True.
  • Caso contrário, retorne False.

Agora precisamos atualizar nossa função 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)
  • 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:

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

Tanto nossa execução principal quanto a função test_game podem permanecer exatamente como estão.

Benchmarking FizzBuzzFibonacci

Agora podemos executar novamente nosso benchmark:

$ 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 =========================================================

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

Expandir FizzBuzzFibonacci em Python

Nosso jogo é um sucesso! As crianças realmente adoram jogar FizzBuzzFibonacci. Tanto que a notícia chegou aos executivos de que eles 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 indefinido, não mais vivendo entre o limite de 1 e 100 (mesmo que seja inclusivo). 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 execução principal para se parecer com isto:

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...")
  • Importe o pacote sys.
  • Recolha todos os argumentos (args) passados para o nosso jogo a partir da linha de comando.
  • Obtenha o primeiro argumento passado para o nosso jogo e verifique se é um dígito.
    • Se for, analise o primeiro argumento como um inteiro, i.
    • Jogue nosso jogo com o novo inteiro i analisado.
  • Se a análise falhar ou nenhum argumento for passado, padrão para solicitar uma entrada válida.

Agora podemos jogar nosso jogo com qualquer número! Execute python game.py seguido por um número inteiro para jogar nosso jogo:

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

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

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

Uau, isso foi um teste completo! CI aprova. Nossos chefes estão encantados. 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:

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)
  • Um microbenchmark test_game_100 para jogar o jogo com o número cem (100)
  • Um microbenchmark test_game_1_000_000 para jogar o jogo com o número um milhão (1_000_000)

Quando executei, obtive o seguinte resultado:

$ 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%]

Espere por isso… espere por isso…

-------------------------------------------------------------------------------------------------- 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 =========================================================

O quê! 15.8470 us x 1.000 deveria ser 15.847.0 us e não 571.684.6334 us 🤯 Mesmo que eu tenha acertado minha função de sequência de Fibonacci, devo ter algum bug de desempenho em algum lugar.

Corrigir FizzBuzzFibonacci em Python

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

Agora que estou pensando em desempenho, percebo que tenho um loop desnecessário e extra. Podemos eliminar completamente o loop for i in range(n + 1): e apenas comparar o valor current com o número fornecido (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
  • Atualize a nossa função is_fibonacci_number.
  • Inicialize a 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 dado n.
  • Adicione os números previous e current para obter o número next_value.
  • Atualize o número previous para o número current.
  • Atualize o número current para o número next_value.
  • Quando current for maior ou igual ao número dado n, sairemos do loop.
  • Verifique se o número current é igual ao número dado n e retorne esse resultado.

Agora vamos executar esses benchmarks novamente e ver como nos saímos:

$ 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 =========================================================

Uau! Nosso benchmark test_game está de volta a algo próximo do original FizzBuzz. Gostaria de lembrar exatamente qual era essa pontuação. Já faz três semanas. Meu histórico do terminal não vai tão longe. E o pytest-benchmark só armazena seus resultados quando pedimos. Mas acho que está perto!

O benchmark test_game_100 caiu quase 50x para 322.0815 ns. E o benchmark test_game_1_000_000 caiu mais de 500.000x! De 571.684.633,4 ns para 753.1445 ns!

🐰 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.

$ 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

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.