GO 反射对性能的影响分析

 更新时间:2023年01月06日 14:31:39   作者:nil  
这篇文章主要为大家介绍了GO 反射对性能的影响分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

写在前面

今天在公司写了一段代码,判断一个变量是否为空值,由于判断的类型太少,code review的时候同事说还有很多类型没有考虑到,并且提到有没有开源的包做这个事,于是找了一段assert.IsEmpty里面的代码。但是这段代码用到了反射。在code review的时候同事又提到了反射影响性能。

基于此,这里对比了一下两种方式实习IsEmpty的性能问题。废话不多说,上代码。

代码

最开始的代码

func IsEmpty(val interface{}) bool {
	if val == nil {
		return true
	}
	switch v := val.(type) {
	case int:
		return v == int(0)
	case int8:
		return v == int8(0)
	case int16:
		return v == int16(0)
	case int32:
		return v == int32(0)
	case int64:
		return v == int64(0)
	case string:
		return v == ""
	default:
		return false
	}
}

由于目前场景里面只需要判断这几种数据类型,因此只实现了几种。这种做法明细很不好,将来如果有别人用怎么办?这是一种偷懒的做法,不是一个高级程序员应该有的素质。

在同事提出可能会有其他数据类型的时候,想着类型太多,穷举容易漏,于是在网上找了一下开源的包,遗憾没有找到。但是想到了assert.Empty函数,于是看了一下源代码,找到了它的实现方法。

// isEmpty gets whether the specified object is considered empty or not.
func isEmpty(object interface{}) bool {
	// get nil case out of the way
	if object == nil {
		return true
	}
	objValue := reflect.ValueOf(object)
	switch objValue.Kind() {
	// collection types are empty when they have no element
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
		return objValue.Len() == 0
		// pointers are empty if nil or if the value they point to is empty
	case reflect.Ptr:
		if objValue.IsNil() {
			return true
		}
		deref := objValue.Elem().Interface()
		return isEmpty(deref)
		// for all other types, compare against the zero value
	default:
		zero := reflect.Zero(objValue.Type())
		return reflect.DeepEqual(object, zero.Interface())
	}
}

于是把这段代码复制过来,code review的时候同事说反射会影响性能(其实我觉得还好,不知道大家觉得它对性能影响有多大,欢迎留言讨论),于是我又改了一版。结合了上面两种方法。先判断是不是基础数据类型,如果不是再用反射。

const (
	IntZero     = int(0)
	Int8Zero    = int8(0)
	Int16Zero   = int16(0)
	Int32Zero   = int32(0)
	Int64Zero   = int64(0)
	UintZero    = uint(0)
	Uint8Zero   = uint8(0)
	Uint16Zero  = uint16(0)
	Uint32Zero  = uint32(0)
	Uint64Zero  = uint64(0)
	Float32Zero = float32(0)
	Float64Zero = float64(0)
	StringZero  = ""
)
func IsEmpty(data interface{}) bool {
	if data == nil {
		return false
	}
	switch v := data.(type) {
	case bool:
		return false
	case *bool:
		return v == nil
	case int:
		return v == IntZero
	case *int:
		return v == nil || *v == IntZero
	case int8:
		return v == Int8Zero
	case *int8:
		return v == nil || *v == Int8Zero
	case int16:
		return v == Int16Zero
	case *int16:
		return v == nil || *v == Int16Zero
	case int32:
		return v == Int32Zero
	case *int32:
		return v == nil || *v == Int32Zero
	case int64:
		return v == Int64Zero
	case *int64:
		return v == nil || *v == Int64Zero
	case uint:
		return v == UintZero
	case *uint:
		return v == nil || *v == UintZero
	case uint8:
		return v == Uint8Zero
	case *uint8:
		return v == nil || *v == Uint8Zero
	case uint16:
		return v == Uint16Zero
	case *uint16:
		return v == nil || *v == Uint16Zero
	case uint32:
		return v == Uint32Zero
	case *uint32:
		return v == nil || *v == Uint32Zero
	case uint64:
		return v == Uint64Zero
	case *uint64:
		return v == nil || *v == Uint64Zero
	case float32:
		return v == Float32Zero
	case *float32:
		return v == nil || *v == Float32Zero
	case float64:
		return v == Float64Zero
	case *float64:
		return v == nil || *v == Float64Zero
	case string:
		return v == StringZero
	case *string:
		return v == nil || *v == StringZero
	default:
		kind := reflect.TypeOf(data).Kind()
		if kind == reflect.Ptr {
			dataKind := reflect.ValueOf(data).Elem().Kind()
			if dataKind == reflect.Invalid || dataKind == reflect.Struct {
				return reflect.ValueOf(data).IsNil()
			} else {
				return false
			}
		} else if kind == reflect.Slice || kind == reflect.Map {
			// slice
			return reflect.ValueOf(data).Len() == 0
		} else if kind == reflect.Struct {
			// struct
			return false
		} else {
			panic("not support type. you can support by yourself")
		}
	}
}

