Go语言中[]byte和string类型相互转换时的性能分析和优化

栏目: Go · 发布时间: 6年前

内容简介:我们在使用Go语言时,经常涉及到[]byte和string两种类型间的转换。本篇文章将讨论转换时的开销,Go编译器在一些特定场景下对转换做的优化,以及在高性能场景下,我们自己如何做相应的优化。[]byte其实就是byte类型的切片,对应的底层结构体定义如下(在string对应的底层结构体定义如下(在

我们在使用 Go 语言时,经常涉及到[]byte和string两种类型间的转换。本篇文章将讨论转换时的开销,Go编译器在一些特定场景下对转换做的优化,以及在高性能场景下,我们自己如何做相应的优化。

[]byte其实就是byte类型的切片,对应的底层结构体定义如下(在 runtime/slice.go 文件中)

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

string对应的底层结构体定义如下(在 runtime/string.go 文件中)

type stringStruct struct {
	str unsafe.Pointer
	len int
}

可以看到它们内部都有一个指针类型(array或str),指向真实数据。另外还有一个len字段,标识数据的长度。

slice多了一个cap字段,表示容量大小。当要往slice尾部追加数据而空余容量又不够时,会重新分配更大的内存块,将当前内存块的内容拷贝至新内存块,再在新内存块做追加。

slice变量间做赋值操作时,只是修改指针指向,不会拷贝真实数据。string变量间赋值也是同样的道理。

但是[]byte和string相互转换,就需要重新申请内存并拷贝内存了。因为Go语义中,slice的内容是可变的( mutable ),而string是不可变的( immutable )。如果他们底部指向同一块数据,那么由于slice可对数据做修改,string就做不到 immutable 了。

[]byte和string互转时的底层调用分别对应 runtime/string.gostringtoslicebyteslicebytetostring 两个函数。

那么如果我们想省去申请和拷贝内存的开销呢?

来看 runtime/string.goslicebytetostringtmpstringtoslicebytetmp 两个函数,如下:

func slicebytetostringtmp(b []byte) string {
	// Return a "string" referring to the actual []byte bytes.
	// This is only for use by internal compiler optimizations
	// that know that the string form will be discarded before
	// the calling goroutine could possibly modify the original
	// slice or synchronize with another goroutine.
	// First such case is a m[string(k)] lookup where
	// m is a string-keyed map and k is a []byte.
	// Second such case is "<"+string(b)+">" concatenation where b is []byte.
	// Third such case is string(b)=="foo" comparison where b is []byte.

	if raceenabled && len(b) > 0 {
		racereadrangepc(unsafe.Pointer(&b[0]),
			uintptr(len(b)),
			getcallerpc(unsafe.Pointer(&b)),
			funcPC(slicebytetostringtmp))
	}
	return *(*string)(unsafe.Pointer(&b))
}

func stringtoslicebytetmp(s string) []byte {
	// Return a slice referring to the actual string bytes.
	// This is only for use by internal compiler optimizations
	// that know that the slice won't be mutated.
	// The only such case today is:
	// for i, c := range []byte(str)

	str := (*stringStruct)(unsafe.Pointer(&s))
	ret := slice{array: unsafe.Pointer(str.str), len: str.len, cap: str.len}
	return *(*[]byte)(unsafe.Pointer(&ret))
}

通过 unsafe.Pointer 直接做指针类型的转换。

注释中也说得很清楚了。

stringtoslicebytetmp 调用的前提是保证返回的[]byte之后不会被修改,只用于编译器内部优化,目前唯一的场景是在for loop中将string转换成[]byte做遍历操作时,比如 for i, c := range []byte(str)

slicebytetostringtmp 调用的前提其实也是类似,保证返回的string在生命周期结束之前,[]byte不会被修改,也是只用于编译器内部优化,目前有三种场景:

m[string(k)]
<"+string(b)+">
string(b)=="foo"

由于以上两个函数是不暴露给Go用户的,所以如果我们在一些高性能场景想要做类似优化时,可以通过 unsafe.Pointer 自己做类似实现,当然,前提是保证数据是immutable的。

参考链接:


以上所述就是小编给大家介绍的《Go语言中[]byte和string类型相互转换时的性能分析和优化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算统计

计算统计

Geof H.Givens、Jennifer A.Hoeting / 王兆军、刘民千、邹长亮、杨建峰 / 人民邮电出版社 / 2009-09-01 / 59.00元

随着计算机的快速发展, 数理统计中许多涉及大计算量的有效方法也得到了广泛应用与迅猛发展, 可以说, 计算统计已是统计中一个很重要的研究方向. 本书既包含一些经典的统计计算方法, 如求解非线性方程组的牛顿方法、传统的随机模拟方法等, 又全面地介绍了近些年来发展起来的某些新方法, 如模拟退火算法、基因算法、EM算法、MCMC方法、Bootstrap方法等, 并通过某些实例, 对这些方法的应用进行......一起来看看 《计算统计》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具