Golong字符串拼接性能優(yōu)化及原理介紹
1.字符串高效拼接
go 字符串是不可修改的,所謂字符串拼接就是創(chuàng)建新的字符串對(duì)象。如果代碼中存在大量的字符串拼接,那么性能將會(huì)存在影響。
1.1 常見(jiàn)的字符串拼接
+號(hào)
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) }
預(yù)分配[]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 字符串拼接測(cè)試
定義一個(gè)隨機(jī)字符串生成函數(shù):
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) }
對(duì)上述6中字符串拼接函數(shù)進(jìn)行基準(zhǔn)測(cè)試:
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
毫無(wú)疑問(wèn) + 和 格式化 兩種方式最耗時(shí),且內(nèi)存分配還多。
性能最好的是預(yù)分配[]byte方式,它只進(jìn)行兩次內(nèi)存分配,其余部分全部在進(jìn)行內(nèi)存拷貝操作。
其次是strings.builder
然后是bytes.buffer
緊接著是 []byte方式
1.3 推薦
一般來(lái)說(shuō),選擇使用string.Builder方式來(lái)進(jìn)行拼接。
其次,strings.Builder 提供了 Grow方法,特殊情況下避免多次內(nèi)存分配。
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 再與 預(yù)分配的[]byte 比較:
得出:builder 比 預(yù)分配[]byte 少一次內(nèi)存分配,當(dāng)然內(nèi)存使用也會(huì)少一半。
2.相關(guān)原理
2.1 + 號(hào)
+ 性能如此差是因?yàn)間o 字符串本省不可修改,兩個(gè)字符串拼接,那么新構(gòu)造一個(gè)字符串,長(zhǎng)度等與兩個(gè)字符串長(zhǎng)度之和,然后分別將兩個(gè)字符串的內(nèi)容拷貝到新的字符串中。且如果連續(xù)的字符串拼接,就像plusConcat函數(shù),會(huì)產(chǎn)生大量臨時(shí)對(duì)象d,對(duì)GC也是一種壓力。
2.2 strings.Builder 與 bytes.Buffer
2.2.1 內(nèi)部[]byte 增長(zhǎng)方式:
strings.Builder 內(nèi)部采用[]byte存儲(chǔ),初始大小為0,每次寫(xiě)入是按go 默認(rèn)切片增長(zhǎng)方式拓展底層[]byte的長(zhǎng)度。
bytes.Buffer 內(nèi)存采用[]byte,其內(nèi)部有控制增長(zhǎng)的算法,最小申請(qǐng)空間就為64bytes,在寫(xiě)入為超過(guò)一倍的情況下,是按1一倍空間增加。
64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 ...
2.2.2 性能比較
為啥 Buffer 比 Builder 多一次內(nèi)存分配:
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在轉(zhuǎn)字符串時(shí),需要重新構(gòu)造string對(duì)象;而B(niǎo)uilder 返回的string 對(duì)象則直接復(fù)用Builder 底層的buf。
到此這篇關(guān)于Golong字符串拼接性能優(yōu)化及原理介紹的文章就介紹到這了,更多相關(guān)Go字符串拼接內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Go語(yǔ)言如何實(shí)現(xiàn)字符串切片反轉(zhuǎn)函數(shù)
Go?語(yǔ)言不像其他語(yǔ)言如?Python,有著內(nèi)置的?reverse()?函數(shù),本文將先學(xué)習(xí)一下Python中對(duì)于列表的反轉(zhuǎn)方法,然后再學(xué)習(xí)如果在Go語(yǔ)言中實(shí)現(xiàn)相同的功能,感興趣的小伙伴快跟隨小編一起來(lái)學(xué)習(xí)一下2022-10-10go-micro使用Consul做服務(wù)發(fā)現(xiàn)的方法和原理解析
這篇文章主要介紹了go-micro使用Consul做服務(wù)發(fā)現(xiàn)的方法和原理,這里提供一個(gè)通過(guò)docker快速安裝Consul的方式,當(dāng)然前提是你得安裝了docker,需要的朋友可以參考下2022-04-04go語(yǔ)言錯(cuò)誤處理基本概念(創(chuàng)建返回)
這篇文章主要為大家介紹了go語(yǔ)言錯(cuò)誤處理基本概念(創(chuàng)建返回),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Golang使用Channel組建高并發(fā)HTTP服務(wù)器
Golang 作為一門(mén)高效的語(yǔ)言,在網(wǎng)絡(luò)編程方面表現(xiàn)也非常出色,這篇文章主要介紹了如何使用 Golang 和 Channel 組建高并發(fā) HTTP 服務(wù)器,感興趣的可以了解一下2023-06-06golang?sync.Cond同步機(jī)制運(yùn)用及實(shí)現(xiàn)
在?Go?里有專門(mén)為同步通信而生的?channel,所以較少看到?sync.Cond?的使用,不過(guò)它也是并發(fā)控制手段里的一種,今天我們就來(lái)認(rèn)識(shí)下它的相關(guān)實(shí)現(xiàn),加深對(duì)同步機(jī)制的運(yùn)用2023-09-09