得到第三版。

性能分析

代码提交之后我一直在想第一版和第二版性能到底差别有多大。其实这个时候我偷懒了,没有做性能分析,做个Bench分析一下很简单,这件事一直在我心里,过了一天终于写了一段代码比对一下性能。

func BenchmarkTestIsEmpty(t *testing.B) {
	v1 := false
	v2 := true
	var v3 *bool
	v4 := int(0)
	var v5 *int
	v6 := int64(0)
	var v7 *int64
	v8 := ""
	var v9 *string
	v10 := "test"
	v11 := float64(0.00)
	v12 := float64(0.01)
	testCases := []TestCase{
		{input: v1, want: false},
		{input: &v1, want: false},
		{input: v2, want: false},
		{input: &v2, want: false},
		{input: v3, want: true},
		{input: v4, want: true},
		{input: &v4, want: true},
		{input: v5, want: true},
		{input: int(1), want: false},
		{input: v6, want: true},
		{input: &v6, want: true},
		{input: v7, want: true},
		{input: int64(1), want: false},
		{input: v8, want: true},
		{input: &v8, want: true},
		{input: v9, want: true},
		{input: v10, want: false},
		{input: &v10, want: false},
		{input: v11, want: true},
		{input: &v11, want: true},
		{input: v12, want: false},
		{input: &v12, want: false},
	}
	for i, testCase := range testCases {
		result := IsEmpty(testCases[i].input)
		assert.Equal(t, testCase.want, result)
	}
}
func BenchmarkTestIsEmptyV1(t *testing.B) {
	v1 := false
	v2 := true
	var v3 *bool
	v4 := int(0)
	var v5 *int
	v6 := int64(0)
	var v7 *int64
	v8 := ""
	var v9 *string
	v10 := "test"
	v11 := float64(0.00)
	v12 := float64(0.01)
	testCases := []TestCase{
		{input: v1, want: true},
		{input: &v1, want: true},
		{input: v2, want: false},
		{input: &v2, want: false},
		{input: v3, want: true},
		{input: v4, want: true},
		{input: &v4, want: true},
		{input: v5, want: true},
		{input: int(1), want: false},
		{input: v6, want: true},
		{input: &v6, want: true},
		{input: v7, want: true},
		{input: int64(1), want: false},
		{input: v8, want: true},
		{input: &v8, want: true},
		{input: v9, want: true},
		{input: v10, want: false},
		{input: &v10, want: false},
		{input: v11, want: true},
		{input: &v11, want: true},
		{input: v12, want: false},
		{input: &v12, want: false},
	}
	for i, testCase := range testCases {
		result := IsEmptyV1(testCases[i].input)
		assert.Equal(t, testCase.want, result)
	}
}

运行

go test -bench=.  -benchmem

结果

goos: darwin
goarch: amd64
pkg: common/util
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkTestIsEmpty-12           120820              9635 ns/op               0 B/op          0 allocs/op
BenchmarkTestIsEmptyV1-12         103664             11582 ns/op              72 B/op          8 allocs/op
PASS
ok      common/util     5.149s

  • ns:每次运行耗费的世界。时间复杂的相差10倍
  • B:每次运行分配的字节数。可见非反射版不需要额外的内存
  • allocs:每次运行分配内存次数。

