欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

golang type assertion and unsafe.Pointer 性能对比

程序员文章站 2022-06-22 21:05:50
...

golang type assertion and unsafe.Pointer 性能对比

最近项目中有这样一个需求背景:有一个存储实体用来做各种指标的counter。这个counter的实现需要能够在以后被别的实现替换。结构自然是这样:

type XXXObj struct {
	startTime uint64
	counter interface{}//暂时用interface{}表示,表示该对象类似于泛型的存在
}

为了避免对象拷贝,所以其实这里存储的一定会是一个指针。所以这里还有一个可选方案:unsafe.Pointer.

最初的设计,这一块更像泛型的一种思想,所以很自然的使用了interface{}。

但是上层拿到这个 interface{} 之后肯定要做类型断言,转成实际的counter对象指针,然后执行实际的统计操作。这一块是一个非常高频的调用。所以performance是非常重要的点。

所以想测试一下:直接调用、类型断言,unsafe.Pointer转换三种方式的性能对比。下面是一个test 的benchmark。这里申明一下这里仅仅针对泛型对象是指针的场景:

package assertion

import (
	"testing"
	"unsafe"
)

type Student struct {
	Name  string
	Age   int
	Class string
	Score int
}


func DirectInvoke(s *Student) {
	s.Name = "Jerry"
	s.Age = 18
	s.Class = "20005"
	s.Score = 100
}

func PointerInvoke(p unsafe.Pointer)  {
	s := (*Student)(p)
	s.Name = "Jerry"
	s.Age = 18
	s.Class = "20005"
	s.Score = 100
}
func InterfaceInvoke(i interface{}) {
	s := i.(*Student)
	s.Name = "Jerry"
	s.Age = 18
	s.Class = "20005"
	s.Score = 100
}

func BenchmarkAssertDirectInvoke(b *testing.B) {
	s := new(Student)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		DirectInvoke(s)
	}
	_ = s
}

func BenchmarkAssertPointerInvoke(b *testing.B) {
	s := new(Student)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		PointerInvoke(unsafe.Pointer(s))
	}
	_ = s
}

func BenchmarkAssertInterfaceInvoke(b *testing.B) {
	s := new(Student)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		InterfaceInvoke(s)
	}
	_ = s
}

三种场景跑出来的结果是:

// 环境:mac pro 2015, 13-in, 4核8G内存

$go test -bench='BenchmarkAssert*' -benchmem
goos: darwin
goarch: amd64
pkg: study_golang/study/basic/assertion
BenchmarkAssertDirectInvoke-4           1000000000               0.328 ns/op           0 B/op          0 allocs/op
BenchmarkAssertPointerInvoke-4          1000000000               0.332 ns/op           0 B/op          0 allocs/op
BenchmarkAssertInterfaceInvoke-4        559252803                2.03 ns/op            0 B/op          0 allocs/op
PASS
ok      study_golang/study/basic/assertion      2.095s

从benchmark结果来看,直接调用和使用指针的性能基本是一样的。但是使用interface{}再做 type assertion 的方式就性能相比就差很多了。但是实际指令执行的速度都是在ns 级别,所以速度似乎又都能接受。

我们应该关注的应该是,为什么 interface{} 的方式和type assertion速度会差这么多?根据我的了解,这里应该与 interface{}的拆箱,装箱,以及类型断言的实际原理有关。

相关标签: Golang