Golangの値渡し、ポインタ渡し、インタフェース呼び出しの計測
interface呼び出しのパフォーマンスが気になったので
ついでに値レシーバとポインタレシーバの比較もしてみた
package main import ( "fmt" "time" ) type IHoge interface { Print() } type HogeImpl struct { num int } func (s *HogeImpl) Print() { //fmt.Printf("%d\n", s.num) //s.num = s.num + 1 } func print1(s HogeImpl) { s.Print() } func print2(s *HogeImpl) { s.Print() } func printInterface1(s IHoge) { s.Print() } func printInterface2(s *IHoge) { (*s).Print() } func bench(f func()(),l int){ start := time.Now() for i:=0; i< l; i++{ f() } end := time.Now() fmt.Printf("%fsec\n", (end.Sub(start)).Seconds()) } func main() { t := 500000000 h := &HogeImpl{0} bench(func(){h.Print()}, t) bench(func(){print1(*h)}, t) bench(func(){print2(h)}, t) bench(func(){printInterface1(h)}, t) // cannot use &h (type **HogeImpl) as type *IHoge in argument to printInterface2: // *IHoge is pointer to interface, not interface //printInterface2(&h) //printInterface2(&h) }
Playgroundでは負荷かかる処理は実行できないのでローカルでどうぞ
今回は、構造体のメンバも int1個なので、ポインタでも値でも変わらないサイズなので
殆どオーバーヘッドはないはず
結果は毎回誤差はあるが
0.640506sec 0.644128sec 0.642002sec 1.175025sec
基本的に、直接呼出し、構造体値渡し、構造体ポインタ渡しは 誤差範囲 ただ、インタフェース呼び出しは約2倍かかった
5億回の呼び出しで0.5秒のオーバーヘッド。10億分の1秒だ
うちのPCが3.6GHzなので、だいたい4クロック程度(スーパースカラー無視して)
オブジェクト指向であれば、仮想関数テーブル呼び出しなどでそこそこ大きなオーバーヘッドが想定されるが
Goの場合はダックタイピングなので、単純に普通の関数呼び出しが1回増えただけなんだろう
オブジェクト指向とちがい、この辺りのオーバーヘッドは無いに等しい
意外とダックタイピングいいかも
ま、C++でも可能ならオブジェクト指向ではなく、テンプレートでダックタイピング継承するし。