伸長するsliceの取扱い
Goで何かしら値を計算した結果をsliceに格納するときにcapを事前に設定出来ない場合もある。
// capがわかってない場合 var slice []T // cap(N)がわかっている場合 slice := make([]T, 0, n)
append時にcapが足りない場合、slice内部の値をコピーして伸長する。 そのため、capを事前に設定できない場合、sliceに格納するstructは実態ではなく参照にしておくことが望ましい。
package slice import ( "testing" ) type T struct { A int } type T2 struct { A int B string } type T3 struct { A int B string C []string } func BenchmarkReal(b *testing.B) { var slices []T for i := 0; i < b.N; i++ { t := T{A: 1} slices = append(slices, t) } } func BenchmarkReal2(b *testing.B) { var slices []T2 for i := 0; i < b.N; i++ { t := T2{A: 1, B: "B"} slices = append(slices, t) } } func BenchmarkReal3(b *testing.B) { var slices []T3 for i := 0; i < b.N; i++ { t := T3{A: 1, B: "B", C: []string{"C"}} slices = append(slices, t) } } func BenchmarkPointer(b *testing.B) { var slices []*T for i := 0; i < b.N; i++ { t := &T{A: 1} slices = append(slices, t) } } func BenchmarkPointer2(b *testing.B) { var slices []*T2 for i := 0; i < b.N; i++ { t := &T2{A: 1, B: "B"} slices = append(slices, t) } } func BenchmarkPointer3(b *testing.B) { var slices []*T3 for i := 0; i < b.N; i++ { t := &T3{A: 1, B: "B", C: []string{"C"}} slices = append(slices, t) } }
$ go test -bench=. -benchmem [~/src/github.com/yoppi/go] goos: darwin goarch: amd64 pkg: github.com/yoppi/go BenchmarkReal-4 100000000 14.2 ns/op 49 B/op 0 allocs/op BenchmarkReal2-4 5000000 220 ns/op 132 B/op 0 allocs/op BenchmarkReal3-4 3000000 551 ns/op 272 B/op 1 allocs/op BenchmarkPointer-4 5000000 235 ns/op 51 B/op 1 allocs/op BenchmarkPointer2-4 10000000 220 ns/op 74 B/op 1 allocs/op BenchmarkPointer3-4 5000000 240 ns/op 107 B/op 2 allocs/op PASS ok github.com/yoppi/go 10.506s
興味深いのは、フィールドがint一つだけの場合参照ではなく実態の方が高速に扱えていること。このあたりもうちょっと調べる。