Я только начинаю с Go и у меня возникает ситуация, когда мне нужно создать набор сущностей, размер / длина которых известен только во время выполнения.Сначала я подумал, что использование списка было бы неплохо, но вскоре понял, что срезы - это идиоматическая структура данных в Go.Любопытно, я написал следующие тесты
package main
import (
"container/list"
"testing"
)
var N = 10000000
func BenchmarkSlices(B *testing.B) {
s := make([]int, 1)
for i := 0; i < N; i += 1 {
s = append(s, i)
}
}
func BenchmarkLists(B *testing.B) {
l := list.New()
for i := 0; i < N; i += 1 {
l.PushBack(i)
}
}
, которые дали мне
BenchmarkSlices-4 2000000000 0.03 ns/op
BenchmarkLists-4 1 1665489308 ns/op
Учитывая, что append
создаст новый массив и скопирует все данные из старого в новыйКогда старый массив заполнится, я ожидал, что списки будут работать лучше, чем срезы в примере выше.Однако мои ожидания явно ошибочны, и я пытаюсь понять, почему.
Я написал следующее, чтобы немного лучше понять, как append
создает новые массивы, когда это необходимо:
package main
import "fmt"
func describe(s []int) {
fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))
}
func main() {
s := make([]int, 2)
for i := 0; i < 15; i += 1 {
fmt.Println(i)
describe(s)
s = append(s, i)
}
}
, который дал мне
0
len = 2, cap = 2
1
len = 3, cap = 4
2
len = 4, cap = 4
3
len = 5, cap = 8
4
len = 6, cap = 8
5
len = 7, cap = 8
6
len = 8, cap = 8
7
len = 9, cap = 16
8
len = 10, cap = 16
9
len = 11, cap = 16
10
len = 12, cap = 16
11
len = 13, cap = 16
12
len = 14, cap = 16
13
len = 15, cap = 16
14
len = 16, cap = 16
На данный момент я могу только догадываться, почему срезы работают лучше, чем списки, - это то, что выделение памяти для нового массива с двойным размером и копирование всех данных происходит быстреечем выделение памяти для одного элемента каждый раз, когда происходит вставка.Правильно ли мое предположение?Я что-то упустил?