由于Golang不支持泛型,所以我们会使用Golang的interface{]reflect来实现语言的动态特性。但是,这个也会带来很严重的性能下降问题。但是,到底有多慢呢?我们可以通过Golang的testing库的Benchmark来测试一下。

测试程序

bench.go的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package bench

import (
	"reflect"
)

type process struct{}

func (p *process) Run1(a []int) int {
	return 0
}

func (p *process) Run2() int {
	return 0
}

func staticRun1(p *process, a []int) {
	p.Run1(a)
}

func staticRun2(p *process) {
	p.Run2()
}

func dynamicRun1(p interface{}, data interface{}) {
	reflect.ValueOf(p).MethodByName("Run1").Call([]reflect.Value{reflect.ValueOf(data)})
}

func dynamicRun2(p interface{}) {
	reflect.ValueOf(p).MethodByName("Run2").Call([]reflect.Value{})
}

主要是测试 staticRundynamicRun方法的性能差距。注意,process类型的Run1方法和Run2方法必须是exported的。

另外建一个文件 bench_test.go保存benchmark代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package bench

import (
	"reflect"
	"testing"
)

func BenchmarkStaticRun1(b *testing.B) {
	p := new(process)
	a := []int{1, 2, 3}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		staticRun1(p, a)
	}
}

func BenchmarkDynamicRun1(b *testing.B) {
	p := new(process)
	a := []int{1, 2, 3}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		dynamicRun1(p, a)
	}
}

func BenchmarkStaticRun2(b *testing.B) {
	p := new(process)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		staticRun2(p)
	}
}

func BenchmarkDynamicRun2(b *testing.B) {
	p := new(process)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		dynamicRun2(p)
	}
}

func BenchmarkValueOf(b *testing.B) {
	p := new(process)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		reflect.ValueOf(p)
	}
}

func BenchmarkMethodByName(b *testing.B) {
	p := new(process)
	v := reflect.ValueOf(p)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		v.MethodByName("Run2")
	}
}

func BenchmarkCallRun2(b *testing.B) {
	p := new(process)
	v := reflect.ValueOf(p).MethodByName("Run2")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		v.Call([]reflect.Value{})
	}
}

测试结果

执行go test -bench-.来运行测试:

➜ ~/go/src/goexamples/bench  $ go test -bench=.
testing: warning: no tests to run
BenchmarkStaticRun1-4           2000000000               0.45 ns/op
BenchmarkDynamicRun1-4           1000000              1840 ns/op
BenchmarkStaticRun2-4           2000000000               0.42 ns/op
BenchmarkDynamicRun2-4           1000000              1389 ns/op
BenchmarkValueOf-4              200000000                7.56 ns/op
BenchmarkMethodByName-4          2000000               753 ns/op
BenchmarkCallRun2-4              3000000               531 ns/op
PASS
ok      goexamples/bench        11.742s

BenchmarkStaticRun1BenchmarkDynamicRun1 这两个测试结果显示,采用动态的方式来调用一个方法,并且传递一个动态的参数,要比使用纯静态的方式慢大概4000倍。

BenchmarkStaticRun2BenchmarkDynamicRun2 这两个测试结果显示,采用动态的方式来调用一个方法,不传递任何参数,要比使用纯静态的方式慢大概3300倍。

另外的三个结果测给出了一次动态调用中,不同部分的耗时,可以看出动态获取方法以及调用这个方法都是很慢的。


知识共享许可协议本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。