什麼是字串?
在 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。)
以上是Go String 解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!