スーパーハカー幼女公主

プログラミング成分多め

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)

}

play.golang.org

Playgroundでは負荷かかる処理は実行できないのでローカルでどうぞ

今回は、構造体のメンバも int1個なので、ポインタでも値でも変わらないサイズなので
殆どオーバーヘッドはないはず

結果は毎回誤差はあるが

0.640506sec
0.644128sec
0.642002sec
1.175025sec

基本的に、直接呼出し、構造体値渡し、構造体ポインタ渡しは 誤差範囲 ただ、インタフェース呼び出しは約2倍かかった

5億回の呼び出しで0.5秒のオーバーヘッド。10億分の1秒だ
うちのPCが3.6GHzなので、だいたい4クロック程度(スーパースカラー無視して)

オブジェクト指向であれば、仮想関数テーブル呼び出しなどでそこそこ大きなオーバーヘッドが想定されるが
Goの場合はダックタイピングなので、単純に普通の関数呼び出しが1回増えただけなんだろう

オブジェクト指向とちがい、この辺りのオーバーヘッドは無いに等しい
意外とダックタイピングいいかも
ま、C++でも可能ならオブジェクト指向ではなく、テンプレートでダックタイピング継承するし。