性能调优之三十六计 —— 「优中取优」字符串拼接 篇

Posted 魏小言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能调优之三十六计 —— 「优中取优」字符串拼接 篇相关的知识,希望对你有一定的参考价值。


性能调优之三十六计 —— 「优中取优」字符串拼接 篇

性能调优是每一个程序员进阶的毕竟之路,掌握各种调优技巧,对自身实力、价值的提升至关重要!继上篇 “ 原生 Echo 框架 JSON 优化 ” 之后,我们把目光转向 “ 字符串拼接 ”。

在生产开发中,“ 字符串拼接 ” 出镜率极高,像日志落地、URI 参数构造、等等众多场景。开发语言也为我们提供了各种拼接姿势,如:

  • Java 中 “ + ”操作符 、StringBuilder、StringBuffer、Concat 、Join 、等等…
  • Go 中 “ + ” 操作符、fmt.Sprintf、Join、bytes.Buffer、strings.Builder、等等…

那么你最常用的是哪一种呢?性能如何呢?

+ 操作符

“ + ” 操作符是比较受喜欢的一种方式,它更便于阅读,符合人们运算范式的认知。
举个例子:

s1 := "字符串"
s2 := "拼接"
s3 := s1 + s2
fmt.Print(s3) //s3 = "打印字符串"

大家可以看到,其拼接过程中会产生新的变量,因为 String 类型为不可变类型。正是变量的产生中,内存的分配、GC 的回收,整体性能在某些场景会拉垮。

fmt.Sprintf

fmt.Sprintf 是 fmt 包内置的一种拼接方式,经常在构造 URI 参数时使用。
举个例子:

s1 := "字符串"
s2 := "拼接"
s3 := fmt.Sprintf("%s%s", s1, s2) //s3 = "打印字符串"

大家可以看到,其在使用的过程中,需要进行格式定义 %s 、%d、等等,为兼容更多的格式类型,Sprintf 会统一以 Interface 为基准,分别对每种类型进行设计实现,性能问题就是在这里。

Sprintf 性能往往不如 “ + ” 操作符。

Join

Join 是指数组类型行为方式,数组通过 JOIN 将元素以某种形式进行拼接。
举个例子:

s1 := "字符串"
s2 := "拼接"
var str []string = []string{s1, s2}
s3 := strings.Join(str, "")
fmt.Print(s3)

Join 会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入。

Join 的性能与数组对象的创建息息相关,与 “ + “ 操作符相差无几,但更适合数组原生场景。

buffer.WriteString

buffer.WriteString 故名思义,往字节缓冲区中写 String。
举个例子:

s1 := "字符串"
s2 := "拼接"
var bt bytes.Buffer
bt.WriteString(s1)
bt.WriteString(s2)
s3 := bt.String()
type Buffer struct {
	buf      []byte // contents are the bytes buf[off : len(buf)]
	off      int    // read at &buf[off], write at &buf[len(buf)]
	lastRead readOp // last read operation, so that Unread* can work correctly.
}

先是建立一字节缓冲区,向其中写入字节数组「string」,最后将其转化为 String 。

WriteString 由于没有多余的变量参与,性能具有最大优势。

buffer.Builder

buffer.Builder 是 Go 1.10 之后,官方提供的另一种方式,也是官方所提倡的。
举个例子

s1 := "字符串"
s2 := "拼接"
var build strings.Builder
build.WriteString(s1)
build.WriteString(s2)
s3 := build.String()
*(*string)(unsafe.Pointer(&bytes))

buffer.Builder 与 buffer.WriteString 内部都是通过 slice 来保存和管理内容。不同的是,buffer.Builder 在 String() 时,使用了指针,节省时间和空间,所以其性能最优!

Q&A

1、既然性能有差别,为啥还提供那么多不同的姿势呢?

这个问题忽略了事物发展演化过程。举个例子,随着工业的不断革新,交通业推出速度更快,更安全的交通工具。但之前诸如,马车、自行车、摩托车、依旧出现在不同的道路。

2、文中的性能具体是指速度吗?

是的。

3、文中貌似是 Go 为基准,可否介绍下其它语言?

文中以 Go 为基准讲解,其它语言可私信博主交流

附录

编程工作适宜乐观主义人群。

以上是关于性能调优之三十六计 —— 「优中取优」字符串拼接 篇的主要内容,如果未能解决你的问题,请参考以下文章

性能调优之三十六计 —— 「取而代之」Echo/Json 篇

性能调优之三十六计 —— 「取而代之」Echo/Json 篇

MYSQL数据库性能调优之三:explain分析慢查询

MySQL 性能调优之存储引擎

全新印刷版《DevOps 三十六计》图书等你来拿

sql server 性能调优之 CPU消耗最大资源分析1 (自sqlserver服务启动以后)