日常开发中字符串 string 和 []byte 类型相关之间的类型间转换是司空见惯的事情,我们常常会使用如下方式进行转行,简单高效:
// string 转 []bytehelloBytes := []byte("hello the golang string convert")// []byte 转 stringhelloStr := string(helloBytes)
go 中的 string 属于 immutable 类型,类型转换会产生新的内存分配,如果我们知道转换的数据不会被修改,那么我们可以尝试直接修改底层数据对应指针指向新的数据类型即可:
func ConvertStr(b []byte) string {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
sh := reflect.StringHeader{
Data: sliceHeader.Data, // 使用slice指针地址
Len: sliceHeader.Len,
}
return *(*string)(unsafe.Pointer(&sh))
}
同样我们使用 benchmark 两种情况,编写如下测试代码:
func ConvertStr(b []byte) string {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
sh := reflect.StringHeader{
Data: sliceHeader.Data, // 使用slice指针地址
Len: sliceHeader.Len,
}
return *(*string)(unsafe.Pointer(&sh))
}
func BenchmarkConvert(b *testing.B) {
tmpBytes := []byte("hello the golang string convert")
for i := 0; i < b.N; i++ {
_ = string(tmpBytes)
}
}
func BenchmarkConvertPointer(b *testing.B) {
tmpBytes := []byte("hello the golang string convert")
for i := 0; i < b.N; i++ {
_ = ConvertStr(tmpBytes)
}
}
测试结果也非常明显,尤其是数据量越大越明显:
go test -bench=Convert -benchmem
BenchmarkConvert-4 38298946 31.0 ns/op 80 B/op 1 allocs/op
BenchmarkConvertPointer-4 1000000000 0.570 ns/op 0 B/op 0 allocs/op
