この記事はGo (その3) Advent Calendar 20163日目の記事です!
はじめに
最近、弊社内でプログラミング言語Goの輪読会をしています。
その中で型アサーションと型switchの部分の発表を担当しましたが、確かに見やすいけど実際のところパフォーマンスはどうなんだろう?と感じたのでベンチマークを取ってみました!
goは標準パッケージでベンチマークを取れるもの(benchmark.go)が存在しているので便利ですね♪
環境
- OSX 10.11.6
- プロセッサ 2.2GHz Intel Core i7
- メモリ 16GB
- go 1.6.3
結果
結論として、パフォーマンスは変わらないので可読性が高いswitch文を使った方が圧倒的によいなーと思いました。
ただ場合によってメモリのアロケーションが増える場合もあるので、その時はまた考える必要がありそうです。
(case文の中で引数のメソッドを使いたいような場合)
$ go test -bench . -benchmem 2>/dev/null
BenchmarkTypeSwitch-8 300000 4224 ns/op 64 B/op 8 allocs/op
BenchmarkTypeIf-8 300000 4326 ns/op 64 B/op 8 allocs/op
コード
今回のベンチマークでは下記のようなTypeSwitch
とTypeIf
という関数を用意しました。
TypeSwitchの中でコメントアウトしている部分で変数への代入を行うと、それぞれのcase文の中で特定のinterfaseのメソッドを使うことができるようになります。
package main
import (
"log"
)
func TypeSwitch(i interface{}) {
switch i.(type) {
//switch i := i.(type) { // 特定のinterfaceのメソッド使いたい場合はこっちのほうが良いかも。
case bool:
//if i{ // switch i:= i.(type)の書き方でないとコンパイルエラーになる
// // do...
//}
log.Printf("%T", i)
case float32:
log.Printf("%T", i)
case float64:
log.Printf("%T", i)
case complex64:
log.Printf("%T", i)
case complex128:
log.Printf("%T", i)
case int:
log.Printf("%T", i)
case int8:
log.Printf("%T", i)
case int16:
log.Printf("%T", i)
case int32:
log.Printf("%T", i)
case int64:
log.Printf("%T", i)
case uint:
log.Printf("%T", i)
case uint8:
log.Printf("%T", i)
case uint16:
log.Printf("%T", i)
case uint32:
log.Printf("%T", i)
case uint64:
log.Printf("%T", i)
case uintptr:
log.Printf("%T", i)
case string:
log.Printf("%T", i)
case []byte:
log.Printf("%T", i)
}
}
func TypeIf(i interface{}) {
if _, ok := i.(bool); ok {
log.Printf("%T", i)
}
if _, ok := i.(float32); ok {
log.Printf("%T", i)
}
if _, ok := i.(float64); ok {
log.Printf("%T", i)
}
if _, ok := i.(complex64); ok {
log.Printf("%T", i)
}
if _, ok := i.(complex128); ok {
log.Printf("%T", i)
}
if _, ok := i.(int); ok {
log.Printf("%T", i)
}
if _, ok := i.(int8); ok {
log.Printf("%T", i)
}
if _, ok := i.(int16); ok {
log.Printf("%T", i)
}
if _, ok := i.(int32); ok {
log.Printf("%T", i)
}
if _, ok := i.(int64); ok {
log.Printf("%T", i)
}
if _, ok := i.(uint8); ok {
log.Printf("%T", i)
}
if _, ok := i.(uint16); ok {
log.Printf("%T", i)
}
if _, ok := i.(uint32); ok {
log.Printf("%T", i)
}
if _, ok := i.(uint64); ok {
log.Printf("%T", i)
}
if _, ok := i.(uintptr); ok {
log.Printf("%T", i)
}
if _, ok := i.(string); ok {
log.Printf("%T", i)
}
if _, ok := i.([]byte); ok {
log.Printf("%T", i)
}
}
参考
-
https://golang.org/src/fmt/print.go (ベンチマークで使ったswitch文はprint.goの
printArg(arg interface{}, verb rune)
を参考にしました) - Go言語のベンチマークでパフォーマンス測定
おわりに
型switchのコードを探してる時にBigswitch
というラベルを見つけてちょっとおもしろかったです。
また気になることがあればサッとベンチマークを取って行きたいと思います。
goはtool周りが標準であるのがステキですね!
使ったコードはこちら
https://github.com/futabooo/playground/tree/master/go