您好,登錄后才能下訂單哦!
本篇內容介紹了“如何理解golang逃逸分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
最近想要將 protobuf 變量和之前設計的數據對象整合起來,維護在內存中,以減少內存申請和 GC 的性能損耗。
由于 gogoproto 在 unmarshal
時不保證輸入和輸出一致,作為結果的指針變量和輸入的字節切片可能不一致(比如說,在 unmarshal
slice 時沒有 reset
操作)。我們需要對這個指針變量進行重置,pb 生成文件的 reset
實現方法如下。
func (m *Data) Reset() { *m = Data{} }
在看到 Data{}
時我陷入了疑惑,按我的理解,這一步是需要申請內存的。那么如此一來,我們在將某個 pb 變量拋入內存時不可避免的還是需要申請內存,這樣本次的研發需求好像失去了意義。
我的第一反應是,這是 gogoproto 的問題,也許官方 go proto 不是這樣的。可是重新生成后發現 reset
方法實現并沒有什么區別。只不過官方 go proto 會在 unmarshal
時主動 reset
。
那么,難道一開始的方向就錯了嗎?啊頭禿。
不死心的我開始看各種文檔,包括 gogoproto 的各種插件,可惜并沒有找到有用的內容。接著我又開始看官方 proto 文檔。。。
這時我發現了一點蛛絲馬跡。
在日常使用 protobuf 時,如果不復用舊的變量,我們一般會
聲明指針變量,data := &pb.Data{}
;
解碼,proto.Unmarshal(bytes, data)
。
顯然,第一步是需要申請內存。而按照 go proto 的源碼,unmarshal
時的 reset
操作又會申請一次內存,難道 Google 會允許這種性能損耗?
真的嗎,我不信。
想的太多,不如寫個 benchmark 試一下。(小心 microbenchmark 的一些坑)
package main import ( "testing" ) type boy struct { name string age int } var b1 = &boy{} var b2 = &boy{} func Benchmark_1(b *testing.B) { for i := 0; i < b.N; i++ { temp := &boy{} b1 = temp } } func Benchmark_2(b *testing.B) { for i := 0; i < b.N; i++ { temp := &boy{} *b1 = *temp } } func Benchmark_3(b *testing.B) { for i := 0; i < b.N; i++ { temp := &boy{} *b1 = *temp b2 = temp } }
結果如下。
goos: linux goarch: amd64 pkg: bible Benchmark_1-4 29142411 42.2 ns/op 32 B/op 1 allocs/op Benchmark_2-4 1000000000 0.711 ns/op 0 B/op 0 allocs/op Benchmark_3-4 28474614 39.5 ns/op 32 B/op 1 allocs/op PASS ok bible 3.258s
結果是一目了然的,temp := &boy{}
確實沒有重復申請內存。
到此,只需要逐個分析就好。首先打開編譯報告看一下。
go build -gcflags "-m -m"
var b1 = &boy{} //go:noinline func main() { temp := &boy{} // &boy literal escapes to heap: // flow: temp = &{storage for &boy literal}: // from &boy literal (spill) at ./main.go:12:10 // from temp := &boy literal (assign) at ./main.go:12:7 // flow: {heap} = temp: // from b1 = temp (assign) at ./main.go:13:5 // &boy literal escapes to heap b1 = temp }
新創建的 &boy{}
被全局變量引用,于是逃逸到堆上,成為動態變量,無法被重復利用。
var b1 = &boy{} //go:noinline func main() { temp := &boy{} // &boy literal does not escape *b1 = *temp }
*b1 = *temp
僅僅是賦值操作,新創建的 &boy{}
沒有被引用,留在棧上,后續被重復利用。
“如何理解golang逃逸分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。