host error什么意思_go语言学习总结(三十九)eflect为什么慢

在上一篇《reflect实践与剖析》的文章中,本来打算写的更深入一点,结果发现reflect里面的东西太多了,在一篇文章中很难写全,就草草收尾了。今天谈一谈关于reflect的另一个问题:reflect为什么慢?

先问是不是,再问为什么。我们先来看一下reflect是不是真的慢。

Golang testing

golang提供了一个testing包,使得单元测试、性能测试尤为简单。只要新建一个以_test结尾的文件,然后使用命令go test就可以自动执行文件中的相应测试函数了(单元测试函数以Test开头,性能测试函数以Benchmark开头)。除了单元测试和性能测试,testing还有其他的功能,暂且按住不表。下面主要介绍一下单元测试和性能测试怎么写。

单元测试

比如我们写了一个简单的浮点数除法函数并编写单元测试。

//浮点除法函数: division.gofunc Division(a, b float64) (float64, error) {  return a/b, nil}//单元测试: division_test.gofunc TestDivision(t *testing.T) {    if i, e := Division(9.3, 3); i!=3.1 || e!= nil {        t.Error("Division Normal Test Failed")    } else if i, e:= Division(1,0); i!=0 || e.Error()!="divisor can't be 0"{        t.Error("Division divisor=0 test Failed")    } else {        t.Log("Division Test Failed")    }}

执行使用go test filename_test.go,对filename_test.go文件进行测试,如果不加文件,则运行当前目录下的所有已_test结尾的文件。为了便于测试Division函数特意没有考虑除数为0的情况。测试结果如下。

--> gotest $ go test division_test.go--- FAIL: TestDivision (0.00s)division_test.go:19: Division divisor=0 test FailedFAIL

完善Division函数如下,再运行显示ok。

func Division(a, b float64) (float64, error){    if b == 0 {        return 0, fmt.Errorf("divisor can't be 0")    }    return a/b, nil}

性能测试

还是对于上面的Division函数,编写性能测试函数如下。

func BenchmarkDivision(b *testing.B) {    for i:=0; i

性能测试的时候需要加一个-bench参数,对于我们这个例子使用go test -bench='.' division_test.go即可,执行结果如下:

PASSBenchmarkDivision-85000000003.07 ns/opok

中间一行的意思是执行了5亿次,每次耗时3.07纳秒。

Reflect Benchmark 测试

我们使用golang testing来做一下reflect的最简单的性能测试。

import (    "testing"    "reflect")type Bench struct {    A int}func Foo1(b Bench) {    _ = Bench{}}func Foo2(x interface{}) {    _ = reflect.ValueOf(x)}func BenchmarkFoo1(b *testing.B) {    var x Bench    for i:=0; i

运行go test -bench='.',结果如下

PASSBenchmarkFoo1-82000000000.47 ns/opBenchmarkFoo2-8300000036.7 ns/op

比较来看差距确实比较大啊,当然如果单纯从时间角度来看感觉还好。我们来看一下reflect的源码,试着分析一下时间都浪费在了什么地方。

func ValueOf(i interface{}) Value {    if i == nil {        return Value{}    }    escapes(i)//异常处理,先不用管    return unpackEface(i)}func unpackEface(i interface{}) Value {    e := (*emptyInterface)(unsafe.Pointer(&i))    t := e.typ    if t == nil {        return Value{}    }    f := flag(t.Kind())    if ifaceIndir(t) {        f |= flagIndir    }    return Value{t, e.word, f}}

ValueOf的工作是将built-in类型或者自定义类型(比如struct)转换成reflect包中的Value Struct,看上去耗时的地方应该就是最后面的内存分配了。为了验证想法,可以做一下CPU Profiling

Golang Profiling

golang提供了一套工具pprof用来做性能分析与优化,主要分为两种:memory使用情况和CPU使用情况。关于pprof的具体使用这里就不细说,后面会把他和gcvis放在一起再说一下。为了做profiling,需要在原程序插入一些代码,主要是做采集用的,代码如下,插入的代码已经标记出来了。

import (    "flag"    "fmt"    "os"    "reflect"    "runtime/pprof")type Kltao struct {    Ageint}func Foo(k interface{}) {    _ = reflect.ValueOf(k)}var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")func main() {    flag.Parse()    if *cpuprofile != "" {        f, err := os.Create(*cpuprofile)        if err != nil {            fmt.Println(err)        }        pprof.StartCPUProfile(f)        defer pprof.StopCPUProfile()    }    for i:=0; i<50000000; i++ {        var k Kltao        Foo(k)    }}
$ go build reflectt.go$ ./reflectt cpuprofile=reflect.prof$ go tool pprof reflectt reflect.profEntering interactive mode (type "help" for command)(pprof) web

看了上图我只想说,这个真不是我想象的那么简单,这个图真是看的脑袋疼。仔细分析之后可以得到一个大概结论,reflect慢主要有两个原因:一是涉及到内存分配以后GC;二是reflect实现里面有大量的枚举,也就是for循环,比如类型之类的。

其他Benchmark Test

上面的测试太简单了,可能会有以偏概全的嫌疑,羡慕提供了一个函数调用的性能测试,profiling图我先自己研究研究。

package testimport (    "testing"    "reflect")type Kltao struct {//Name string    Age  int//B    int//C    int//D    int}func (k Kltao) Foo() {    for i := 0; i < 10000; i++ {        k.Age++    }}func (k Kltao) Foo1() {    for i := 0; i < 10000; i++ {        k.Age++    }}func (k Kltao) Foo2() {    for i := 0; i < 1000000; i++ {        k.Age++    }}func (k Kltao) Foo3() {    for i := 0; i < 10000; i++ {        k.Age++    }}func SimpleFoo(kltao Kltao) {    kltao.Foo1()}func ReflectFoo(k interface{}) {    v := reflect.ValueOf(k)    foo := v.MethodByName("Foo1")    foo.Call(nil)}func BenchmarkSimpleFoo(b *testing.B) {    var kltao Kltao    for i := 0; i < b.N; i++ {        SimpleFoo(kltao)    }}func BenchmarkReflectFoo(b *testing.B) {    var kltao Kltao    for i := 0; i < b.N; i++ {        ReflectFoo(kltao)    }}

性能测试结果如下。

PASSBenchmarkSimpleFoo-85000003206 ns/opBenchmarkReflectFoo-8500000 3547 ns/op
18c79de8dd877b16146a45d7d11d7c32.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值