Golang逃逸分析

Golang逃逸分析

介紹逃逸分析的概念,go怎麼開啓逃逸分析的log。
如下資料來自互聯網,有錯誤之處,請必定告之。
sheepbao 2017.06.10html

什麼是逃逸分析

wiki上的定義

In compiler optimization, escape analysis is a method for determining the dynamic scope of pointers - where in the program a pointer can be accessed. It is related to pointer analysis and shape analysis.golang

When a variable (or an object) is allocated in a subroutine, a pointer to the variable can escape to other threads of execution, or to calling subroutines. If an implementation uses tail call optimization (usually required for functional languages), objects may also be seen as escaping to called subroutines. If a language supports first-class continuations (as do Scheme and Standard ML of New Jersey), portions of the call stack may also escape.編程

If a subroutine allocates an object and returns a pointer to it, the object can be accessed from undetermined places in the program — the pointer has "escaped". Pointers can also escape if they are stored in global variables or other data structures that, in turn, escape the current procedure.api

Escape analysis determines all the places where a pointer can be stored and whether the lifetime of the pointer can be proven to be restricted only to the current procedure and/or threa數據結構

大概的意思是在編譯程序優化理論中,逃逸分析是一種肯定指針動態範圍的方法,能夠分析在程序的哪些地方能夠訪問到指針。它涉及到指針分析和形狀分析。 當一個變量(或對象)在子程序中被分配時,一個指向變量的指針可能逃逸到其它執行線程中,或者去調用子程序。若是使用尾遞歸優化(一般在函數編程語言中是須要的),對象也可能逃逸到被調用的子程序中。 若是一個子程序分配一個對象並返回一個該對象的指針,該對象可能在程序中的任何一個地方被訪問到——這樣指針就成功「逃逸」了。若是指針存儲在全局變量或者其它數據結構中,它們也可能發生逃逸,這種狀況是當前程序中的指針逃逸。 逃逸分析須要肯定指針全部能夠存儲的地方,保證指針的生命週期只在當前進程或線程中。編程語言

逃逸分析的用處(爲了性能)

  • 最大的好處應該是減小gc的壓力,不逃逸的對象分配在棧上,當函數返回時就回收了資源,不須要gc標記清除。
  • 由於逃逸分析完後能夠肯定哪些變量能夠分配在棧上,棧的分配比堆快,性能好
  • 同步消除,若是你定義的對象的方法上有同步鎖,但在運行時,卻只有一個線程在訪問,此時逃逸分析後的機器碼,會去掉同步鎖運行。

go消除了堆和棧的區別

go在必定程度消除了堆和棧的區別,由於go在編譯的時候進行逃逸分析,來決定一個對象放棧上仍是放堆上,不逃逸的對象放棧上,可能逃逸的放堆上。ide

開啓go編譯時的逃逸分析日誌

開啓逃逸分析日誌很簡單,只要在編譯的時候加上-gcflags '-m',可是咱們爲了避免讓編譯時自動內連函數,通常會加-l參數,最終爲-gcflags '-m -l'函數

Example:性能

package main

import (
	"fmt"
)

func main() {
	s := "hello"
	fmt.Println(s)
}

go run -gcflags '-m -l' escape.go
Output:優化

# command-line-arguments
escape_analysis/main.go:9: s escapes to heap
escape_analysis/main.go:9: main ... argument does not escape
hello

何時逃逸,何時不逃逸

Example1:

package main

type S struct{}

func main() {
	var x S
	y := &x
	_ = *identity(y)
}

func identity(z *S) *S {
	return z
}

Output:

# command-line-arguments
escape_analysis/main.go:11: leaking param: z to result ~r1 level=0
escape_analysis/main.go:7: main &x does not escape

這裏的第一行表示z變量是「流式」,由於identity這個函數僅僅輸入一個變量,又將這個變量做爲返回輸出,但identity並無引用z,因此這個變量沒有逃逸,而x沒有被引用,且生命週期也在mian裏,x沒有逃逸,分配在棧上。

Example2:

package main

type S struct{}

func main() {
	var x S
	_ = *ref(x)
}

func ref(z S) *S {
	return &z
}

Output:

# command-line-arguments
escape_analysis/main.go:11: &z escapes to heap
escape_analysis/main.go:10: moved to heap: z

這裏的z是逃逸了,緣由很簡單,go都是值傳遞,ref函數copy了x的值,傳給z,返回z的指針,而後在函數外被引用,說明z這個變量在函數內聲明,可能會被函數外的其餘程序訪問。因此z逃逸了,分配在堆上

對象裏的變量會怎麼樣呢?看下面

Example3:

package main

type S struct { 
	M *int
}

func main() { 
	var i int 
	refStruct(i)
}

func refStruct(y int) (z S) {
	z.M = &y
	return z 
}

Output:

# command-line-arguments
escape_analysis/main.go:13: &y escapes to heap
escape_analysis/main.go:12: moved to heap: y

看日誌的輸出,這裏的y是逃逸了,看來在struct裏好像並無區別,有可能被函數外的程序訪問就會逃逸

Example4:

package main

type S struct { 
	M *int
}

func main() { 
	var i int 
	refStruct(&i)
}

func refStruct(y *int) (z S) {
	z.M = y
	return z 
}

Output:

# command-line-arguments
escape_analysis/main.go:12: leaking param: y to result z level=0
escape_analysis/main.go:9: main &i does not escape

這裏的y沒有逃逸,分配在棧上,緣由和Example1是同樣的。

Example5:

package main

type S struct { 
	M *int
}

func main() { 
	var x S
	var i int
	ref(&i, &x) 
}

func ref(y *int, z *S) { 
	z.M = y
}

Output:

# command-line-arguments
escape_analysis/main.go:13: leaking param: y
escape_analysis/main.go:13: ref z does not escape
escape_analysis/main.go:10: &i escapes to heap
escape_analysis/main.go:9: moved to heap: i
escape_analysis/main.go:10: main &x does not escape

這裏的z沒有逃逸,而i卻逃逸了,這是由於go的逃逸分析不知道z和i的關係,逃逸分析不知道參數y是z的一個成員,因此只能把它分配給堆。

參考

Go Escape Analysis Flaws go-escape-analysis

相關文章
相關標籤/搜索