¿Qué es el benchmark?
El benchmark es el proceso mediante el cual podemos realizar una medición del performance de un programa o los componentes que lo conforman.
El paquete testing
incluye un poderoso framework de benchmark. El número de veces que se invoca una función es controlado automáticamente por el framework y al final se reporta en la salida.
Supongamos que deseamos almacenar preparar un string que se va construyendo a través de un loop. En un ejemplo anterior vimos que los buffers son mas eficientes que utilizar el operador +=
de concatenación, o que almacenar un arreglo de strings y luego explotarlo. Pero como podemos verificar tal performance. Para ello preparamos 3 funciones que realizan la misma función, de cada una de las formas posibles.
Primero el paquete longstring dentro del archivo longstring.go.
package longstring
import (
"bytes"
"strings"
)
func MedianteConcatenacion(longitud int) string {
var s string
for i := 0; i < longitud; i++ {
s += "texto"
}
return s
}
func MedianteArreglo(longitud int) string {
s := []string{}
for i := 0; i < longitud; i++ {
s = append(s, "texto")
}
return strings.Join(s, "")
}
func MedianteBuffer(longitud int) string {
var buffer bytes.Buffer
for i := 0; i < longitud; i++ {
buffer.WriteString("texto")
}
return buffer.String()
}
En este podemos ver que tenemos 3 funciones que hacen la misma operación de ir construyendo este string de salida. El número de ciclos realizados por el loop dependen del parámetro longitud.
- MedianteConcatenacion
- MedianteArreglo
- MedianteBuffer
Ahora creamos las pruebas de benchmark, estas a diferencia de las pruebas de test tienen el prefijo Benchmark
y reciben un parámetro *testing.B
. El argumento contiene un valor N
que representa el numero de veces que el loop se va a repetir, este valor es ajustado de forma automática por Golang. En cada uno de los casos invocamos cada uno de los distintos métodos.
package longstring
import "testing"
func BenchmarkMedianteConcatenacion(b *testing.B) {
for i := 0; i < b.N; i++ {
MedianteConcatenacion(100)
}
}
func BenchmarkMedianteArreglo(b *testing.B) {
for i := 0; i < b.N; i++ {
MedianteArreglo(100)
}
}
func BenchmarkMedianteBuffer(b *testing.B) {
for i := 0; i < b.N; i++ {
MedianteBuffer(100)
}
}
En la salida obtenemos los resultados del benchmark.
go test -bench=.
goos: darwin
goarch: amd64
pkg: .../benchmark
BenchmarkMedianteConcatenacion-8 200000 7916 ns/op
BenchmarkMedianteArreglo-8 500000 2464 ns/op
BenchmarkMedianteBuffer-8 1000000 1429 ns/op
PASS
ok .../benchmark 4.378s
En el caso anterior podemos darnos cuenta que basado en el benchmark, el método mas idóneo es BenchmarkMedianteBuffer
.