Quando trabalhamos com slices, existem duas funções essenciais que utilizamos para “medir” o tamanho (length) e a capacidade (capacity) de um slice, que são len e cap, respectivamente.
Mas afinal, qual a diferença de length e capacity?
A documentação do Go define length como sendo a quantidade de elementos em um slice, enquanto capacity é a capacidade do array para abrigar elementos.
No exemplo abaixo vamos criar 2 slices. No primeiro vamos definir somente o length, enquanto no segundo vamos definir o length e a capacity.
sl1 := make([]string, 10)
sl2 := make([]string, 10, 20)
fmt.Println("sl1", len(sl1), cap(sl1))
fmt.Println("sl2", len(sl2), cap(sl2))
// output
sl1 10 10
sl2 10 20
Como podemos ver, quando não passamos o terceiro parâmetro da função make a capacidade do slice terá o mesmo valor que o tamanho.
Para demonstrar a diferença a nível de performance, vamos escrever duas funções. Na primeira vamos iniciar o slice com X de length e depois expandi-lo até seu tamanho ser 2X. Na segunda, vamos iniciar o slice com X de length e 2X de capacity.
package main
import "strings"
var letters = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
func Len() []string {
sl1 := make([]string, len(letters))
for key, letter := range letters {
sl1[key] = letter
}
for _, letter := range letters {
sl1 = append(sl1, strings.ToUpper(letter))
}
return sl1
}
func Cap() []string {
sl2 := make([]string, len(letters), len(letters)*2)
for key, letter := range letters {
sl2[key] = letter
}
for _, letter := range letters {
sl2 = append(sl2, strings.ToUpper(letter))
}
return sl2
}
Agora vamos escrever um pequeno benchmark para analisar a diferença das duas funções.
package main
import (
"testing"
)
var result []string
func BenchmarkLen(b *testing.B) {
var r []string
for n := 0; n < b.N; n++ {
r = Len()
}
result = r
}
func BenchmarkCap(b *testing.B) {
var r []string
for n := 0; n < b.N; n++ {
r = Cap()
}
result = r
}
Feito isso, vamos executar o benchmark com o seguinte comando:
$ go test -bench=. -cpu=8 -benchmem -benchtime=5s -count 5

Como podemos ver nos resultados acima, a função Cap tem 1 alocação a menos por operação, consumindo 33% menos bytes por operação com um tempo médio 20% mais baixo por operação, o que no final faz com que essa função consiga executar um número maior de operações.
Dessa forma, em casos onde determinado array sempre terá tamanho entre X e Y, a melhor estratégia é criar ele usando length X e capacity Y. Um exemplo pode ser iterar sobre os resultados de uma consulta no banco de dados com limite Y, pois sabemos que o retorno será entre 0 e Y.
Claro que, caso você saiba a quantidade exata retornada na consulta, o ideal é criar o array no tamanho exato pois a performance seria melhor ainda.
Deixem suas dúvidas nos comentários.
Até a próxima!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.