Golang仿函数实现方法及效率测试

来源:互联网 发布:网络问卷调查怎么弄 编辑:程序博客网 时间:2024/05/01 03:16

在C++ STL中,仿函数(functors)被大量用作改变算法的内在行为。
由于Golang不支持泛型,所以没法像C++那样灵活的使用仿函数。但是Golang有interface,函数是”一等公民”(可赋值给指定类型变量),因此,在Golang中实际上也可以像仿函数那样,通过具有相同参数和返回值的方法声明的不同对象,实现行为的差异化。
下面,详细讲述,Golang中的实现方法:
以下通过不同方法实现 Lesser(int,int)bool 和 Greater(int,int)bool的不同行为举例。
1. 通过interface实现

type Comparer interface {    F(left, right int) bool}//create cmp object by namefunc CreateComparer(cmpName string) (r Comparer) {    switch cmpName {    case "": //default Lesser        fallthrough    case "Lesser":        r = Lesser{}    case "Greater":        r = Greater{}    default: //unsupport name        panic(cmpName)    }    return}//Lessertype Lesser struct{}func (this Lesser) F(left, right int) (ok bool) {    ok = left < right    return}//Greatertype Greater struct{}func (this Greater) F(left, right int) (ok bool) {    ok = right < left    return}

2.通过函数对象实现

type CmpFunc func(left, right int) bool//create cmp object by namefunc GetCmpFunc(cmpName string) (r CmpFunc) {    switch cmpName {    case "": //default Lesser        fallthrough    case "Lesser":        r = Less    case "Greater":        r = Great    default: //unsupport name        panic(cmpName)    }    return}//Lesserfunc Less(left, right int) (ok bool) {    ok = left < right    return}//Greaterfunc Great(left, right int) (ok bool) {    ok = right < left    return}

3.通过转调对象实现

type CmpObj byteconst (    CMP_LESS CmpObj = iota    CMP_GREAT)func (me CmpObj) F(left, right int) (ok bool) {    switch me {    case CMP_LESS:        ok = Less(left, right)    case CMP_GREAT:        ok = Great(left, right)    default:        panic(me)    }    return}

以下是3种方法的效率测试:

var (    cmp1 = CreateComparer("Lesser")    cmp2 = GetCmpFunc("Lesser")    cmp3 = CMP_LESS    N    = 100000000)func TestSize(t *testing.T) {    fmt.Println("Interface", unsafe.Sizeof(cmp1))    fmt.Println("Func", unsafe.Sizeof(cmp2))    fmt.Println("Obj", unsafe.Sizeof(cmp3))}func Benchmark_Interface(b *testing.B) {    for i := 0; i < N; i++ {        cmp1.F(1, 2)    }}func Benchmark_Func(b *testing.B) {    for i := 0; i < N; i++ {        cmp2(1, 2)    }}func Benchmark_Obj(b *testing.B) {    for i := 0; i < N; i++ {        cmp3.F(1, 2)    }}//Interface 16//Func 8//Obj 1//Benchmark_Interface-4     1000000000           0.55 ns/op//Benchmark_Func-4          2000000000           0.19 ns/op//Benchmark_Obj-4           2000000000           0.24 ns/op

结论如下:
用interface实现多态,会占用两个指针(16字节空间) 执行效率上 大概慢一倍
使用函数指针 占用1个指针(8字节) 执行效率最高 但使用起来不够灵活
使用转调对象 只需要1个字节 执行效率跟函数指针差不多

推荐使用第三种方法(转调对象)的方法,使用数值枚举标识多路分发逻辑,效率上几乎没有损失,对外部引用对象的空间需求也比较小。

我将以上测试代码放在这里,欢迎查阅:
https://github.com/vipally/glab/blob/master/lab5/lab5_test.go

0 0
原创粉丝点击