在Golang的代码开发中,我们经常会用到字符串的拼接。Golang提供了不同的字符串拼接方式,性能也不尽相同。有时候在做性能优化的时候,往往会看到有些同学想当然的选择一些自认为性能比较高的方法。但是实际情况是否真的能提升性能呢?我们一起来看一下。
对比较短字符串拼接
var (
str1 = "my name is "
str2 = "zhangSan"
)
func BenchmarkSprintf(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = fmt.Sprintf("my name is %s", str2)
}
}
func BenchmarkAddStr(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = str1 + str2
}
}
func BenchmarkJoin(b *testing.B) {
b.ReportAllocs()
slice := []string{str1, str2}
for i := 0; i < b.N; i++ {
strings.Join(slice, "")
}
}
func BenchmarkWriteString(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var bt bytes.Buffer
bt.WriteString(str1)
bt.WriteString(str2)
bt.String()
}
}
func BenchmarkBuilder(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var builder strings.Builder
builder.WriteString(str1)
builder.WriteString(str2)
builder.String()
}
}
结果:
BenchmarkSprintf-12 9046708 115.7 ns/op 40 B/op 2 allocs/op
BenchmarkAddStr-12 65630836 18.38 ns/op 0 B/op 0 allocs/op
BenchmarkJoin-12 27375624 42.56 ns/op 24 B/op 1 allocs/op
BenchmarkWriteString-12 24698983 48.77 ns/op 64 B/op 1 allocs/op
BenchmarkBuilder-12 48307612 51.00 ns/op 94 B/op 0 allocs/op
结论
如果字符串很短的情况下,直接使用字符串拼接 比 其他方式 性能高很多。
对比长字符串拼接
var (
str1 = "This is a test string This is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test string" +
"This is a test string This is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test string" +
"This is a test string This is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test string "
str2 = "zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan" +
"zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan" +
"zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan"
)
结果:
BenchmarkSprintf-12 1848331 617.1 ns/op 1169 B/op 2 allocs/op
BenchmarkAddStr-12 4457694 245.4 ns/op 1152 B/op 1 allocs/op
BenchmarkJoin-12 4750164 233.3 ns/op 1152 B/op 1 allocs/op
BenchmarkWriteString-12 2055642 543.4 ns/op 3520 B/op 3 allocs/op
BenchmarkBuilder-12 3731229 310.9 ns/op 1984 B/op 2 allocs/op
结论
如果字符串较长的情况下,除了sprintf 和WriteString,其他的性能差不多
对比不那么长的字符串拼接
var (
str1 = "This is a test string This is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test stringThis is a test string "
str2 = "zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan zhangSan"
)
结果:
BenchmarkSprintf-12 4700313 270.5 ns/op 368 B/op 2 allocs/op
BenchmarkAddStr-12 14756258 77.44 ns/op 352 B/op 1 allocs/op
BenchmarkJoin-12 14365586 83.16 ns/op 352 B/op 1 allocs/op
BenchmarkWriteString-12 4964223 247.7 ns/op 1120 B/op 3 allocs/op
BenchmarkBuilder-12 9257761 136.3 ns/op 576 B/op 2 allocs/op
结论
不那么长的字符串拼接,除了sprintf 和WriteString,其他的性能差不多
对动态字符串拼接
var (
str1 = "my name is "
//str2 = "zhangSan"
)
func BenchmarkSprintf(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
_ = fmt.Sprintf("my name is %s", strconv.Itoa(i))
}
}
func BenchmarkAddStr(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
_ = str1 + strconv.Itoa(i)
}
}
func BenchmarkJoin(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
slice := []string{str1, strconv.Itoa(i)}
strings.Join(slice, "")
}
}
func BenchmarkWriteString(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
var bt bytes.Buffer
bt.WriteString(str1)
bt.WriteString(strconv.Itoa(i))
bt.String()
}
}
func BenchmarkBuilder(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
var builder strings.Builder
builder.WriteString(str1)
builder.WriteString(strconv.Itoa(i))
builder.String()
}
}
结果:
BenchmarkSprintf-12 7725692 140.8 ns/op 47 B/op 2 allocs/op
BenchmarkAddStr-12 25583774 46.80 ns/op 7 B/op 0 allocs/op
BenchmarkJoin-12 18607413 85.10 ns/op 31 B/op 1 allocs/op
BenchmarkWriteString-12 12217564 108.2 ns/op 71 B/op 1 allocs/op
BenchmarkBuilder-12 12008508 97.00 ns/op 55 B/op 2 allocs/op
结论
依然是,除了sprintf 和WriteString,其他的性能差不多
多次连续拼接M次测试
var (
m = 100
)
func BenchmarkSprintf(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
s := ""
str := strconv.Itoa(i)
for j := 0; j < m; j++ {
s = fmt.Sprintf("%s%s", s, str)
}
}
}
func BenchmarkAddStr(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
s := ""
str := strconv.Itoa(i)
for j := 0; j < m; j++ {
s += str
}
}
}
func BenchmarkJoin(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
s := ""
str := strconv.Itoa(i)
for j := 0; j < m; j++ {
slice := []string{s, str}
s = strings.Join(slice, "")
}
}
}
func BenchmarkWriteString(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
s := ""
str := strconv.Itoa(i)
var bt bytes.Buffer
bt.WriteString(s)
for j := 0; j < m; j++ {
bt.WriteString(str)
s = bt.String()
}
}
}
func BenchmarkBuilder(b *testing.B) {
b.ReportAllocs()
for i := 10000; i < b.N; i++ {
s := ""
str := strconv.Itoa(i)
var builder strings.Builder
builder.WriteString(s)
for j := 0; j < m; j++ {
builder.WriteString(str)
s = builder.String()
}
}
}
结果:
BenchmarkSprintf-12 1000000 22329 ns/op 34126 B/op 297 allocs/op
BenchmarkAddStr-12 1000000 9804 ns/op 30936 B/op 99 allocs/op
BenchmarkJoin-12 1000000 9315 ns/op 30944 B/op 99 allocs/op
BenchmarkWriteString-12 1000000 7713 ns/op 33042 B/op 104 allocs/op
BenchmarkBuilder-12 1385815 835.4 ns/op 1847 B/op 8 allocs/op
如果我们将m提升到200次呢?
结果:
BenchmarkSprintf-12 1000000 54355 ns/op 130971 B/op 594 allocs/op
BenchmarkAddStr-12 1000000 32290 ns/op 124485 B/op 198 allocs/op
BenchmarkJoin-12 1000000 41579 ns/op 124492 B/op 198 allocs/op
BenchmarkWriteString-12 1000000 30737 ns/op 128768 B/op 204 allocs/op
BenchmarkBuilder-12 1000000 1635 ns/op 3294 B/op 9 allocs/op
结论
连续大量字符串拼接的时候,使用builder一种方式性能比较好
结论
在不同的使用场景下,每种字符串拼接的方式性能一不定和你想象中的一样。不要想当然的觉得 字符串拼接 性能很差,所以改用其他方式实现。
评论区