【原】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   studyphp  阅读(712)  评论(0编辑  收藏  举报

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术

导航

< 2025年3月 >
23 24 25 26 27 28 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 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示