golang-unsafe包的用处

我们都知道byte切片转字符串用 string(byte[]{'a'}) 就可以办到,但这种方式会在内存中声明一块新的内存来存储字符串,原来的切片在没用被引用的情况下可能会在下一次GC被回收。这里我们可以用unsafe包来达到不申请新内存用原来切片的底层数据来完成切片到字符串的转换。(在go中字符串的底层就是是byte切片,而切片的底层是数组)
  • byte切片或者数组转字符串

下面看代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"unsafe"
)

func main() {

b := []byte{'a','b','c'} // 声明一个切片
fmt.Printf("%p\n",&b) // 打印地址
s := string(b) // 显示转换为字符串
fmt.Printf("方式1: %p=>%s\n",&s,s) // 打印新字符串的地址和数据

s2 := (*string)(unsafe.Pointer(&b)) // 用unsafe包通过指针转换的形式转为字符串
fmt.Printf("方式2: %p=>%s\n",s2,*s2) // 打印新字符串的地址和数据

}
  • 上面代码的执行结果如下:
可以看到用unsafe包的方式转换的变量地址和原始切片的地址是一样的,unsafe包达到了byte切片转 字符串的目的,而且避免了产生新的内存申请。这在需要大量处理byte切片转为字符串的应用场景中可以有很好的作用。比如消息队列,大量减少小内存的申请减少GC的回收压力,对高并发的应用有一定程度的优化作用。 并且两种转换方式的效率也不一样,通过指针的方式更高效,在对性能有着极致要求的时候还是有用,看下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"time"
)

type B struct {
b string
}

func main() {
println(time.Now().AddDate(100, 0,0 ).UnixNano() / 1e6)

f := []byte{'a'}
now := time.Now().UnixNano() // 纳秒
b := B{}
i := 0
for {
if i > 100000000 {
break
}
//b.b = *(*string)(unsafe.Pointer(&f)) // 耗时: 259306400纳秒
b.b = string(f) // 耗时:2588078400纳秒
i++
}
fmt.Println(time.Now().UnixNano()-now)

}

作者

itpika

发布于

2020-02-23 10:56:40

更新于

2021-01-28 16:44:10

许可协议

评论