Golang 字符串拼接及builder优化

Overview

  • +号拼接
  • fmt拼接
  • Join拼接
  • buffer拼接
  • builder拼接

在少数据的情况下,这几个方法相差不大,但是当要拼接的字符串很多的时候,推荐使用builder。而+号连接适用于短小的,常量字符串的拼接,因为编译器会优化

+号拼接

s := s1+s2+s3

fmt拼接

s := fmt.Sprintf("%v%v",s1,s2)

Join拼接

接收一个字符串数组,转换为一个拼接好的字符串

sList := []string{s1,s2}
s := strings.Join(SList,"") 

buffer拼接

不止可以拼接字符串,还可以拼接byte等

var b = bytes.Buffer
b.WriteString(s1)
b.WriteString(S2)
s := String() 

builder拼接

var b strings.Builder
b.WriteString(s1)
b.WriteString(s2)
s := b.String()

对builder进行优化

测试代码

project\stringBuilder\sb.go

package stringBuilder

import "strings"

func StringBuilder(p []string) string {
	var b strings.Builder
	l:=len(p)
	for i:=0;i<l;i++{
		b.WriteString(p[i])
	}
	return b.String()
}


project\stringBuilder\sb_test.go

package stringBuilder

import "testing"

const TESTSTRING  = "test,"

/*
	初始化函数
	生成一个具有N个字符串的数组
*/

func initStrings(N int) []string{
	s:=make([]string,N)
	for i:=0;i<N;i++{
		s[i]=TESTSTRING
	}
	return s;
}

/*
	测试
	10个字符串
*/
func BenchmarkStringBuilder10(b *testing.B) {
	p:= initStrings(10)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuilder(p)
	}
}

/*
	测试
	100个字符串
*/
func BenchmarkStringBuilder100(b *testing.B) {
	p:= initStrings(100)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuilder(p)
	}
}
/*
	测试
	1000个字符串
*/
func BenchmarkStringBuilder1000(b *testing.B) {
	p:= initStrings(1000)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuilder(p)
	}
}


测试结果

goos: windows
goarch: amd64
pkg: TestProject/stringBuilder
BenchmarkStringBuilder10-4   5163981    228 ns/op      120 B/op       4 allocs/op
BenchmarkStringBuilder100-4  1000000    1150 ns/op     1016 B/op      7 allocs/op
BenchmarkStringBuilder1000-4 107428     11735 ns/op    21240 B/op     13 allocs/op
PASS

builder慢在哪了?

从基准测试结果可以看出,主要是慢在了多次内存分配上,如果多次内存分配而且引起GC的话,就会更慢!

builder 源码

// WriteString appends the contents of s to b's buffer.
// It returns the length of s and a nil error.
func (b *Builder) WriteString(s string) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, s...)
	return len(s), nil
}

简单来说,就是通过append函数向[]byte b中填充,当b字节数组扩容时,就会引起内存分配,从而使得操作变慢。

解决办法

减少内存分配次数,也就是预先给b字节数组分配大小cap

修改代码

func StringBuilder(p []string,cap int) string {
	var b strings.Builder
	l:=len(p)
	b.Grow(cap)
	for i:=0;i<l;i++{
		b.WriteString(p[i])
	}
	return b.String()
}
//测试代码以10个字符串为例
//修改为如下
func BenchmarkStringBuilder10(b *testing.B) {
	p:= initStrings(10)
	b.ResetTimer()
	cap := 10*len(TESTSTRING)
	for i:=0;i<b.N;i++{
		StringBuilder(p,cap)
	}
}

优化后测试结果

BenchmarkStringBuilder10-4   10027047    114 ns/op    64 B/op    1 allocs/op
BenchmarkStringBuilder100-4  1312066     810 ns/op    512 B/op   1 allocs/op
BenchmarkStringBuilder1000-4  141570     8080 ns/op   5376 B/op   1 allocs/op
PASS

能优化20%~50%

猜你喜欢

转载自www.cnblogs.com/Jun10ng/p/12682524.html