【原】golang 中字符串拼接的方式性能对比

背景

    开发过程中,常常会用到字符串拼接完成某种需求,我们能马上想到的解决办法有+,fmt.Sprintf,高级一点可能还会想到strings包的Join 方法,甚至想到bytes.buffer,再用writeString 方法完成,再而想到strings.builder。但究竟哪种效率高呢?我们在使用过程中如何选择?只有实测才知道

结论

    测试结论 bytes.Buffer > + > strings.Join > strings.builder > fmt.Sprintf 。

    简单拼接用+,复杂,追求高效用bytes.Buffer

实测

    以下测试在个人mac 系统下。go 版本1.15,不同版本可能略有不同,以自己实际输出为准。

测试1

 

package strings

import (
"bytes"
"fmt"
"strings"
"testing"
)

func BenchmarkStringBuild(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result strings.Builder
result.WriteString(hello)
result.WriteString(world)
//result.WriteString("aaa")
//result.WriteString(strconv.Itoa(10000))
}
}

func BenchmarkStringJia(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
//_ = hello + world + "aaa !" + strconv.Itoa(10000)
_ = hello + world
}
}

func BenchmarkStringSprintf(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
//_ = fmt.Sprintf("%s,%s,%s,%d", hello, world, "aaa !", 1000)
_ = fmt.Sprintf("%s,%s", hello, world)
}
}

func BenchmarkAddStringWithJoin(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
//_ = strings.Join([]string{hello, world, "aaa !", strconv.Itoa(10000)}, ",")
_ = strings.Join([]string{hello, world}, ",")
}
}

func BenchmarkAddStringWithBuffer(b *testing.B) {
hello := "hello"
world := "world"
for i := 0; i < 1000; i++ {
var buffer bytes.Buffer
buffer.WriteString(hello)
buffer.WriteString(",")
buffer.WriteString(world)
//buffer.WriteString("aaa !")
//buffer.WriteString(strconv.Itoa(10000))
_ = buffer.String()
}
}

 

 

测试2

 

 

package strings

import (
"bytes"
"fmt"
"strconv"
"strings"
"testing"
)

func BenchmarkStringBuild(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result strings.Builder
result.WriteString(hello)
result.WriteString(world)
result.WriteString("aaa")
result.WriteString(strconv.Itoa(10000))
}
}

func BenchmarkStringJia(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = hello + world + "aaa !" + strconv.Itoa(10000)
//_ = hello + world
}
}

func BenchmarkStringSprintf(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = fmt.Sprintf("%s,%s,%s,%d", hello, world, "aaa !", 1000)
//_ = fmt.Sprintf("%s,%s", hello, world)
}
}

func BenchmarkAddStringWithJoin(b *testing.B) {
hello := "hello"
world := "world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = strings.Join([]string{hello, world, "aaa !", strconv.Itoa(10000)}, ",")
//_ = strings.Join([]string{hello, world}, ",")
}
}

func BenchmarkAddStringWithBuffer(b *testing.B) {
hello := "hello"
world := "world"
for i := 0; i < 1000; i++ {
var buffer bytes.Buffer
buffer.WriteString(hello)
buffer.WriteString(",")
buffer.WriteString(world)
buffer.WriteString("aaa !")
buffer.WriteString(strconv.Itoa(10000))
_ = buffer.String()
}
}

 

 

执行:$ go test -bench=. -benchmem -run=none

 

结果显而易见,即便我只拼接hello world,不同方式的对比差异也是显著的。

可看到

  • string.Builder内存分配4次,分配字节数64字节
  • + 进行内存分配1次,分配字节数5字节
  • fmt.Sprintf 进行内存分配3次,分配字节数64字节
  • strings.Join 进行内存分配2次,分配字节数37字节
  • bytes.Buffer WriteString进行内存分配0次,分配字节数0

结果一般没想到的是+ 在1.15版本性能是大于 strings.Join 和fmt.Sprintf的。所以在我们使用中优先选择bytes.Buffer WriteString、其次简单的,方便阅读和编写可用+。

posted on 2020-09-17 16:57  studyphp  阅读(696)  评论(0编辑  收藏  举报

导航