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) string {
	var d string
	for i := 0; i < n; i++ {
		d += s
	}
	return d
}

格式化

func sprintfConcat(n int, s string) string {
	var d string
	for i := 0; i < n; i++ {
		d = fmt.Sprintf("%s%s", d, s)
	}
	return d
}

strings.Builder

func builderConcat(n int, s string) string {
	var sb = new(strings.Builder)
	for i := 0; i < n; i++ {
		sb.WriteString(s)
	}
	return sb.String()
}

bytes.Buffer

func bufferConcat(n int, s string) string {
	var bb = new(bytes.Buffer)
	for i := 0; i < n; i++ {
		bb.WriteString(s)
	}
	return bb.String()
}

[]byte

func byteConcat(n int, s string) string {
	var b = make([]byte, 0)
	for i := 0; i < n; i++ {
		b = append(b, s...)
	}
	return string(b)
}

预分配[]byte

func preByteConcat(n int, s string) string {
	var b = make([]byte, 0, n*len(s))
	for i := 0; i < n; i++ {
		b = append(b, s...)
	}
	return string(b)
}

1.2 字符串拼接测试

定义一个随机字符串生成函数:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randomString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

对上述6中字符串拼接函数进行基准测试:

func benchmark(b *testing.B, f func(int, string) string) {
	var str = randomString(10)
	for i := 0; i < b.N; i++ {
		f(10000, str)
	}
}
func BenchmarkPlusConcat(b *testing.B)    { benchmark(b, plusConcat) }
func BenchmarkSprintfConcat(b *testing.B) { benchmark(b, sprintfConcat) }
func BenchmarkBuilderConcat(b *testing.B) { benchmark(b, builderConcat) }
func BenchmarkBufferConcat(b *testing.B)  { benchmark(b, bufferConcat) }
func BenchmarkByteConcat(b *testing.B)    { benchmark(b, byteConcat) }
func BenchmarkPreByteConcat(b *testing.B) { benchmark(b, preByteConcat) }

go test -bench=. test/string -benchmem

毫无疑问 + 和 格式化 两种方式最耗时,且内存分配还多。

性能最好的是预分配[]byte方式,它只进行两次内存分配,其余部分全部在进行内存拷贝操作。

其次是strings.builder

然后是bytes.buffer

紧接着是 []byte方式

1.3 推荐

一般来说,选择使用string.Builder方式来进行拼接。

其次,strings.Builder 提供了 Grow方法,特殊情况下避免多次内存分配。

func builderConcat(n int, s string) string {
	var sb = new(strings.Builder)
	sb.Grow(n * len(s))
	for i := 0; i < n; i++ {
		sb.WriteString(s)
	}
	return sb.String()
}

然后Builder 再与 预分配的[]byte 比较:

得出:builder 比 预分配[]byte 少一次内存分配,当然内存使用也会少一半。

2.相关原理

2.1 + 号

+ 性能如此差是因为go 字符串本省不可修改,两个字符串拼接,那么新构造一个字符串,长度等与两个字符串长度之和,然后分别将两个字符串的内容拷贝到新的字符串中。且如果连续的字符串拼接,就像plusConcat函数,会产生大量临时对象d,对GC也是一种压力。

2.2 strings.Builder 与 bytes.Buffer

2.2.1 内部[]byte 增长方式:

strings.Builder 内部采用[]byte存储,初始大小为0,每次写入是按go 默认切片增长方式拓展底层[]byte的长度。

bytes.Buffer 内存采用[]byte,其内部有控制增长的算法,最小申请空间就为64bytes,在写入为超过一倍的情况下,是按1一倍空间增加。

64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 ...

2.2.2 性能比较

为啥 Buffer 比 Builder 多一次内存分配:

Buffer 的String() 方法:

func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return string(b.buf[b.off:])
}

Builder 的String() 方法:

// String returns the accumulated string.
func (b *Builder) String() string {
	return unsafe.String(unsafe.SliceData(b.buf), len(b.buf))
}

可以看出,Buffer在转字符串时,需要重新构造string对象;而Builder 返回的string 对象则直接复用Builder 底层的buf。

