您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何理解golang逃逸分析”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
最近想要將 protobuf 變量和之前設計的數(shù)據(jù)對象整合起來,維護在內(nèi)存中,以減少內(nèi)存申請和 GC 的性能損耗。
由于 gogoproto 在 unmarshal
時不保證輸入和輸出一致,作為結(jié)果的指針變量和輸入的字節(jié)切片可能不一致(比如說,在 unmarshal
slice 時沒有 reset
操作)。我們需要對這個指針變量進行重置,pb 生成文件的 reset
實現(xiàn)方法如下。
func (m *Data) Reset() { *m = Data{} }
在看到 Data{}
時我陷入了疑惑,按我的理解,這一步是需要申請內(nèi)存的。那么如此一來,我們在將某個 pb 變量拋入內(nèi)存時不可避免的還是需要申請內(nèi)存,這樣本次的研發(fā)需求好像失去了意義。
我的第一反應是,這是 gogoproto 的問題,也許官方 go proto 不是這樣的??墒侵匦律珊蟀l(fā)現(xiàn) reset
方法實現(xiàn)并沒有什么區(qū)別。只不過官方 go proto 會在 unmarshal
時主動 reset
。
那么,難道一開始的方向就錯了嗎?啊頭禿。
不死心的我開始看各種文檔,包括 gogoproto 的各種插件,可惜并沒有找到有用的內(nèi)容。接著我又開始看官方 proto 文檔。。。
這時我發(fā)現(xiàn)了一點蛛絲馬跡。
在日常使用 protobuf 時,如果不復用舊的變量,我們一般會
聲明指針變量,data := &pb.Data{}
;
解碼,proto.Unmarshal(bytes, data)
。
顯然,第一步是需要申請內(nèi)存。而按照 go proto 的源碼,unmarshal
時的 reset
操作又會申請一次內(nèi)存,難道 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 } }
結(jié)果如下。
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
結(jié)果是一目了然的,temp := &boy{}
確實沒有重復申請內(nèi)存。
到此,只需要逐個分析就好。首先打開編譯報告看一下。
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 }
新創(chuàng)建的 &boy{}
被全局變量引用,于是逃逸到堆上,成為動態(tài)變量,無法被重復利用。
var b1 = &boy{} //go:noinline func main() { temp := &boy{} // &boy literal does not escape *b1 = *temp }
*b1 = *temp
僅僅是賦值操作,新創(chuàng)建的 &boy{}
沒有被引用,留在棧上,后續(xù)被重復利用。
“如何理解golang逃逸分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。