既然要優化builder
拼接,那麼我們起碼知道他慢在哪,我們繼續使用我們上篇文章的測試用例,執行看下效能。
builder10-8 5000000 258 ns/op 480 b/op 4 allocs/op
builder100-8 1000000 2012 ns/op 6752 b/op 8 allocs/op
builder1000-8 100000 21016 ns/op 96224 b/op 16 allocs/op
builder10000-8 10000 195098 ns/op 1120226 b/op 25 allocs/op
複製**
針對既然要優化builder
拼接,採取了10、100、1000、10000四種不同數量的字串進行拼接測試。我們發現每次操作都有不同次數的記憶體分配,記憶體分配越多,越慢,如果引起gc,就更慢了,首先我們先優化這個,減少記憶體分配的次數。
通過cpuprofile,檢視生成的火焰圖可以得知,runtime.growslice
函式會被頻繁的呼叫,並且時間佔比也比較長。我們檢視builder.writestring
的源**:
func
(b *builder)
writestring
(s string)
(int, error)
複製**
既然問題的原因找到了,那麼我們就可以優化了,核心手段就是減少runtime.growslice
呼叫,甚至不呼叫。照著這個思路的話,我們就要提前為b.buf
分配好容量cap
。幸好builder
為我們提供了擴充容量的方法grow
,我們在進行writestring
之前,先通過grow
方法,擴充好容量即可。
現在開始改造我們的stringbuilder
函式。
func
stringbuilder
(p string,cap
int)
string
複製**
增加乙個引數cap
,讓使用者告訴我們需要的容量大小。grow
方法的實現非常簡單,就是乙個通過make
函式,擴充b.buf
大小,然後再拷貝b.buf
的過程。
func
(b *builder)
grow
(n int)
複製**
那麼現在我們的效能測試用例變成如下:
func
benchmarkstringbuilder10
(b *testing.b) }
func
benchmarkstringbuilder1000
(b *testing.b) }
複製**
為了說明情況和簡短**,這裡只有10和1000個元素的用例,其他類似。為了把效能優化到極致,我一次性把需要的容量分配足夠。現在我們再執行效能(benchmark)測試**。
builder10-8 10000000 123 ns/op 352 b/op 1 allocs/op
builder100-8 2000000 898 ns/op 2688 b/op 1 allocs/op
builder1000-8 200000 7729 ns/op 24576 b/op 1 allocs/op
builder10000-8 20000 78678 ns/op 237568 b/op 1 allocs/op
複製**
效能足足翻了1倍多,只有1次記憶體分配,每次操作占用的記憶體也減少了一半多,降低了gc。
字串拼接的系列,到這裡結束了,一共三個系列,希望對大家所有幫助。
go字串拼接方法
go 語言中的字串是唯讀的 拼接方式 這種方式是我在寫golang經常用的方式,go語言用 拼接,php使用.拼接,不過由於golang中的字串是不可變的型別,因此用 連線會產生乙個新的字串對效率有影響。go語言中文文件 www.topgoer.com func main sprintf函式 s1 ...
Go語言字串拼接的三種方式
通過建立乙個緩衝byte型別的緩衝器str1,然後通過writestring方法將傳入的字串放入緩衝器的尾部,已達到拼接的作用,然後呼叫緩衝器str1的string 方法,可以返回放入緩衝器中的內容 實際返回的是緩衝器中未讀的部分 返回型別為string。var str1 bytes.buffer ...
拼接字串
border 1 class box 標籤名稱th 是否顯示th 標籤順序th tr thead 首頁td class check 是option 否option select td class number 1option 2option 3option 4option 5option 6opti...