到此这篇关于Golong字符串拼接性能优化及原理介绍的文章就介绍到这了,更多相关Go字符串拼接内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • go语言字符串的拼接和切片方法总结

    目录 一,go字符串的本质 二,字符串拼接的几种方法 1,使用+号拼接字符串 2,使用fmt包的Sprintf()函数 3,使用strings包的Join()函数 4,使用bytes.Buffer储存字符串再打印输出 三,字符串的切片 四,字符串函数 附:golang将整型切片转换为字符串 总结 一,go字符串的本质 go语言字符串的本质就是byte[]数组,里面每一个数据存的是字符的Unicode码. 二,字符串拼接的几种方法 1,使用+号拼接字符串 拼接之后返回一个新的字符串. packag

  • Golang语言如何高效拼接字符串详解

    目录 01.介绍 02.操作符 + 03.strings.Join 方法 04.fmt.Sprint 方法 05.bytes.Buffer 类型 06.strings.Builder 类型 07.总结 01.介绍 在编程语言中,字符串是一种重要的数据结构.在 Golang 语言中,因为字符串只能被访问,不能被修改,所以,如果我们在 Golang 语言中进行字符串拼接操作,Golang 需要进行内存拷贝. 如果读者朋友们了解过 Golang 语言内存管理的相关知识,就会知道内存拷贝会带来性能消耗.

  • Go语言字符串高效拼接的实现

    +号拼接 这种拼接最简单,也最容易被我们使用,因为它是不限编程语言的,比如Go语言有,Java也有,它们是+号运算符,在运行时计算的. var s string s+="昵称"+":"+"志强1224"+"\n" s+="联系方式QQ"+":"+"354662600"+"\n" fmt.Println(s) fmt 拼接 这种拼接,借助于fmt.S

  • Golang字符串的拼接方法汇总

    字符串拼接在 golang 里面其实有很多种实现. 实现方式 直接使用运算符 func BenchmarkAddStringWithOperator(b *testing.B) {     hello := "hello"     world := "world"     for i := 0; i < b.N; i++ {         _ = hello + "," + world     } } golang里面的字符串都是不可变的

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

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

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

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

  • go语言中五种字符串的拼接方式(小结)

    目录 +拼接方式 sprintf函数 Join函数 buffer.Builderbuffer.WriteString函数 buffer.Builder函数 ps:直接使用运算符 主要结论 +拼接方式 这种方式是我在写golang经常用的方式,go语言用+拼接,php使用.拼接,不过由于golang中的字符串是不可变的类型,因此用 + 连接会产生一个新的字符串对效率有影响. func main() { s1 := "hello" s2 := "word" s3 :=

  • Go语言中的字符串拼接方法详情

    目录 1.string类型 2.strings包 2.1 strings.Builder类型 2.2 strings.Reader类型 3.bytes.Buffer 3.1 bytes.Buffer:写数据 3.2 bytes.Buffer:读数据 4.字符串拼接 4.1 直接相加 4.2strings.Builder 4.3 strings.Join() 4.4 bytes.Buffer 4.5 append方法 4.6 fmt.Sprintf 5.字符串拼接性能测试 1.string类型 s

  • 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

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

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

  • JavaScript 字符串连接性能优化

    复制代码 代码如下: var str = "hello"; str += " world"; 后台所做工作: 1)创建存储 "hello" 的字符串,且使 str 指向它. 2)创建存储 "world" 的字符串. 3)创建存储结果的字符串. 4)将 str 中的当前内容复制到结果字符串中. 5)把 world 复制到结果字符串中. 6)更新 str ,使 str 指向结果字符串. 每拼接一次字符串就循环重复2)~6),如果重

  • Java 8中字符串拼接新姿势StringJoiner详解

    在为什么阿里巴巴不建议在for循环中使用"+"进行字符串拼接一文中,我们介绍了几种Java中字符串拼接的方式,以及优缺点.其中还有一个重要的拼接方式我没有介绍,那就是Java 8中提供的StringJoiner ,本文就来介绍一下这个字符串拼接的新兵. 如果你想知道一共有多少种方法可以进行字符串拼接,教你一个简单的办法,在Intellij IDEA中,定义一个Java Bean,然后尝试使用快捷键自动生成一个toString方法,IDEA会提示多种toString生成策略可供选择. 1

  • javascript中字符串拼接详解

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

  • Listview的异步加载性能优化

    Android中ListView是使用平率最高的控件之一(GridView跟ListView是兄弟,都是继承AbsListView),ListView优化最有效的无非就是采用ViewHolder来减少频繁的对view查询和更新,缓存图片加快解码,减小图片尺寸. 关于listview的异步加载,网上其实很多示例了,中心思想都差不多,不过很多版本或是有bug,或是有性能问题有待优化,下面就让在下阐述其原理以探索个中奥秘在APP应用中,listview的异步加载图片方式能够带来很好的用户体验,同时也是

  • React 性能优化方法总结

    目录 前言 为什么页面会出现卡顿的现象? React 到底是在哪里出现了卡顿? React 有哪些场景会需要性能优化? 一:父组件刷新,而不波及子组件. 第一种:使用 PureComponent 第三种:函数组件如何判断props的变化的更新呢? 使用 React.memo函数 使用 React.useMemo来实现对子组件的缓冲 一:组件自己控制自己是否刷新 三:减少波及范围,无关刷新数据不存入state中 场景一:无意义重复调用setState,合并相关的state 场景二:和页面刷新没有相

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

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

  • Python字符串拼接的4种方法实例

    目录 1. 算术运算符拼接 (1)+算术运算符 (2) * 算术运算符 2.format方法 3.百分号操作符 4.特殊符号f 附:常见字符串去除空格的方法总结 总结 在程序实际应用中,少不了要进行字符串拼接的操作.下面介绍一下Python语言中四种字符串拼接的方式. 1. 算术运算符拼接 在Python中算术运算符一共有七种种,分别是+.-.*././/.**和%.其中+和*不仅可以用来进行算数计算,也可以用来字符串拼接. (1)+算术运算符 +运算符在Python中可以用作数学计算,例如:

随机推荐