Go性能优化技巧 1/10

雨痕学堂

字符串(string)作为一种不可变类型,在与字节数组(slice, [ ]byte)转换时需付出 “沉重” 代价,根本原因是对底层字节数组的复制。这种代价会在以万为单位的高并发压力下迅速放大,所以对它的优化常变成 “必须” 行为。

首先,须了解 string 和 [ ]byte 数据结构,并确认默认方式的复制行为。

package main
 
import (
    "fmt"
)
 
func main() {
    s := "hello, world!"
    b := []byte(s)
    fmt.Println(s, b)
}

图片描述

动态演示: https://asciinema.org/a/6up6gvgqo0v9zkjpusvyucg8g

从 GDB 输出结果可看出,转换后 [ ]byte 底层数组与原 string 内部指针并不相同,以此可确定数据被复制。那么,如不修改数据,仅转换类型,是否可避开复制,从而提升性能?

从 ptype 输出的结构来看,string 可看做 [2]uintptr,而 [ ]byte 则是 [3]uintptr,这便于我们编写代码,无需额外定义结构类型。如此,str2bytes 只需构建 [3]uintptr{ptr, len, len},而 bytes2str 更简单,直接转换指针类型,忽略掉 cap 即可。

package main
 
import (
    "fmt"
    "strings"
    "unsafe"
)
 
func str2bytes(s string) []byte {
    x := (*[2]uintptr)(unsafe.Pointer(&s))
    h := [3]uintptr{x[0], x[1], x[1]}
    return *(*[]byte)(unsafe.Pointer(&h))
}
 
func bytes2str(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}
 
func main() {
    s := strings.Repeat("abc", 3)
    b := str2bytes(s)
    s2 := bytes2str(b)
    fmt.Println(b, s2)
}

用 unsafe 完成指针类型转换,所以得自行为底层数组生命周期做出保证。好在这两个函数都很简单,编译器会完成内联处理,并未发生逃逸行为。

图片描述

对比一下优化前后的性能差异。

package main
 
import (
    "strings"
    "testing"
)
 
var s = strings.Repeat("a", 1024)
 
func test() {
    b := []byte(s)
    _ = string(b)
}
 
func test2() {
    b := str2bytes(s)
    _ = bytes2str(b)
}
 
func BenchmarkTest(b *testing.B) {
    for i := 0; i < b.N; i++ {
        test()
    }
}
 
func BenchmarkTestBlock(b *testing.B) {
    for i := 0; i < b.N; i++ {
        test2()
    }
}

图片描述

性能提升明显,最关键的是 zero-garbage。


最新动态,请扫码关注
图片描述

阅读 15k

雨痕学堂
技术分享,关于Go、Python等。
279 声望
0 粉丝
0 条评论
推荐阅读
Go: Readonly Variable
只读变量的缺失,应该算 Go 语言 “设计缺陷”。举例来说,默认以 error 实例来判断错误类别,但这些可导出全局变量实际可被外部修改,那么就存在隐性风险。

雨痕学堂3阅读 6.9k

前端如何入门 Go 语言
类比法是一种学习方法,它是通过将新知识与已知知识进行比较,从而加深对新知识的理解。在学习 Go 语言的过程中,我发现,通过类比已有的前端知识,可以更好地理解 Go 语言的特性。

robin15阅读 1.6k评论 2

封面图
Go语言三十讲【目录】
第一章 Go语言快速入门&emsp;&emsp;第一篇 基本语法&emsp;&emsp;第二讲 数组与切片&emsp;&emsp;第三讲 字符串&emsp;&emsp;第四讲 哈希表MAP&emsp;&emsp;第五讲 结构体与接口&emsp;&emsp;第六讲 反射&emsp;&emsp;...

西红柿9阅读 2.3k评论 4

【已结束】SegmentFault 思否技术征文丨浅谈 Go 语言框架
亲爱的开发者们:我们的 11 月技术征文如期而来,这次主题围绕 「 Go 」 语言,欢迎大家来参与分享~征文时间11 月 4 日 - 11 月 27 日 23:5911 月 28 日 18:00 前发布中奖名单参与条件新老思否作者均可参加征文...

SegmentFault思否10阅读 4.1k评论 11

封面图
【Go微服务】开发gRPC总共分三步
之前我也有写过RPC相关的文章:《 Go RPC入门指南:RPC的使用边界在哪里?如何实现跨语言调用?》,详细介绍了RPC是什么,使用边界在哪里?并且用Go和php举例,实现了跨语言调用。不了解RPC的同学建议先读这篇文...

王中阳Go6阅读 2.5k

封面图
Golang 轮子之 Supervisor
Supervisor 是一个强大的 进程管理工具。在非容器化管理的服务器上, Supervisor 是有非常广泛的使用场景的。例如:服务批量重启,多服务按顺序启动,服务oom后自动拉起,服务std日志收集等,甚至服务健康检查它...

小白要生发4阅读 896

封面图
终于实现了一门属于自己的编程语言
都说程序员的三大浪漫是:操作系统、编译原理、图形学;最后的图形学确实是特定的专业领域,我们几乎接触不到,所以对我来说换成网络更合适一些,最后再加上一个数据库。

crossoverJie3阅读 1.6k

封面图
279 声望
281 粉丝
宣传栏