Golang的reflect有多慢?
由于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{})
}
主要是测试 staticRun和 dynamicRun方法的性能差距。注意,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
BenchmarkStaticRun1和BenchmarkDynamicRun1 这两个测试结果显示,采用动态的方式来调用一个方法,并且传递一个动态的参数,要比使用纯静态的方式慢大概4000倍。
BenchmarkStaticRun2和BenchmarkDynamicRun2 这两个测试结果显示,采用动态的方式来调用一个方法,不传递任何参数,要比使用纯静态的方式慢大概3300倍。
另外的三个结果测给出了一次动态调用中,不同部分的耗时,可以看出动态获取方法以及调用这个方法都是很慢的。
