Goにおける空スライスの作り方
Goでは空のスライスを作る方法が二種類あります。
型Typeについて
1. スライスリテラル記法による作成 []Type{}
2. Goのビルトイン関数による作成 make([]Type, 0)
正直どっちも対してやってることは変わらないだろうと思い習慣的に使っていたmake
による作成をあらゆる場面で使っていました。というのも、make
はキャパシティ指定ができるので後からappend
することを想定した場合にキャパシティ確保分の時間をセーブできるだろうと思っていました。しかしある時にベンチマークを眺めているとruntime.mallocgc
がやたらと時間を食っていたため掘っていくとmake
の行にたどり着いたため、「もしや」と思い空のスライスリテラルに書き換えてみたらパフォーマンスが改善しました。
ベンチマーク
こうなってくると実際にどの程度実行時に影響があるのかが気になります。そこで次のようなベンチマークを書いてみました。
func BenchmarkSliceAllocation(b *testing.B) {
var p []byte
b.Run("empty slice literal syntax", func(b *testing.B) {
for i := 0; i < b.N; i++ {
p = []byte{}
}
})
func([]byte) {}(p)
b.Run("make zero-length slice", func(b *testing.B) {
for i := 0; i < b.N; i++ {
p = make([]byte, 0)
}
})
func([]byte) {}(p)
}
empty_slice_literal_syntax-4 161896716 0.765 ns/op
make_zero-length_slice-4 23673788 5.10 ns/op
実行してみてびっくり、なんと空のスライスリテラルはmake
の6倍以上早かったのです。
最適化に関してはド素人なのでこれ以上は掘ってないのですがそもそもキャパシティ付きでmake
する目的が間違ってたんだろうなという気持ちになりつつあります。
もし詳しい方がいたらご指摘いただけましたら幸いです。