亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解golang逃逸分析

發布時間:2021-10-21 13:56:48 來源:億速云 閱讀:330 作者:iii 欄目:編程語言

本篇內容介紹了“如何理解golang逃逸分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

背景

最近想要將 protobuf 變量和之前設計的數據對象整合起來,維護在內存中,以減少內存申請和 GC 的性能損耗。

feature or bug,gogoproto 解碼疑惑

由于 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 時,如果不復用舊的變量,我們一般會

  1. 聲明指針變量,data := &pb.Data{}

  2. 解碼,proto.Unmarshal(bytes, data)

顯然,第一步是需要申請內存。而按照 go proto 的源碼,unmarshal 時的 reset 操作又會申請一次內存,難道 Google 會允許這種性能損耗?

真的嗎,我不信。

逃逸分析入門

想的太多,不如寫個 benchmark 試一下。(小心 microbenchmark 的一些坑)

benchmark

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逃逸分析

“如何理解golang逃逸分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

高阳县| 前郭尔| 淮南市| 祁门县| 巴东县| 苍梧县| 南溪县| 罗平县| 临澧县| 杭锦后旗| 冀州市| 和硕县| 三明市| 怀柔区| 松潘县| 万荣县| 永修县| 鹿邑县| 昆明市| 明水县| 美姑县| 会泽县| 平安县| 平山县| 清徐县| 乌拉特前旗| 伽师县| 伊宁市| 永兴县| 原阳县| 河津市| 陕西省| 抚顺县| 秦皇岛市| 施秉县| 泗阳县| 两当县| 闵行区| 榕江县| 芷江| 铁岭市|