综上可见,性能差别挺大的。如果只看运行时间,相差了10倍。确实反射影响性能,以后还是少用为好,最好不要在循环里面用。

最终解决办法

最终结合第一版和第二版,写出了第三版。

写在后面

要善于利用工具,不会的就去学习,不能偷懒。

今天看到「字节跳动技术团队」一个直播,几位掘金小册大佬在分享自己是如何写文章、如何学习的,感受挺深。

写文章要:

  • 1.列提纲
  • 2.利用碎片化的时间积累、记录
  • 3.利用周末大片的时间思考文章
  • 4.文章写完自己对知识的认知又提升了一个层次,利人利己

写文章不要:

  • 1.太在意工具。有的人写文章之前在想用什么打草稿、列提纲、写思维导图,在你想这个的时候内心其实已经在打退堂鼓了。你应该直接打开一个文本编辑器或者控制台等任何能写文字的地方,甚至微信都行
  • 2.不要在意有多少人会阅读你的文章。写完了你自己也会有新的认识
  • 3.定时清理收藏夹。不要只放到收藏夹里,里面的东西要定时整理、清理

以上就是GO 反射对性能的影响分析的详细内容,更多关于GO 反射性能分析的资料请关注脚本之家其它相关文章!

相关文章

  • go语言在请求http时加入自定义http header的方法

    go语言在请求http时加入自定义http header的方法

    这篇文章主要介绍了go语言在请求http时加入自定义http header的方法,实例分析了Go语言http请求的原理与操作技巧,需要的朋友可以参考下
    2015-03-03
  • golang 实现对Map进行键值自定义排序

    golang 实现对Map进行键值自定义排序

    这篇文章主要介绍了golang 实现对Map进行键值自定义排序,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Go语音开发中常见Error类型处理示例详解

    Go语音开发中常见Error类型处理示例详解

    这篇文章主要为大家介绍了Go语音开发中常见Error类型处理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 基于Golang实现YOLO目标检测算法

    基于Golang实现YOLO目标检测算法

    目标检测是计算机视觉领域的重要任务,它不仅可以识别图像中的物体,还可以标记出物体的位置和边界框,YOLO是一种先进的目标检测算法,以其高精度和实时性而闻名,本文将介绍如何使用Golang实现YOLO目标检测算法,文中有相关的代码示例供大家参考,需要的朋友可以参考下
    2023-11-11
  • golang 使用 viper 读取自定义配置文件

    golang 使用 viper 读取自定义配置文件

    这篇文章主要介绍了golang 使用 viper 读取自定义配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • go本地环境配置及vscode go插件安装的详细教程

    go本地环境配置及vscode go插件安装的详细教程

    这篇文章主要介绍了go本地环境配置及vscode go插件安装的详细教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • Go语言单链表实现方法

    Go语言单链表实现方法

    这篇文章主要介绍了Go语言单链表实现方法,实例分析了基于Go语言的单链表实现原理与使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • GoLang读取文件的10种方法实例

    GoLang读取文件的10种方法实例

    读取文件是所有编程语言中最常见的操作之一,下面这篇文章主要给大家介绍了关于GoLang读取文件的10种方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • 使用Go HTTP客户端打造高性能服务

    使用Go HTTP客户端打造高性能服务

    大多数语言都有提供各自的 HTTP 客户端,本文将动手实践如何使用Go语言发起HTTP请求,并讨论其中有可能遇到的问题。具有一定的参考价值,感兴趣的可以了解一下
    2021-12-12
  • Go语言实现类似c++中的多态功能实例

    Go语言实现类似c++中的多态功能实例

    Go本身不具有多态的特性,不能够像Java、C++那样编写多态类、多态方法。但是,使用Go可以编写具有多态功能的类绑定的方法。下面来一起看看吧
    2016-09-09

最新评论