golang 字符串拼接性能的对比分析

背景

最近在做一个服务发现/注册的agent, 各个服务需要通过这个agent来注册自己的服务,在完成

开发后,测试性能时发现性能达不到要求,通过pprof 来确认cpu主要耗费在gc上,分析结果主要是由于字符串拼接导致,故需要测试一下字符串拼接的几种方法的性能;

字符串拼接的几种方法

1、直接使用加号进行拼接

2、strings.Join()

3、fmt.Sprintf()

4、bytes.Buffer

大量字符串拼接性能测试

我们使用的场景主要是大量字符串拼接,所以需要的场景是不断在字符串上拼接所以测试函数如下:

// fmt.Printf
func BenchmarkFmtSprintfMore(b *testing.B) {
 var s string
 for i := 0; i < b.N; i++ {
  s += fmt.Sprintf("%s%s", "hello", "world")
 }
 fmt.Errorf(s)
}
// 加号 拼接
func BenchmarkAddMore(b *testing.B) {
 var s string
 for i := 0; i < b.N; i++ {
  s += "hello" + "world"
 }
 fmt.Errorf(s)
}

// strings.Join
func BenchmarkStringsJoinMore(b *testing.B) {

 var s string
 for i := 0; i < b.N; i++ {
  s += strings.Join([]string{"hello", "world"}, "")

 }
 fmt.Errorf(s)
}

// bytes.Buffer
func BenchmarkBufferMore(b *testing.B) {

 buffer := bytes.Buffer{}
 for i := 0; i < b.N; i++ {
  buffer.WriteString("hello")
  buffer.WriteString("world")

 }
 fmt.Errorf(buffer.String())
}

执行测试函数

~/gopath/src/test/string  go test -bench="."
goos: darwin
goarch: amd64
pkg: test/string
BenchmarkFmtSprintfMore-4   300000  118493 ns/op
BenchmarkAddMore-4    300000  124940 ns/op
BenchmarkStringsJoinMore-4  300000  117050 ns/op
BenchmarkBufferMore-4   100000000   37.2 ns/op
PASS
ok  test/string 112.294s

从上可以看出使用bytes.buffer的性能是非常高的,如果涉及到大量数据拼接推荐

bytes.buffer{}

单次字符串拼接性能测试

func BenchmarkFmtSprintf(b *testing.B) {
 for i := 0; i < b.N; i++ {
  s := fmt.Sprintf("%s%s", "hello", "world")
  fmt.Errorf(s)
 }

}

func BenchmarkAdd(b *testing.B) {
 for i := 0; i < b.N; i++ {
  s := "hello" + "world"
  fmt.Errorf(s)
 }
}
func BenchmarkStringsJoin(b *testing.B) {
 for i := 0; i < b.N; i++ {
  s := strings.Join([]string{"hello", "world"}, "")
  fmt.Errorf(s)
 }
}
func BenchmarkBuffer(b *testing.B) {

 for i := 0; i < b.N; i++ {
  b := bytes.Buffer{}
  b.WriteString("hello")
  b.WriteString("world")
  fmt.Errorf(b.String())
 }
}

执行测试函数

 ~/gopath/src/test/string  go test -bench="."
goos: darwin
goarch: amd64
pkg: test/string
BenchmarkFmtSprintf-4  10000000   200 ns/op
BenchmarkAdd-4    20000000   93.6 ns/op
BenchmarkStringsJoin-4  10000000   152 ns/op
BenchmarkBuffer-4   10000000   175 ns/op
PASS
ok  test/string 7.818s

从上可以看出单词调用字符串拼接性能 + > strings.Join > bytes.Buffer > fmt.Sprintf

总结

如果涉及到大量数据拼接推荐 bytes.buffer{}

后记

当然只是通过bytes.buffer{} 性能还是不够的,针对这个问题我们通过添加缓存进一步接口qps.

cpu 耗费在gc 上的原因,需要分析golang gc 回收机制, 这是另外一个topic, 之后总结。

补充:Go语言字符串批量拼接-StringBuilder

1.安装

go get -u github.com/typa01/go-utils

import (
 "github.com/typa01/go-utils"
)

使用,例:

fieldNames := tsgutils.NewInterfaceBuilder()

2.使用

