appendって毎回新しいスライスが作られているような感じに見えるので、なんか遅そうなイメージがあるんすよね。まぁ、実際には内部で参照している配列のキャパシティに余裕があれば空きに追加されるだけなのでキャパシティさえあれば高速なのだが。
では、実際にキャパシティの有無でどのくらい差が出るのか?
キャパシティ未指定の場合
const x = 200000000 func main() { hoge := []int{} for i := 0; i < x; i++ { hoge = append(hoge, i) } fmt.Println(hoge[0] + hoge[x-1]) }
$ time go run 1.go 199999999 go run 1.go 5.87s user 5.83s system 139% cpu 8.393 total
キャパシティを指定した場合
const x = 200000000 func main() { hoge := make([]int, 0, x) for i := 0; i < x; i++ { hoge = append(hoge, i) } fmt.Println(hoge[0] + hoge[x-1]) }
$ time go run 2.go 199999999 go run 2.go 0.78s user 0.71s system 135% cpu 1.104 total
7〜8倍程度の高速化。
動的言語ではキャパシティが不足したら現在の倍のキャパシティを確保という感じのアルゴリズムになっているため、こんなに差が付かないのだが、Goはそういう余計なことはしないらしい。GCありの言語のくせにこういう所では低水準言語感を示してくるなこやつw
インデックスアクセスで値をセット
const x = 200000000 func main() { hoge := make([]int, x) for i := 0; i < x; i++ { hoge[i] = i } fmt.Println(hoge[0] + hoge[x-1]) }
$ time go run 3.go 199999999 go run 3.go 0.81s user 0.80s system 137% cpu 1.170 total
容量確保してappendとほぼ同じ速度。
まぁ、あえてインデックスアクセスで書く理由もないのでキャパシティ確保した上でappendする方がいいと思います。