首頁 > 後端開發 > Golang > 主體

Go String 解析

Guanhui
發布: 2020-06-13 09:05:28
轉載
3045 人瀏覽過

Go String 解析

什麼是字串?

在 Go 中,字串是一個 (可能為空) 不可變的位元組序列。對我們來說,這裡的關鍵字是 不可變。因為位元組片是可變的,所以在 string 和 []byte 之間進行轉換通常需要分配和複製,這是很昂貴的。

在幕後,Go 的字串 (當前) 表示為 長度和指向字串資料的指標.

什麼是字串駐留?

考慮這段程式碼:

b := []byte("hello")
s := string(b)
t := string(b)
登入後複製

s 和 t 是字串,因此它們都有長度和資料指標。它們的長度顯然是相同的。那它們的數據指針呢?

Go 語言無法提供我們直接的查找方法。但我們可以使用unsafe 來探查:

func pointer(s string) uintptr {
    p := unsafe.Pointer(&s)
    h := *(*reflect.StringHeader)(p)
    return h.Data
}
登入後複製

(此函數應傳回unsafe.Pointer。詳見Go 問題19367.)

如果我們fmt.Println(pointer(s), pointer( t)),我們會得到類似4302664 4302632 的資訊。指標是不同的;它們有兩個單獨的資料副本 hello。

(這是一個練習連結。如果你想要嘗試,將"hello" 變成"h" 會發生什麼情況?解釋  )

假設您希望重新使用資料hello 的單個副本?這就是字串駐留。字串駐留有兩個優點。明顯的一個優點是,你不需要分配和複製資料。另一個優點是它加快了字串相等性檢查的速度。如果兩個字串具有相同的長度和相同的資料指針,則它們是相等的;沒有必要檢查位元組。

從 Go 1.14 開始,Go 不會駐留大多數字串。與其它形式的快取一樣,駐留也有成本:並發安全性的同步,垃圾收集器的複雜性,以及每次創建字串時要執行的額外程式碼。而且,就像快取一樣,在某些情況下它是有害的,而不是有用的。如果你在處理字典裡的單詞,則任何單字都不會出現兩次,這時,字串駐留既浪費時間又浪費記憶體。

手動字串駐留

可以在 Go 中手動駐留字串。我們需要的是一種在給定位元組切片 (byte slice) 的情況下尋找現有字串以重新使用的方法,也許使用諸如 map[[]byte]string 之類的方法。如果查找成功,則使用現有字串;如果失敗,我們將轉換並儲存該字串以備將來使用。

這裡只有一個問題:您不能使用 []byte 作為 map 的鍵。

多虧了長期的編譯器最佳化,我們可以使用 map[string]string 來代替。這裡有一個最佳化,鍵是轉換後位元組切片的 map 操作實際上不會產生在查找期間會用到的新字串。

m := make(map[string]string)
b := []byte("hello")
s := string(b) // 分配了
_ = m[string(b)] // 不分配!
登入後複製

(類似的最佳化適用於其他情況,在這些情況下,編譯器可以證明轉換後的位元組切片在使用過程中不會被修改,例如switch string(b),當所有switch情況都沒有副作用時。)

駐留字串所需的全部程式碼是這樣的:

func intern(m map[string]string, b []byte) string {
    // 查找一个存在的字符串来重用
    c, ok := m[string(b)]
    if ok {
        // 找到一个存在的字符串
        return c
    }
    // 没有找到,所以制作一个并且存储它
    s := string(b)
    m[s] = s
    return s
}
登入後複製

很簡單

新出現的困難(並發症)

請注意,這個手動駐留例程將駐留問題推入了呼叫程式碼。您需要管理對 map 的並發存取;您需要確定 map (以及其中的所有內容) 的生命週期;並且您每次需要字串時都需要付出 map 查找的額外費用。

將這些決定推到呼叫程式碼上可以產生更好的效能。例如,假設您正在將 json 解碼為 map[string]interface{}。 json 解碼器可能不是並發的。 map 的生命週期可以綁定到 json 解碼器。並且此 map 的鍵很可能會經常重複,這是字串駐留的最佳情況;這使得額外的 map 查找成本值得。

一個助手包

如果您不想考慮這些併發症中的任何一個,並且願意接受輕微的效能損失,並且有字串駐留可能會有所幫助的程式碼,則有一個為此的套件:github.com/josharian/intern。

它的工作原理是可怕的濫用 sync.Pool。它將駐留 maps 儲存在 sync.Pool 中,根據需要檢索它們。這很好的解決了並發存取問題,因為 sync.Pool 的存取是並發安全的。它主要解決了生存期問題,因為在 sync.Pool 中的內容通常最終會被垃圾收集。 (有關管理生存期的相關閱讀,請參閱 Go issue 29696。)

推薦教學:《PHP》《GO教學

以上是Go String 解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:learnku.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!