golang字符串拼接与性能分析

我傻笑你跑掉 · · 2056 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

字符串拼接在golang中是非常常见的操作,本文介绍几种常用方法并分析各种方法的效率.

拼接

+ 号拼接

+ 号拼接是最常见的方式

var a string = "Hello,"
var b string = "World!"
func Test1() string {
    return a + b
}

buffer拼接

bytes 库提供一个结构体 Buffer, Buffer结构允许多次写入[]bytestringrune类型的数据并一次性输出

var a string = "Hello,"
var b string = "World!"
func Test2() string {
    var buffer bytes.Buffer
    buffer.WriteString(a)
    buffer.WriteString(b)
    return buffer.String()
}

fmt.Sprint()格式化

fmt 库提供的 SprintX() 系列函数可以返回格式化后的字符串,也可用来做拼接操作

var a string = "Hello,"
var b string = "World!"
func Test3() string {
    return fmt.Sprint(a, b)
}

append拼接

字符串的底层是数组,而数组的拼接可以使用 append(),因此可以利用这一特性来进行字符串拼接操作.

var a string = "Hello,"
var b string = "World!"
func Test4() string {
    return string(append([]byte(a), []byte(b)...))
}

性能

以上介绍了比较常见的几种拼接方式,但是究竟哪种效率更高呢?下面针对单次的拼接做一个测试,将上述代码保存为plus.go.

package plus
import (
    "bytes"
    "fmt"
)
var a string = "Hello,"
var b string = "World!"

func Test1() string {
    return a + b
}

func Test2() string {
    var buffer bytes.Buffer
    buffer.WriteString(a)
    buffer.WriteString(b)
    return buffer.String()
}

func Test3() string {
    return fmt.Sprint(a, b)
}

func Test4() string {
    return string(append([]byte(a), []byte(b)...))
}

然后编写测试脚本plus_test.go.

package plus_test

import (
    p "plus"
    "testing"
)

func BenchmarkTestPlus(b *testing.B) {
    for i := 0; i < b.N; i++ {
        p.Test1()
    }
}

func BenchmarkTestBuffer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        p.Test2()
    }
}

func BenchmarkTestFormat(b *testing.B) {
    for i := 0; i < b.N; i++ {
        p.Test3()
    }
}

func BenchmarkTestAppend(b *testing.B) {
    for i := 0; i < b.N; i++ {
        p.Test4()
    }
}

运行性能测试代码 go test stringplus_test.go -bench=. 结果如下:

  • goos: darwin
  • goarch: amd64
  • BenchmarkTestPlus-8 100000000 22.2 ns/op
  • BenchmarkTestBuffer-8 20000000 102 ns/op
  • BenchmarkTestFormat-8 10000000 191 ns/op
  • BenchmarkTestAppend-8 50000000 26.4 ns/op

然后我们可以做一个排序,单次拼接运行时间:
+ < append()< bytes.Buffer < fmt.Sprint()

分析

+运算符为系统底层提供,我们无法一窥究竟.但可以由此来推断其余几种方式的运行过程, 字符串的底层是数组,姑且猜测 +方法底层就append(a,b) 的方式实习.

来看 append() 方式, 拼接期间进行了两次类型转换,分别是

  1. 字符串 a,b 转换成 []byte类型.
  2. append()拼接后 []byte类型转为string

append()执行时间与+差距不大,由此可以推测出时间浪费在类型转换上.做一个验证:

var a string = "Hello,"
var b string = "World!"
var c = []byte{72, 101, 108, 108, 111, 44}
var d = []byte{87, 111, 114, 108, 100, 33}

func Test1() string {
    return a+b
}

func Test5() []byte {
    return append(c, d...)
}

结果出乎意料,速度反而不如转换类型后进行append()然后再转换.
goos: darwin
goarch: amd64

  • BenchmarkTestPlus-8 100000000 22.0 ns/op
  • BenchmarkTestAppend-8 50000000 26.0 ns/op
  • BenchmarkTestAppendByte-8 50000000 37.6 ns/op

查看append()的源码 src/builtin/builtin.go 得知如下极点.

  1. 对于[]slice拼接, 有len 和 cap两个属性,而且还涉及底层数组.
  2. 如果容量够则直接拼接,如果不够,先扩容容量再拼接.
  3. 如果没有连续的内存空间可以扩容,会在新的内存空间建立扩容后的切片,再将原切片拷贝过去.

//明天接着写


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:我傻笑你跑掉

查看原文:golang字符串拼接与性能分析

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2056 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传