为什么反映Type.Implements()比类型断言慢得多?

时间:2017-10-19 19:29:20

标签: go reflection

我试图有效地测试接口{}是否实现了给定的函数,我的解决方案是创建一个只有这个函数的接口,然后检查接口{}是否实现了这个单一的函数接口。这里的两个选项似乎是使用反射或类型断言。两者似乎都有相同的行为,但速度差异很大。

查看Value.Implements()的代码,它对值上定义的函数进行线性扫描,并将它们与接口进行比较。 然而,类型断言似乎只进行了一个恒定的时间比较(与接口中的函数数量无关)。

有没有理由为什么Implements()不会做一个类型断言?

基准:

package benchmarks

import (
    "reflect"
    "testing"
)

type ITest interface {
    Foo()
}

type Base struct{}

func (Base) A() {}
func (Base) B() {}
func (Base) C() {}
func (Base) D() {}
func (Base) E() {}
func (Base) F() {}
func (Base) G() {}
func (Base) H() {}
func (Base) I() {}
func (Base) J() {}

var Interface = reflect.TypeOf((*ITest)(nil)).Elem()

func BenchmarkReflection(b *testing.B) {
    var iface interface{}
    iface = Base{}
    for i := 0; i < b.N; i++ {
        if reflect.TypeOf(iface).Implements(Interface) {
            b.FailNow()
        }
    }
}

func BenchmarkAssertion(b *testing.B) {
    var iface interface{}
    iface = Base{}
    for i := 0; i < b.N; i++ {
        if _, ok := iface.(ITest); ok {
            b.FailNow()
        }
    }
}

结果:

go test -run=XXX -bench=. so_test.go
goos: linux
goarch: amd64
BenchmarkReflection-8           10000000                  208 ns/op
BenchmarkAssertion-8            200000000                9.24 ns/op
PASS
ok      command-line-arguments  5.115s

1 个答案:

答案 0 :(得分:8)

Go中的类型断言依赖于一个名为How do I disable Laravel view cache?的函数。如果您查看代码,您会发现它依赖于getitabadditab依赖于additab(在同一文件中)。

现在,检查给定类型是否在Implements内实现接口的实际逻辑与reflect中的// both inter and typ have method sorted by name, // and interface names are unique, // so can iterate over both in lock step; // the loop is O(ni+nt) not O(ni*nt). 完全相同 - 线性搜索,甚至指出在这个评论中:

additab

然而,区别在于df[grep("TissueA", names(df)] # Extract sum(grepl("TissueA", names(df)) # Count 实际上利用了缓存 - 类型断言的结果存储在哈希映射中,因此相同类型的后续类型断言将在恒定时间内运行,这就是为什么你'重新看到性能上的巨大差异。