はじめに
こんにちは、LIFULL HOME'S事業本部 技術開発部の宮崎です。
これはLIFULL その2 Advent Calendarの15日目の投稿になります。
ついでにLIFULL Advent Calendarもよければご覧下さい。
8日目にも記事を書きました。よければご覧ください。
強いエンジニアにHelloWorldさせてみた
Benchmark
golangは色々なツールが公式で提供されています。
その中で今回はbenchmarkツールを用いて疑問に思っていた内容のベンチマークをとってみようと思います。
内容
今年のISUCONの問題を解いている時に、sliceの先頭に要素を追加する必要がありました。
その時に、sliceの型が参照である場合と、値である時どちらのほうが早いのか疑問に思いました。
以下のようなものです。
type User struct {
Id int
a int
b int
c int
d string
}
// 参照のslice
sliceRef := make([]*User)
// 値のslice
sliceVal := make([]User)
それぞれの要素のサイズがいくつになるか計算してみます。
package main
import (
"fmt"
"unsafe"
)
type User struct {
Id int
a int
b int
c int
d string
}
func main() {
u0 := &User{}
u1 := User{}
fmt.Println(unsafe.Sizeof(u0))
fmt.Println(unsafe.Sizeof(u1))
}
8
48
&User{}
は参照で、User{}
は値なので当然ですね。
参照のほうがサイズが小さいため、早そうです。
実行結果
package main
import "testing"
type User struct {
Id int
a int
b int
c int
d string
}
func BenchmarkRef(b *testing.B) {
slice := make([]*User, 0)
for i := 0; i < b.N; i++ {
slice = append(slice, &User{Id: i})
}
}
func BenchmarkVal(b *testing.B) {
slice := make([]User, 0)
for i := 0; i < b.N; i++ {
slice = append(slice, User{Id: i})
}
}
$ go test -bench . -benchmem
goos: darwin
goarch: amd64
BenchmarkRef-4 5000000 227 ns/op 91 B/op 1 allocs/op
BenchmarkVal-4 2000000 524 ns/op 246 B/op 0 allocs/op
PASS
ok _/Users/taisuke/Desktop/bench 3.141s
やはり参照のほうが早そうです。
しかし、このベンチのコードの中では、sliceを確保する際に容量を指定していません。
容量を指定してみます。
package main
import "testing"
func BenchmarkRef(b *testing.B) {
slice := make([]*User, 0, b.N)
for i := 0; i < b.N; i++ {
slice = append(slice, &User{Id: i})
}
}
func BenchmarkVal(b *testing.B) {
slice := make([]User, 0, b.N)
for i := 0; i < b.N; i++ {
slice = append(slice, User{Id: i})
}
}
$ go test -bench . -benchmem
goos: darwin
goarch: amd64
BenchmarkRef-4 10000000 193 ns/op 56 B/op 1 allocs/op
BenchmarkVal-4 20000000 184 ns/op 48 B/op 0 allocs/op
PASS
ok _/Users/taisuke/Desktop/bench 6.111s
意外な結果になりました。
まとめ
想像だけでコードを書いてはいけないですね。
場面によって内容が異なると思いますので、パフォーマンスが気になる場合は自分でベンチマークを取ってみてはいかがでしょうか。
どうしてこのような原因になったのかは後日調べてみようと思います。
本当はgolangでのduck typeの実装について調べようと思っていたけど、気付いたら時間が…