Go常用数据结构的高性能实战—— string 篇(一)

131 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第34天,点击查看活动详情

前言

在我们进行 Go 开发的过程中,通常会用到一些数据结构来实现我们的需求,比较常用的有 string、slice (切片)、map(字典)等。对于它们的实现,我们是否又清楚呢?而对于它们的使用,我们又如何发挥出它们的高性能呢?接下来我们通过这篇文章来一起学习探讨~

string 字符串

结构

string 其实就是一个不可变的 byte 数组,其结构为

image.png

  • 一个 string 在内存中占用16字节
  • 0~8 字节的 str 指向实际存储字符串的地址,也就是字节数组的头部
  • 8~16 字节的 len 代表字符串长度

字符串拼接效率

Go 的 string 直接相加的操作效率较低

str3 = str1 + str2

每一次拼接都需要重新分配内存,首先分配一个 str3 所需要的内存,然后将 str1 与 str2 进行内存拷贝。如果构建个超大的字符串,那么这种性能是非常低的。比如这个for循环拼接:

var str string
for i := 0; i < 1000; i++ {
   str += "zhongger" 
}

每执行一次加号拼接,就会重新分配内存和进行内存拷贝。我们可以使用 Benchmark 来测试下性能

func StringAdd() string {
   var str string
   for i := 0; i < 1000; i++ {
      str += "zhongger"
   }
   return str
}


func BenchmarkTestString(b *testing.B) {
   for i := 0; i < b.N; i++ {
      StringAdd()
   }
}

使用命令 go test -bench=. -benchmem 执行之,输出以下结果

image.png

由 999 allocs/op 可以知道,StringAdd 方式的字符串拼接,进行了 999 次的内存分配申请。那么我们有没有办法进行优化呢?当然有,接下来介绍下 strings.Join 方法

strings.Join 方法字符串拼接

func StringJoin() string {
   list := make([]string, 1000)
   for i := 0; i < 1000; i++ {
      list[i] = "zhongger"
   }
   return strings.Join(list, "")
}


func BenchmarkTestJoin(b *testing.B) {
   for i := 0; i < b.N; i++ {
      StringJoin()
   }
}


使用命令 go test -bench=. -benchmem 执行之,输出以下结果

image.png

由 1 allocs/op 可以知道,使用 strings.Join 方法进行字符串拼接,只进行了 1 次内存分配申请,性能得到了非常大的提高。

小结

今天主要学习了 string 的高性能字符串拼接方法 strings.Join ,明天会有更多的字符串拼接性能优化的原理分析,大家敬请期待