func TestStringBuilderReplace(t *testing.T) {
 builder1 := NewStringBuilder()
 builder1.Append("%111%abc%987%")
 FmtPrintln(builder1.Replace("%", "$").ToString()) // $111$abc$987$
 builder2 := builder1.Clear()
 builder2.AppendStrings("abc","defg").AppendInt(123).AppendFloat64(66.44).AppendStrings("aaa", "bbb", "&")
 FmtPrintln(builder2.RemoveLast().ToString()) // abcdefg1236.644E+01aaabbb
 str1 := NewString("123")
 builder3 := NewStringBuilderString(str1).Append("456")
 FmtPrintln(builder3.ToString()) // 123456
}

3.GitHub源码地址

https://github.com/typa01/go-utils

https://github.com/typa01/go-utils/blob/master/string_builder.go

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • golang 中获取字符串个数的方法

    在 golang 中不能直接用 len 函数来统计字符串长度,查看了下源码发现字符串是以 UTF-8 为格式存储的,说明 len 函数是取得包含 byte 的个数 // string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values

  • golang时间字符串和时间戳转换的案例

    1. 获取当前时间字符串和时间戳 package main import ( "fmt" "time" ) func main() { now := time.Now().UTC() // 显示时间格式: UnixDate = "Mon Jan _2 15:04:05 MST 2006" fmt.Printf("%s\n", now.Format(time.UnixDate)) // 显示时间戳 fmt.Printf(&quo

  • golang 字符串切片去重实例

    实现目的:实现字符串切片去重,只是两个字符完全相同时,去除一个. 实现方式:考虑两种,一种是常规的双重循环去除,另一种是利用map的key的唯一性实现. 1.通过双重循环来过滤重复元素 方法1, 思路:利用for双重循环,新建一个slice,遍历原slice中的每一个元素,每一次判断这个元素和后面元素是否相同,若相同则去除,若不同则存入新slice中,判断本元素后,再继续判断下一个元素,直到判断完毕. package main import "fmt" func main() { va

  • 利用golang的字符串解决leetcode翻转字符串里的单词

    题目 给定一个字符串,逐个翻转字符串中的每个单词. 示例 1: 输入: "the sky is blue" 输出: "blue is sky the" 示例 2: 输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括. 示例 3: 输入: "a good example" 输出: "exampl

  • Golang中的Unicode与字符串示例详解

    背景: 在我们使用Golang进行开发过程中,总是绕不开对字符或字符串的处理,而在Golang语言中,对字符和字符串的处理方式可能和其他语言不太一样,比如Python或Java类的语言,本篇文章分享一些Golang语言下的Unicode和字符串编码. Go语言字符编码 注意: 在Golang语言中的标识符可以包含 " 任何Unicode编码可以标识的字母字符 ". 被转换的整数值应该可以代表一个有效的 Unicode 代码点,否则转换的结果就将会是 "�",即:一个

  • Golang中生成随机字符串并复制到粘贴板的方法

    前段时间在生活中偶尔需要对某些文件进行重命名,而且是随机名字,刚 开始是手动重命名然后在键盘上胡乱打一些字母数字,时间长了发现也挺麻烦的,于是想到能不能用golang实现这个功能并且自动把生成的字符串 复制到粘贴板,然后生成exe文件,要用的是直接鼠标双击就行.说干就干. 网上搜了些相关资料,于是写了出来. 安装必要的库 go get github.com/atotto/clipboard 代码实现 package main import ( "fmt" "github.co

  • golang 字符串拼接性能的对比分析

    背景 最近在做一个服务发现/注册的agent, 各个服务需要通过这个agent来注册自己的服务,在完成 开发后,测试性能时发现性能达不到要求,通过pprof 来确认cpu主要耗费在gc上,分析结果主要是由于字符串拼接导致,故需要测试一下字符串拼接的几种方法的性能: 字符串拼接的几种方法 1.直接使用加号进行拼接 2.strings.Join() 3.fmt.Sprintf() 4.bytes.Buffer 大量字符串拼接性能测试 我们使用的场景主要是大量字符串拼接,所以需要的场景是不断在字符串上

  • java原生序列化和Kryo序列化性能实例对比分析

    简介 最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括: 专门针对Java语言的:Kryo,FST等等 跨语言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等 这些序列化方式的性能多数都显著优于hessian2(甚至包括尚未成熟的dubbo序列化).有鉴于此,我们为dubbo引入Kryo和FST这 两种高效Java序列化实现,来逐步取代hessian2.其中,Kryo是一种非常成熟的序列化实现,已经在Twitter.Group

  • Golong字符串拼接性能优化及原理介绍

    目录 1.字符串高效拼接 1.1 常见的字符串拼接 1.2 字符串拼接测试 1.3 推荐 2.相关原理 2.1 + 号 2.2 strings.Builder 与 bytes.Buffer 2.2.1 内部[]byte 增长方式: 2.2.2 性能比较 1.字符串高效拼接 go 字符串是不可修改的,所谓字符串拼接就是创建新的字符串对象.如果代码中存在大量的字符串拼接,那么性能将会存在影响. 1.1 常见的字符串拼接 +号 func plusConcat(n int, s string) stri

  • Java中字符串拼接的一些细节分析

    工作日忙于项目的逻辑实现,周六有点时间,从书柜里拿出厚厚的英文版Thinking In Java,读到了字符串对象的拼接.参考着这本书做个翻译,加上自己思考的东西,写上这篇文章记录一下. 不可变的String对象 在Java中,String对象是不可变的(Immutable).在代码中,可以创建多个某一个String对象的别名.但是这些别名都是的引用是相同的. 比如s1和s2都是"droidyue.com"对象的别名,别名保存着到真实对象的引用.所以s1 = s2 复制代码 代码如下:

  • C# 利用StringBuilder提升字符串拼接性能的小例子

    用Stopwatch分段监控了一下,发现耗时最多的函数是SaveToExcel 此函数中遍列所有数据行,通过Replace替换标签生成Excel行,然后将行数据累加赋值到一个字符串 复制代码 代码如下: string excelString = ""; foreach(var item in list){         excelString += string.Format("<row>....{0}</row>",list.Title)

  • 关于若干数据库数据插入性能的对比分析

    本地数据库接触不多,最早用过Access,但现在SQLite功能更加强大--而且,说实在的我不喜欢Access,连带着不喜欢SqlServer,只要一看到满眼的@号go号我就头晕不止:更何况有一个我感觉非常致命的问题:分页太麻烦!远不如mySql/SQLite中的limit或者Oracle中的rownum来得痛快. 平时基本使用Oracle,对它的性能知根知底了:mySql近年来混得风生水起,想必有过人之处,也一并纳入测试范围了. 另外,Access现在有2007版,不知道和2003版在性能上有

  • Golang::slice和nil的对比分析

    我就废话不多说了,大家还是直接看代码吧~ package main import ( "fmt" ) func main() { var s1 []int if s1 == nil { fmt.Println("s1==nil") } else { fmt.Println("s1!=nil") } var arr = [5]int{} s1 = arr[:] if s1 == nil { fmt.Println("s1==nil&quo

  • 浅析Golang中字符串拼接问题

    目录 1.概述 2.Golang中字符串拼接的方式 3.总结 1.概述 Go的字符串是一个不可改变的数据结构,这和其他语言如JAVA,C++等的设定很类似.总体来说,有如下五种拼接方式,下面我们将论述各种方式的性能问题,以及如何选择. (golang字符串,内存模型) type StringHeader struct { Data uintptr Len int } 注意:字符串具有不可改变的特性,即便通过指针等变相操作 var a string = "old" bptr := (*r

  • javascript中字符串拼接详解

    最近在研究<javascript高级程序设计>中,有一段关于字符串特点的描述,原文大概如下:ECMAScript中的字符串是不可变的,也就是说,字符串一旦创建,他们的值就不能改变.要改变某个变量的保存的的字符串,首先要销毁原来的字符串,然后再用另外一个包含新值的字符串填充该变量,例如: 复制代码 代码如下: var lang = "Java"; lang = lang + "Script"; 实现这个操作的过程如下:首先创建一个能容纳10个字符的新字符串

  • Go语言如何高效的进行字符串拼接(6种方式对比分析)

    目录 前言 string类型 字符串拼接的6种方式及原理 原生拼接方式"+" 字符串格式化函数fmt.Sprintf Strings.builder bytes.Buffer strings.join 切片append Benchmark对比 结论 总结 前言 日常业务开发中离不开字符串的拼接操作,不同语言的字符串实现方式都不同,在Go语言中就提供了6种方式进行字符串拼接,那这几种拼接方式该如何选择呢?使用那个更高效呢?本文我们就一起来分析一下. 本文使用Go语言版本:1.17.1 s

随机推荐