Effective Go#channelsに載ってるclosureへのパラメータの渡し方2パターンのperformanceを比較した。
きっかけ
-
Go研vol.16で、ほぼ同じだけどclosureへのパラメータの渡し方だけ違うコードがあった。
-
2つの書き方でperformanceに差があるのか調べてみよう。
closureへのパラメータの渡し方
以下はEffective Goからの引用。
loop変数はloop内で繰り返し再利用されるため、下記コードはバグがある。
全てのgoroutineでloop変数を共有した状態になってしまう。
bug.go
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func() {
process(req)
<- sem
}()
}
}
解決策として、Effective Goでは以下2パターンを上げている。
1.closureに引数として渡す
pattern1.go
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func(req *Request) {
process(req)
<-sem
}(req)
}
}
2.loop中でloop変数と同じ名前の変数を作る
pattern2.go
func Serve(queue chan *Request) {
for req := range queue {
req := req // Create new instance of req for the goroutine.
sem <- 1
go func() {
process(req)
<-sem
}()
}
}
performance比較
上記2パターンのperformanceをgo test -benchで比較した。
ソースコード
results
BenchmarkClosure1 500 5356625 ns/op 11165 B/op 38 allocs/op
BenchmarkClosure2 500 6917004 ns/op 262473 B/op 20029 allocs/op
結果
- pattern2はpattern1と比較して、より多くのメモリを確保する
- ちゃんとclosureに引数として渡すように書いたほうが良い