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!