型アサーションをswitchとifでベンチマーク取ってみる

この記事は[Go (その3) Advent Calendar 2016](http://qiita.com/advent-calendar/2016/go3)3日目の記事です！

## はじめに
最近、弊社内で[プログラミング言語Go](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9EGo-ADDISON-WESLEY-PROFESSIONAL-COMPUTING-Donovan/dp/4621300253/ref=sr_1_1?ie=UTF8&qid=1480650776&sr=8-1&keywords=%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0Go%E8%A8%80%E8%AA%9E)の輪読会をしています。
その中で型アサーションと型switchの部分の発表を担当しましたが、確かに見やすいけど実際のところパフォーマンスはどうなんだろう？と感じたのでベンチマークを取ってみました！
goは標準パッケージでベンチマークを取れるもの（[benchmark.go](https://golang.org/src/testing/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のメソッドを使うことができるようになります。

```go
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言語のベンチマークでパフォーマンス測定](https://developers.eure.jp/tech/go-benchmark-evaluation/)



## おわりに
型switchのコードを探してる時に`Bigswitch`というラベルを見つけてちょっとおもしろかったです。
また気になることがあればサッとベンチマークを取って行きたいと思います。
goはtool周りが標準であるのがステキですね！

使ったコードはこちら
https://github.com/futabooo/playground/tree/master/go