Go語(yǔ)言中[]byte和string類型相互轉(zhuǎn)換時(shí)的性能分析和優(yōu)化

歡迎訪問我的個(gè)人網(wǎng)站獲取更佳排版體驗(yàn): https://pengrl.com/p/31544/Go語(yǔ)言中[]byte和string類型相互轉(zhuǎn)換時(shí)的性能分析和優(yōu)化 | yoko blog

我們?cè)谑褂肎o語(yǔ)言時(shí)即舌,經(jīng)常涉及到[]byte和string兩種類型間的轉(zhuǎn)換葛圃。本篇文章將討論轉(zhuǎn)換時(shí)的開銷肪获,Go編譯器在一些特定場(chǎng)景下對(duì)轉(zhuǎn)換做的優(yōu)化灶壶,以及在高性能場(chǎng)景下,我們自己如何做相應(yīng)的優(yōu)化夭委。

[]byte其實(shí)就是byte類型的切片旅薄,對(duì)應(yīng)的底層結(jié)構(gòu)體定義如下(在runtime/slice.go文件中)

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

string對(duì)應(yīng)的底層結(jié)構(gòu)體定義如下(在runtime/string.go文件中)

type stringStruct struct {
    str unsafe.Pointer
    len int
}

可以看到它們內(nèi)部都有一個(gè)指針類型(array或str)沥阱,指向真實(shí)數(shù)據(jù)。另外還有一個(gè)len字段昔头,標(biāo)識(shí)數(shù)據(jù)的長(zhǎng)度饼问。
slice多了一個(gè)cap字段,表示容量大小揭斧。當(dāng)要往slice尾部追加數(shù)據(jù)而空余容量又不夠時(shí)莱革,會(huì)重新分配更大的內(nèi)存塊,將當(dāng)前內(nèi)存塊的內(nèi)容拷貝至新內(nèi)存塊,再在新內(nèi)存塊做追加盅视。

slice變量間做賦值操作時(shí)捐名,只是修改指針指向,不會(huì)拷貝真實(shí)數(shù)據(jù)闹击。string變量間賦值也是同樣的道理镶蹋。

但是[]byte和string相互轉(zhuǎn)換,就需要重新申請(qǐng)內(nèi)存并拷貝內(nèi)存了赏半。因?yàn)镚o語(yǔ)義中贺归,slice的內(nèi)容是可變的(mutable),而string是不可變的(immutable)除破。如果他們底部指向同一塊數(shù)據(jù)牧氮,那么由于slice可對(duì)數(shù)據(jù)做修改琼腔,string就做不到immutable了瑰枫。

[]byte和string互轉(zhuǎn)時(shí)的底層調(diào)用分別對(duì)應(yīng)runtime/string.gostringtoslicebyteslicebytetostring兩個(gè)函數(shù)。

那么如果我們想省去申請(qǐng)和拷貝內(nèi)存的開銷呢丹莲?
來看runtime/string.goslicebytetostringtmpstringtoslicebytetmp兩個(gè)函數(shù)光坝,如下:

func slicebytetostringtmp(b []byte) string {
    // Return a "string" referring to the actual []byte bytes.
    // This is only for use by internal compiler optimizations
    // that know that the string form will be discarded before
    // the calling goroutine could possibly modify the original
    // slice or synchronize with another goroutine.
    // First such case is a m[string(k)] lookup where
    // m is a string-keyed map and k is a []byte.
    // Second such case is "<"+string(b)+">" concatenation where b is []byte.
    // Third such case is string(b)=="foo" comparison where b is []byte.

    if raceenabled && len(b) > 0 {
        racereadrangepc(unsafe.Pointer(&b[0]),
            uintptr(len(b)),
            getcallerpc(unsafe.Pointer(&b)),
            funcPC(slicebytetostringtmp))
    }
    return *(*string)(unsafe.Pointer(&b))
}

func stringtoslicebytetmp(s string) []byte {
    // Return a slice referring to the actual string bytes.
    // This is only for use by internal compiler optimizations
    // that know that the slice won't be mutated.
    // The only such case today is:
    // for i, c := range []byte(str)

    str := (*stringStruct)(unsafe.Pointer(&s))
    ret := slice{array: unsafe.Pointer(str.str), len: str.len, cap: str.len}
    return *(*[]byte)(unsafe.Pointer(&ret))
}

通過unsafe.Pointer直接做指針類型的轉(zhuǎn)換。

注釋中也說得很清楚了甥材。

stringtoslicebytetmp調(diào)用的前提是保證返回的[]byte之后不會(huì)被修改盯另,只用于編譯器內(nèi)部?jī)?yōu)化,目前唯一的場(chǎng)景是在for loop中將string轉(zhuǎn)換成[]byte做遍歷操作時(shí)洲赵,比如 for i, c := range []byte(str)

slicebytetostringtmp調(diào)用的前提其實(shí)也是類似鸳惯,保證返回的string在生命周期結(jié)束之前,[]byte不會(huì)被修改叠萍,也是只用于編譯器內(nèi)部?jī)?yōu)化芝发,目前有三種場(chǎng)景:

  1. 假設(shè)有一個(gè)key為string的map遍歷m,你想使用[]byte類型的變量k做查找操作苛谷,比如 m[string(k)]
  2. 做字符串拼接操作時(shí)辅鲸,比如 <"+string(b)+">,其中b是[]byte類型
  3. []byte類型和常量字符串做比較操作腹殿,比如 string(b)=="foo"

由于以上兩個(gè)函數(shù)是不暴露給Go用戶的独悴,所以如果我們?cè)谝恍└咝阅軋?chǎng)景想要做類似優(yōu)化時(shí),可以通過unsafe.Pointer自己做類似實(shí)現(xiàn)锣尉,當(dāng)然刻炒,前提是保證數(shù)據(jù)是immutable的。

參考鏈接:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末自沧,一起剝皮案震驚了整個(gè)濱河市坟奥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖筏勒,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件移迫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡管行,警方通過查閱死者的電腦和手機(jī)厨埋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捐顷,“玉大人荡陷,你說我怎么就攤上這事⊙镐蹋” “怎么了废赞?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叮姑。 經(jīng)常有香客問我唉地,道長(zhǎng),這世上最難降的妖魔是什么传透? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任耘沼,我火速辦了婚禮,結(jié)果婚禮上朱盐,老公的妹妹穿的比我還像新娘群嗤。我一直安慰自己,他們只是感情好兵琳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布狂秘。 她就那樣靜靜地躺著,像睡著了一般躯肌。 火紅的嫁衣襯著肌膚如雪者春。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天羡榴,我揣著相機(jī)與錄音碧查,去河邊找鬼。 笑死校仑,一個(gè)胖子當(dāng)著我的面吹牛忠售,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迄沫,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼稻扬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了羊瘩?” 一聲冷哼從身側(cè)響起泰佳,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤盼砍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后逝她,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浇坐,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年黔宛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了近刘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臀晃,死狀恐怖觉渴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情徽惋,我是刑警寧澤案淋,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站险绘,受9級(jí)特大地震影響踢京,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隆圆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一漱挚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渺氧,春花似錦、人聲如沸蹬屹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慨默。三九已至贩耐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間厦取,已是汗流浹背潮太。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虾攻,地道東北人铡买。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像霎箍,于是被迫代替她去往敵國(guó)和親奇钞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容