Go - 基於逃逸分析來提升程序性能

前言

爲什麼需要了解逃逸分析?

因爲我們想要提升程序性能,通過逃逸分析我們能夠知道變量是分配到堆上還是棧上,如果分配到棧上,內存的分配和釋放都是由編譯器進行管理,分配和釋放的速度非常快,如果分配到堆上,堆不像棧那樣可以自動清理,它會引起頻繁地進行垃圾回收(GC),而垃圾回收會佔用比較大的系統開銷。

什麼是逃逸分析?

在編譯程序優化理論中,逃逸分析是一種確定指針動態範圍的方法,簡單來說就是分析在程序的哪些地方可以訪問到該指針。

簡單的說,它是在對變量放到堆上還是棧上進行分析,該分析在編譯階段完成。如果一個變量超過了函數調用的生命週期,也就是這個變量在函數外部存在引用,編譯器會把這個變量分配到堆上,這時我們就說這個變量發生逃逸了。

如何確定是否逃逸?

go run -gcflags '-m -l' main.go

可能出現逃逸的場景

01

package main

type Student struct {
	Name interface{}
}

func main()  {
	stu := new(Student)
	stu.Name = "tom"

}

分析結果:

go run -gcflags '-m -l' 01.go
# command-line-arguments
./01.go:8:12: new(Student) does not escape
./01.go:9:11: "tom" escapes to heap

interface{} 賦值,會發生逃逸,優化方案是將類型設置爲固定類型,例如:string

package main

type Student struct {
	Name string
}

func main()  {
	stu := new(Student)
	stu.Name = "tom"

}

分析結果:

go run -gcflags '-m -l' 01.go
# command-line-arguments
./01.go:8:12: new(Student) does not escape

02

package main

type Student struct {
	Name string
}

func GetStudent() *Student {
	stu := new(Student)
	stu.Name = "tom"
	return stu
}

func main() {
	GetStudent()
}

分析結果:

go run -gcflags '-m -l' 02.go
# command-line-arguments
./02.go:8:12: new(Student) escapes to heap

返回指針類型,會發生逃逸,優化方案視情況而定。

函數傳遞指針和傳值哪個效率高嗎?我們知道傳遞指針可以減少底層值的拷貝,可以提高效率,但是如果拷貝的數據量小,由於指針傳遞會產生逃逸,可能會使用堆,也可能會增加 GC 的負擔,所以傳遞指針不一定是高效的。

不要盲目使用變量指針作爲參數,雖然減少了複製,但變量逃逸的開銷可能更大。

03

package main

func main() {
	nums := make([]int, 10000, 10000)

	for i := range nums {
		nums[i] = i
	}
}

分析結果:

go run -gcflags '-m -l' 03.go
# command-line-arguments
./03.go:4:14: make([]int, 10000, 10000) escapes to heap

棧空間不足,會發生逃逸,優化方案儘量設置容量,如果容量實在過大那就沒辦法了。

小結

  1. 逃逸分析是編譯器在靜態編譯時完成的。
  2. 逃逸分析後可以確定哪些變量可以分配在棧上,棧的性能好。

以上,希望對你能夠有所幫助。

推薦閱讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章