Qual a melhor forma para aumentar um array?

Nesse post vamos ver 3 formas diferentes de aumentar o tamanho de um array em Go.

Para ficar um pouco mais interessante, vamos trabalhara com 2 arrays. O primeiro será um array de string e o segundo um array de Pessoa.

type Pessoa struct {
    Nome string
    Idade int32
}

Primeira forma é também a mais simples. Particularmente, eu tento evitar ao máximo utilizar esse método para aumentar meus arrays, pois embora ela seja a mais simples, é também a mais custosa.

nomes := []string{"Tiago", "Alexandre", "Luara"}
pessoas := []Pessoa{
    {Nome: "Dani", Idade: 35},
    {Nome: "Lucas", Idade: 29},
    {Nome: "Rafael", Idade: 38},
}

for _, pessoa := range pessoas {
    nomes = append(nomes, pessoa.Nome)
}

Como nós já discutimos em outro post (click aqui para ler), essa abordagem faz com que o Go aumente o array nomes uma posição por vez, o que pode custar muito em relação a processamento e memória.

Segunda forma que vamos abordar, é criando um novo array com o tamanho 0 e capacidade 6.

nomes := []string{"Tiago", "Alexandre", "Luara"}
pessoas := []Pessoa{
    {Nome: "Dani", Idade: 35},
    {Nome: "Lucas", Idade: 29},
    {Nome: "Rafael", Idade: 38},
}

todos := make([]string, 0, len(nomes) + len(pessoas))

for _, nome := range nomes {
    todos = append(todos, nome)
}

for _, pessoa := range pessoas {
    todos = append(todos, pessoa.Nome)
}

Embora esse código seja mais extenso, ele garante que não seja necessário realocação do array, já que o criamos com a capacidade necessária desde o inicio. Nesse caso acabamos não aumentando o tamanho de um array, mas sim criando um novo. O lado ruim dessa abordagem é que, ao final da execução teremos 3 arrays ocupando 12 posições na memória.

Na terceira forma, assim como na primeira, vamos expandir o array nomes, mas de uma forma um pouco diferente.

nomes := []string{"Tiago", "Alexandre", "Luara"}
pessoas := []Pessoa{
    {Nome: "Dani", Idade: 35},
    {Nome: "Lucas", Idade: 29},
    {Nome: "Rafael", Idade: 38},
}
// o tamanho do array é a posição seguinte
index := len(nomes)

nomes = append(nomes, make([]string, len(pessoas))...)

for _, pessoa := range pessoas {
    nomes[index] = pessoa.Nome
    index++
}

Nesse último exemplo, criamos um array de string com o tamanho do array pessoas e fazemos o append ao array nomes. A diferença dele para o primeiro é que, como criamos um array vazio e passamos os itens como parâmetro do append, o Go só vai realocar o array 1 vez, pois já sabe que precisa aumentar o array nomes em 3 posições.

Mas afinal, qual dessas abordagens tem melhor performance????

Nos primeiros testes que fiz, usando 2 arrays com 3 posições cada, a diferença não foi muito grande. Por isso resolvi fazer um segundo teste usando o array nomes com 3 posições e o pessoas com 30.

Para o benchmark, executei o seguinte comando:

go test -bench="NomeFunc" -cpu=8 -benchmem -benchtime=5s -count 5 -memprofile=mem.out

Primeira forma

total de memória: 40.54GB

Segunda forma

total de memória: 45.80GB

Terceira forma

total de memória: 37.68GB

Como podemos ver, a terceira forma é sem dúvida a melhor opção, pois tem seus números muito parecidos com a segunda (que é a segunda melhor opção), porém um consumo de memória é 18% menor.

Deixem suas dúvidas nos comentários.

Até a próxima!


Subscreva

Fique por dentro de tudo o que acontece no mundo Go.

Deixe uma resposta