07/24
2021
reflect性能测试
本节来测试一下golang中的reflect常见方法的性能,以及对比断言的性能
interface断言
interface断言在很多时候都不太好看,主要对于后期维护上来说确实是相当困难。因为你不得不判断异常情况,否则会panic。
我们来对比一下基础类型、复合类型map、结构类型struct、指针类型分别耗时情况如何
// 源代码
func AssertInt(srcData interface{}) (int, error) {
if val, ok := srcData.(int); ok {
return val, nil
}
return -1, errors.New("srcData type not int")
}
func AssertFloat64(srcData interface{}) (float64, error) {
if val, ok := srcData.(float64); ok {
return val, nil
}
return -1, errors.New("srcData type not float")
}
func AssertMap(srcData interface{}) (map[string]interface{}, error) {
if val, ok := srcData.(map[string]interface{}); ok {
return val, nil
}
return nil, errors.New("srcData type not map")
}
func AssertMapPtr(srcData interface{}) (*map[string]interface{}, error) {
if val, ok := srcData.(*map[string]interface{}); ok {
return val, nil
}
return nil, errors.New("srcData type not map")
}
type Person struct {
Name string
Age int
}
func AssertStruct(srcData interface{}) (p Person, err error) {
if val, ok := srcData.(Person); ok {
return val, nil
}
return p, errors.New("srcData type not struct")
}
func AssertStructPtr(srcData interface{}) (*Person, error) {
if val, ok := srcData.(*Person); ok {
return val, nil
}
return nil, errors.New("srcData type not struct")
}
// 测试代码
func BenchmarkAssertInt(b *testing.B) {
for n := 0; n < b.N; n++ {
AssertInt(1)
}
}
func BenchmarkAssertFloat64(b *testing.B) {
for n := 0; n < b.N; n++ {
AssertFloat64(float64(1))
}
}
var mapData = map[string]interface{}{"a": 1}
func BenchmarkAssertMap(b *testing.B) {
for n := 0; n < b.N; n++ {
AssertMap(mapData)
}
}
func BenchmarkAssertMapPtr(b *testing.B) {
for n := 0; n < b.N; n++ {
AssertMapPtr(&mapData)
}
}
var data = Person{"abc", 12}
func BenchmarkAssertStruct(b *testing.B) {
for n := 0; n < b.N; n++ {
AssertStruct(data)
}
}
func BenchmarkAssertStructPtr(b *testing.B) {
for n := 0; n < b.N; n++ {
AssertStructPtr(&data)
}
}
// go test -bench .
goos: darwin
goarch: amd64
pkg: blurty/test/benchtest
cpu: VirtualApple @ 2.50GHz
BenchmarkAssertInt-8 1000000000 0.3138 ns/op
BenchmarkAssertFloat64-8 1000000000 0.3145 ns/op
BenchmarkAssertMap-8 1000000000 0.3138 ns/op
BenchmarkAssertMapPtr-8 1000000000 0.3154 ns/op
BenchmarkAssertStruct-8 1000000000 0.3151 ns/op
BenchmarkAssertStructPtr-8 1000000000 0.3154 ns/op
PASS
ok blurty/test/benchtest 2.799s
结论
| 类型 | 耗时 | 备注 |
|---|---|---|
| int | 0.3ns/op | 基础类型还是很快的 |
| float | 0.3ns/op | 基础类型还是很快的 |
| map[string]interface{} | 0.3ns/op | 符合类型map断言也很快 |
| *map[string]interface{} | 0.3ns/op | 符合类型map的指针断言也很快 |
| struct | 0.3ns/op | 结构体类型断言也很快 |
| struct pointer | 0.3ns/op | 结构体的指针类型断言也很快 |
无论是什么类型的数据,使用断言来做转换,都很快,每次操作只需要0.3ns
reflect.TypeOf
reflect.TypeOf可以说是最常见的一个方法了
// 源码
func TypeOf(srcData interface{}) reflect.Type {
return reflect.TypeOf(srcData)
}
// 测试用例
func BenchmarkTypeOf(b *testing.B) {
for n := 0; n < b.N; n++ {
TypeOf(1)
}
}
// go test -bench .
goos: darwin
goarch: amd64
pkg: blurty/test/benchtest
cpu: VirtualApple @ 2.50GHz
BenchmarkTypeOf-8 1000000000 0.3135 ns/op
PASS
ok blurty/test/benchtest 1.067s
结论
| 方法 | 耗时 | 备注 |
|---|---|---|
| TypeOf | 0.3ns/op | 很快 |
仅仅是TypeOf的性能很高,堪比断言。
通过TypeOf源码看看,可以看到基本就是在做了一下指针的类型转换,可以忽略不计的消耗。
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
func toType(t *rtype) Type {
if t == nil {
return nil
}
return t
}
reflect.ValueOf
// 代码
func ValueOf(srcData interface{}) reflect.Value {
return reflect.ValueOf(srcData)
}
// 测试用例
func BenchmarkValueOf(b *testing.B) {
for n := 0; n < b.N; n++ {
ValueOf(1)
}
}
// go test -bench .
goos: darwin
goarch: amd64
pkg: blurty/test/benchtest
cpu: VirtualApple @ 2.50GHz
BenchmarkValueOf-8 445375815 2.703 ns/op
PASS
ok blurty/test/benchtest 2.210s
结论
ValueOf每次操作需要2.7ns,性能算式比较慢了
reflect.TypeOf.Kind
一般我们获取interface的类型,光是type和value还是不够的,还是需要从中解析出类型值来,这就需要用到Kind了。通过Kind的源码可以看到,是做了一个位运算的。
// Type Kind源码
func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }
// 代码
func TypeKindOf(srcData interface{}) reflect.Kind {
return reflect.TypeOf(srcData).Kind()
}
func ValueKindOf(srcData interface{}) reflect.Kind {
return reflect.ValueOf(srcData).Kind()
}
func ValueKindOf2(val reflect.Value) reflect.Kind {
return val.Kind()
}
func TypeKindOf2(typ reflect.Type) reflect.Kind {
return typ.Kind()
}
// 测试用例
func BenchmarkTypeKindOf(b *testing.B) {
for n := 0; n < b.N; n++ {
TypeKindOf(1)
}
}
func BenchmarkValueKindOf(b *testing.B) {
for n := 0; n < b.N; n++ {
ValueKindOf(1)
}
}
var val2 = reflect.ValueOf(1)
var typ2 = reflect.TypeOf(1)
func BenchmarkValueKindOf2(b *testing.B) {
for n := 0; n < b.N; n++ {
ValueKindOf2(val2)
}
}
func BenchmarkTypeKindOf2(b *testing.B) {
for n := 0; n < b.N; n++ {
TypeKindOf2(typ2)
}
}
// go test -bench .
goos: darwin
goarch: amd64
pkg: blurty/test/benchtest
cpu: VirtualApple @ 2.50GHz
BenchmarkTypeKindOf-8 344061193 3.503 ns/op
BenchmarkValueKindOf-8 450969230 2.664 ns/op
BenchmarkValueKindOf2-8 1000000000 0.3132 ns/op
BenchmarkTypeKindOf2-8 574637876 2.086 ns/op
PASS
ok blurty/test/benchtest 5.333s
结论
这个耗时就比较厉害了,比仅做类型转换的断言和TypeOf慢了一个数量级。
Type的Kind每次操作要3.5ns,刨去Type需要2ns
Value的Kind每次操作要2.7ns,刨去Value需要0.3ns
所以TypeOf要比ValueOf更快,ValueOf.Kind要比TypeOf.Kind更快。
疑问
TypeOf需要0.3ns,Type.Kind需要2ns,加起来是2.3ns。为什么TypeOf().Kind()组合起来需要3.5ns,多了1.2ns之多。
本站总访问量次
本站访客数人次
本文总阅读量次