關(guān)于Go的四種字符串拼接及性能比較'

先上代碼

// fmt.Sprintf
func BenchmarkStringSprintf(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        var str string
        for j := 0; j < numbers; j++ {
            str = fmt.Sprintf("%s%d", str, j)
        }
    }
    b.StopTimer()
}

// add
func BenchmarkStringAdd(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        var str string
        for j := 0; j < numbers; j++ {
            str = str + string(j)
        }
    }
    b.StopTimer()
}

// bytes.Buffer
func BenchmarkStringBuffer(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        var buffer bytes.Buffer
        for j := 0; j < numbers; j++ {
            buffer.WriteString(strconv.Itoa(j))
        }
        _ = buffer.String()
    }
    b.StopTimer()
}

// strings.Builder
func BenchmarkStringBuilder(b *testing.B) {
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        var builder strings.Builder
        for j := 0; j < numbers; j++ {
            builder.WriteString(strconv.Itoa(j))
        }
        _ = builder.String()
    }
    b.StopTimer()
}

運(yùn)行結(jié)果

lijun:benchmark/ $ go test -bench=.                                                           [10:01:20]
goos: darwin
goarch: amd64
pkg: class12/benchmark
BenchmarkStringSprintf-4              30          47358694 ns/op
BenchmarkStringAdd-4                  50          27664814 ns/op
BenchmarkStringBuffer-4            10000            184422 ns/op
BenchmarkStringBuilder-4           10000            157039 ns/op
PASS
ok      class12/benchmark       6.350s
lijun:benchmark/ $                                                                            [10:01:58]

如果還不知道 Go 的 benchmark腔长,可以先去了解一下草则,個(gè)人認(rèn)為還是非常不錯(cuò)的性能測(cè)試的工具摧莽。

得出結(jié)論

四種拼接字符串的方式,性能比較結(jié)果
strings.Builder > bytes.Buffer > string add > fmt.Sprintf

為什么赵讯?

這里我們還是直接看源碼吧

先看 Sprintf
// Sprintf formats according to a format specifier and returns the resulting string.
func Sprintf(format string, a ...interface{}) string {
    p := newPrinter()
    p.doPrintf(format, a)
    s := string(p.buf)
    p.free()
    return s
}

這是 fmt.Sprintf 的源碼,我們可以看到內(nèi)部會(huì)通過(guò) newPrinter 創(chuàng)建一個(gè)新對(duì)象 p耿眉,點(diǎn)進(jìn)去看一下 newPrinter 這個(gè)函數(shù)

// newPrinter allocates a new pp struct or grabs a cached one.
func newPrinter() *pp {
    p := ppFree.Get().(*pp)
    p.panicking = false
    p.erroring = false
    p.fmt.init(&p.buf)
    return p
}

它會(huì)從系統(tǒng)的臨時(shí)對(duì)象池中那 pp 這個(gè)對(duì)象边翼,關(guān)于臨時(shí)對(duì)象池(sync.Pool),下次有機(jī)會(huì)再探討鸣剪。這里可以知道组底, Sprintf 會(huì)從臨時(shí)對(duì)象池中獲取一個(gè) *pp 的指針丈积,然后再做一些格式化的操作,doPrintf 代碼就不貼了债鸡,格式化后的底層字節(jié)會(huì)放到 []byte 這個(gè)切片里面江滨,最后再 string 轉(zhuǎn)換成字符串返回,并且釋放掉 p 對(duì)象厌均。
整個(gè)過(guò)程:創(chuàng)建對(duì)象 - 格式化操作 - string化 - 釋放對(duì)象

接下來(lái)看 string

string 是在 Go 里面是一個(gè)不可變類型唬滑,所以下面的代碼

str = str + str2

每次都會(huì)創(chuàng)建一個(gè)新的 string 類型的值,然后重新賦值給 str 這個(gè)變量棺弊,相比于上面的 Sprintf 主要少了格式化這個(gè)過(guò)程晶密,所以在性能上肯定要優(yōu)于 Sprintf

bytes.Buffer

我們看一下 builder 的 String() 函數(shù)源碼

// String returns the contents of the unread portion of the buffer
// as a string. If the Buffer is a nil pointer, it returns "<nil>".
//
// To build strings more efficiently, see the strings.Builder type.
func (b *Buffer) String() string {
    if b == nil {
        // Special case, useful in debugging.
        return "<nil>"
    }
    return string(b.buf[b.off:])
}

字符串的底層結(jié)構(gòu)是一個(gè) []byte 的字節(jié)序列,而 Buffer 是直接獲取未讀取的 []byte序列模她,在轉(zhuǎn)成 string 返回惹挟,少了重復(fù)創(chuàng)建對(duì)象這個(gè)步驟。
b.buf 是 []byte 切片
b.off 是已讀取的字節(jié)位置

strings.Builder
// String returns the accumulated string.
func (b *Builder) String() string {
    return *(*string)(unsafe.Pointer(&b.buf))
}

strings.Builder 直接通過(guò)指針來(lái)操作了缝驳,在效率上更進(jìn)一步连锯。

總結(jié)

通過(guò)源碼來(lái)分析,還是比較清晰明了的用狱,但是限于我自身的水平运怖,對(duì)于源碼的解讀并不都是特別深入,這里也是給大家做出一個(gè)參考夏伊。關(guān)于最后的通過(guò)轉(zhuǎn)換成指針來(lái)返回字符串的操作摇展,我也就是知道轉(zhuǎn)成指針效率會(huì)高,但是關(guān)于為什么溺忧,也都是模棱兩可(是因?yàn)橹苯油ㄟ^(guò)操作內(nèi)存地址嗎)咏连。總之關(guān)于基礎(chǔ)性鲁森、底層的東西還是要多多學(xué)習(xí)祟滴。

來(lái)自 https://leejnull.github.io/2019/08/29/2019-08-29/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市歌溉,隨后出現(xiàn)的幾起案子垄懂,更是在濱河造成了極大的恐慌,老刑警劉巖痛垛,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件草慧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡匙头,警方通過(guò)查閱死者的電腦和手機(jī)漫谷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蹂析,“玉大人舔示,你說(shuō)我怎么就攤上這事朽寞。” “怎么了斩郎?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵脑融,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我缩宜,道長(zhǎng)肘迎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任锻煌,我火速辦了婚禮妓布,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宋梧。我一直安慰自己匣沼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布捂龄。 她就那樣靜靜地躺著释涛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪倦沧。 梳的紋絲不亂的頭發(fā)上唇撬,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音展融,去河邊找鬼窖认。 笑死,一個(gè)胖子當(dāng)著我的面吹牛告希,可吹牛的內(nèi)容都是我干的扑浸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼燕偶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喝噪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起杭跪,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仙逻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后涧尿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡檬贰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年姑廉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翁涤。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桥言,死狀恐怖萌踱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情号阿,我是刑警寧澤并鸵,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站扔涧,受9級(jí)特大地震影響园担,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枯夜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一弯汰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧湖雹,春花似錦咏闪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至征讲,卻和暖如春溪胶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稳诚。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工哗脖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扳还。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓胸哥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親躲撰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜒谤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345