Pular para o conteúdo principal

victorstein.dev

27/100 Dias de Golang - Testes de benchmark

Table of Contents

# Benchmarking em Go: Medindo e Otimizando o desempenho

Como você deve ter visto até aqui, testes em go são muito simples e a biblioteca padrão nos oferece recursos bem poderosos. Outra ferramenta que a biblioteca testing oferece de forma nativa é o benchmarking. O benchmarking serve para diversas finalidades, todas relacionadas à avaliação e otimização do desempenho de sistemas, aplicações, algoritmos ou componentes de software. A questão mais legal que eu achei foi a comparação de implementações, testar qual implementação de uma função performa melhor que outra. Veja como é simples fazer esses testes:

func BenchmarkExemplo(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // código a ser medido
    }
}

A principal diferença é que passamos de parâmetro o b *testing.B, nos testes unitários passamos t *testing.T. Dentro da função fazemos um loop passando i < b.N esse N será o número de execuções da função.

Vamos adicionar uma nova função na nossa “biblioteca” utils.go. Essa função será a SomaComLoop, passamos um int e ela retorna a soma de todos os números de n até 1. Curiosidade: Isso, na matemática, se chama “Termial” e é representado pelo símbolo “?” 3? = 3 + 2 + 2 = 6

func SomaComLoop(n int) int {
    sum := 0
    for i := 1; i <= n; i++ {
        sum += i
    }
    return sum
}

E vamos criar um benchmark para ela:

func BenchmarkSomaComLoop(b *testing.B) {
	for i := 0; i < b.N; i++ {
		SomaComLoop(10000)
	}
}

Veja a saída:

goos: linux
goarch: amd64
pkg: firsttest
cpu: Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz
BenchmarkSomaComLoop-4            169431              6882 ns/op
PASS
ok      firsttest       2.230s

Agora vamos fazer uma otimização nesse código e fazer a soma utilizando uma fórmula:

func SomaComFormula(n int) int {
	return (n * (n + 1)) / 2
}

e criar o benchmark

func BenchmarkSomaComFormula(b *testing.B){
	for i := 0; i< b.N; i++ {
		SomaComFormula(10000)
	}
}

Saída:

goos: linux
goarch: amd64
pkg: firsttest
cpu: Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz
BenchmarkSomaComFormula-4       1000000000               0.3671 ns/op
PASS
ok      firsttest       0.425s

Agora rodando o com a flag -benchmem para obtermos informações sobre a memória dos testes:

go test -bench=. -benchmem

Veja o resultado:

goos: linux
goarch: amd64
pkg: firsttest
cpu: Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz
BenchmarkSomaComLoop-4            175250              6874 ns/op               0 B/op          0 allocs/op
BenchmarkSomaComFormula-4       1000000000               0.3457 ns/op          0 B/op          0 allocs/op
PASS
ok      firsttest       2.677s

No começo temos informações sobre a plataforma que estamos rodando mas o interessante são as comparações entra as funções:

BenchmarkSomaComLoop-4            175250              6874 ns/op               0 B/op          0 allocs/op
BenchmarkSomaComFormula-4       1000000000               0.3457 ns/op          0 B/op          0 allocs/op

O primeiro valor 175250 e 1000000000 representa o b.N do loop. Esse valor é ajustado pelo compilador para encontrar uma amostra com dados significativos. Veja que a função com loop foi executada 175.250 vezes enquanto a com a fórmula foi executada 1 bilhão de vezes.

O segundo valor é o tempo médio que cada operação levou. Na com loop: 6874 nanossegundos (ou 6,874 microssegundos), com fórmula: 0.3457 nanossegundos

O terceiro e o quarto valor são referentes a quantidade de memória alocada e o número alocações. No nosso exemplo não tivemos esse valor.

Olhando os resultados me lembrei das aulas de Análise de Algoritmos, a função SomaComFormula é de complexidade O(1) ou constante, enquanto a SomaComLoop é O(n).

Muito interessante essa ferramenta nativa do Go para testarmos nossas funções!