欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析Go語(yǔ)言中的逃逸分析

 更新時(shí)間:2024年10月29日 09:32:04   作者:apocelipes  
逃逸分析算是go語(yǔ)言的特色之一,所以這篇文章小編就來(lái)和大家聊聊為什么不應(yīng)該過(guò)度關(guān)注go語(yǔ)言的逃逸分析,感興趣的小伙伴可以跟隨小編一起了解一下

逃逸分析算是go語(yǔ)言的特色之一,編譯器自動(dòng)分析變量/內(nèi)存應(yīng)該分配在棧上還是堆上,程序員不需要主動(dòng)關(guān)心這些事情,保證了內(nèi)存安全的同時(shí)也減輕了程序員的負(fù)擔(dān)。

然而這個(gè)“減輕負(fù)擔(dān)”的特性現(xiàn)在卻成了程序員的心智負(fù)擔(dān)。尤其是各路八股文普及之后,逃逸分析相關(guān)的問(wèn)題在面試?yán)锍霈F(xiàn)的頻率越來(lái)越高,不會(huì)往往意味著和工作機(jī)會(huì)失之交臂,更有甚者會(huì)認(rèn)為不了解逃逸分析約等于不會(huì)go。

我很不喜歡這些現(xiàn)象,不是因?yàn)槲也粫?huì)go,而是我知道逃逸分析是個(gè)啥情況:分析規(guī)則有版本間差異、規(guī)則過(guò)于保守很多時(shí)候把可以在棧上的變量逃逸到堆上、規(guī)則繁雜導(dǎo)致有很多corner case等等。更不提有些質(zhì)量欠佳的八股在逃逸分析的描述上還有誤導(dǎo)了。

所以我建議大部分人回歸逃逸分析的初心——對(duì)于程序員來(lái)說(shuō)逃逸分析應(yīng)該就像是透明的,不要過(guò)度關(guān)心它。

怎么知道變量是不是逃逸了

我還見(jiàn)過(guò)一些比背過(guò)時(shí)的八股文更過(guò)分的情況:一群人圍著一段光禿禿的代碼就變量到底會(huì)不會(huì)逃逸爭(zhēng)得面紅耳赤。

他們甚至沒(méi)有用go編譯器自帶的驗(yàn)證方法來(lái)論證自己的觀點(diǎn)。

那樣的爭(zhēng)論是沒(méi)有意義的,你應(yīng)該用下面的命令來(lái)檢查編譯器逃逸分析的結(jié)果:

$ go build -gcflags=-m=2 a.go

# command-line-arguments
./a.go:5:6: cannot inline main: function too complex: cost 104 exceeds budget 80
./a.go:12:20: inlining call to fmt.Println
./a.go:12:21: num escapes to heap:
./a.go:12:21:   flow: {storage for ... argument} = &{storage for num}:
./a.go:12:21:     from num (spill) at ./a.go:12:21
./a.go:12:21:     from ... argument (slice-literal-element) at ./a.go:12:20
./a.go:12:21:   flow: fmt.a = &{storage for ... argument}:
./a.go:12:21:     from ... argument (spill) at ./a.go:12:20
./a.go:12:21:     from fmt.a := ... argument (assign-pair) at ./a.go:12:20
./a.go:12:21:   flow: {heap} = *fmt.a:
./a.go:12:21:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./a.go:12:20
./a.go:7:19: make([]int, 10) does not escape
./a.go:12:20: ... argument does not escape
./a.go:12:21: num escapes to heap

哪些東西逃逸了哪些沒(méi)有顯示得一清二楚——escapes to heap表示變量或表達(dá)式逃逸了,does not escape則表示沒(méi)有發(fā)生逃逸。

另外本文討論的是go官方的gc編譯器,像一些第三方編譯器比如tinygo沒(méi)義務(wù)也沒(méi)理由使用和官方完全相同的逃逸規(guī)則——這些規(guī)則并不是標(biāo)準(zhǔn)的一部分也不適用于某些特殊場(chǎng)景。

本文的go版本是1.23,我也不希望未來(lái)某一天有人用1.1x或者1.3x版本的編譯器來(lái)問(wèn)我為啥實(shí)驗(yàn)結(jié)果不一樣了。

八股文里的問(wèn)題

先聲明,對(duì)事不對(duì)人,愿意分享信息的精神還是值得尊敬的。

不過(guò)分享之前至少先做點(diǎn)簡(jiǎn)單的驗(yàn)證,不然那些倒果為因還有胡言亂語(yǔ)的內(nèi)容就止增笑耳了。

編譯期不知道大小的東西會(huì)逃逸

這話其實(shí)沒(méi)說(shuō)錯(cuò),但很多八股文要么到這里結(jié)束了,要么給出一個(gè)很多時(shí)候其實(shí)不逃逸的例子然后做一大通令人捧腹的解釋。

比如:

package main
 
import "fmt"
 
type S struct {}
 
func (*S) String() string { return "hello" }
 
type Stringer interface {
        String() string
}
 
func getString(s Stringer) string {
        if s == nil {
                return "<nil>"
        }
        return s.String()
}
 
func main() {
        s := &S{}
        str := getString(s)
        fmt.Println(str)
}

一些八股文會(huì)說(shuō)getString的參數(shù)s在編譯期很難知道實(shí)際類型是什么,所以大小不好確定,所以會(huì)導(dǎo)致傳給它的參數(shù)逃逸。

這話對(duì)嗎?對(duì)也不對(duì),因?yàn)榫幾g期這個(gè)時(shí)間段太寬泛了,一個(gè)interface在“編譯期”的前半段時(shí)間不知道實(shí)際類型,但后半段就有可能知道了。所以關(guān)鍵在于逃逸分析在什么時(shí)候進(jìn)行,這直接決定了類型為接口的變量的逃逸分析結(jié)果。

我們驗(yàn)證一下:

# command-line-arguments
...
./b.go:22:18: inlining call to getString
...
./b.go:22:18: devirtualizing s.String to *S
...
./b.go:23:21: str escapes to heap:
./b.go:23:21:   flow: {storage for ... argument} = &{storage for str}:
./b.go:23:21:     from str (spill) at ./b.go:23:21
./b.go:23:21:     from ... argument (slice-literal-element) at ./b.go:23:20
./b.go:23:21:   flow: fmt.a = &{storage for ... argument}:
./b.go:23:21:     from ... argument (spill) at ./b.go:23:20
./b.go:23:21:     from fmt.a := ... argument (assign-pair) at ./b.go:23:20
./b.go:23:21:   flow: {heap} = *fmt.a:
./b.go:23:21:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./b.go:23:20
./b.go:21:14: &S{} does not escape
./b.go:23:20: ... argument does not escape
./b.go:23:21: str escapes to heap

我只截取了關(guān)鍵信息,否則雜音太大。&S{} does not escape這句直接告訴我們getString的參數(shù)并沒(méi)有逃逸。

為啥?因?yàn)?code>getString被內(nèi)聯(lián)了,內(nèi)聯(lián)后編譯器發(fā)現(xiàn)參數(shù)的實(shí)際類型就是S,所以devirtualizing s.String to *S做了去虛擬化,這下接口的實(shí)際類型編譯器知道了,所以沒(méi)有讓參數(shù)逃逸的必要了。

而str逃逸了,str的類型是已知的,內(nèi)容也是常量字符串,按八股文的理論不是不應(yīng)該逃逸么?其實(shí)上面的信息也告訴你為什么了,因?yàn)?code>fmt.Println內(nèi)部的一些函數(shù)沒(méi)法內(nèi)聯(lián),而它們又用any去接受參數(shù),這時(shí)候編譯器沒(méi)法做去虛擬化,沒(méi)法最終確定變量的真實(shí)大小,所以str只能逃逸了。記得最開(kāi)頭我說(shuō)的嗎,逃逸分析是很保守的,因?yàn)閮?nèi)存安全和程序的正確性是第一位的。

如果禁止函數(shù)inline,情況就不同了,我們?cè)趃o里可以手動(dòng)禁止一個(gè)函數(shù)被內(nèi)聯(lián):

+//go:noinline
func getString(s Stringer) string {
        if s == nil {
                return "<nil>"
        }
        return s.String()
}

這回再看結(jié)果:

# command-line-arguments
./b.go:14:6: cannot inline getString: marked go:noinline
...
./b.go:22:14: &S{} escapes to heap:
./b.go:22:14:   flow: s = &{storage for &S{}}:
./b.go:22:14:     from &S{} (spill) at ./b.go:22:14
./b.go:22:14:     from s := &S{} (assign) at ./b.go:22:11
./b.go:22:14:   flow: {heap} = s:
./b.go:22:14:     from s (interface-converted) at ./b.go:23:19
./b.go:22:14:     from getString(s) (call parameter) at ./b.go:23:18
./b.go:22:14: &S{} escapes to heap
./b.go:24:20: ... argument does not escape
./b.go:24:21: str escapes to heap

getString沒(méi)法內(nèi)聯(lián),所以沒(méi)法做去虛擬化,最后無(wú)法在逃逸分析前得知變量的大小,所以作為參數(shù)的s最后逃逸了。

因此“編譯期”這個(gè)表述不太對(duì),正確的應(yīng)該是“在逃逸分析執(zhí)行時(shí)不能知道確切大小的變量/內(nèi)存分配會(huì)逃逸”。還有一點(diǎn)要注意:內(nèi)聯(lián)和一部分內(nèi)置函數(shù)/語(yǔ)句的改寫(xiě)發(fā)生在逃逸分析之前。內(nèi)聯(lián)是什么大家應(yīng)該知道,改寫(xiě)改天有空了再好好介紹。

而且go對(duì)于什么能在逃逸分析前計(jì)算出來(lái)也是比較隨性的:

func main() {
        arr := [4]int{}
        slice := make([]int, 4)
        s1 := make([]int, len(arr)) // not escape
        s2 := make([]int, len(slice)) // escape
}

s1不逃逸但s2逃逸,因?yàn)閘en在計(jì)算數(shù)組的長(zhǎng)度時(shí)會(huì)直接返回一個(gè)編譯期常量。而len計(jì)算slice的長(zhǎng)度時(shí)并不能在編譯期完成計(jì)算,所以即使我們很清楚slice此時(shí)的長(zhǎng)度就是4,但go還是會(huì)認(rèn)為s2的大小不能在逃逸分析前就確定。

這也是為什么我告誡大家不要過(guò)度關(guān)心逃逸分析這東西,很多時(shí)候它是反常識(shí)的。

編譯期知道大小就不會(huì)逃逸嗎

有的八股文基于上一節(jié)的現(xiàn)象,得出了下面這樣的結(jié)論:make([]T, 常數(shù))不會(huì)逃逸。

我覺(jué)得一個(gè)合格的go或者c/c++/rust程序員應(yīng)該馬上近乎本能地反駁:不逃逸就會(huì)分配在棧上,??臻g通常有限(系統(tǒng)棧通常8-10M,goroutine則是固定的1G),如果這個(gè)make需要的內(nèi)存空間大小超過(guò)了棧的上限呢?

很顯然超過(guò)了上限就會(huì)逃逸到堆上,所以上面那句不太對(duì)。go當(dāng)然有規(guī)定一次在??臻g上分配內(nèi)存的上限,這個(gè)上限也遠(yuǎn)小于棧大小的上限,但我不會(huì)告訴你是多少,因?yàn)闆](méi)人保證以后不會(huì)改,而且我說(shuō)了,你關(guān)心這個(gè)并沒(méi)有什么用。

還有一種經(jīng)典的情況,make生成的內(nèi)容做返回值:

func f1() []int {
        return make([]int, 64)
}

逃逸分析會(huì)給出這樣的結(jié)果:

# command-line-arguments
...
./c.go:6:13: make([]int, 64) escapes to heap:
./c.go:6:13:   flow: ~r0 = &{storage for make([]int, 64)}:
./c.go:6:13:     from make([]int, 64) (spill) at ./c.go:6:13
./c.go:6:13:     from return make([]int, 64) (return) at ./c.go:6:2
./c.go:6:13: make([]int, 64) escapes to heap

這沒(méi)什么好意外的,因?yàn)榉祷刂狄诤瘮?shù)調(diào)用結(jié)束后繼續(xù)被使用,所以它只能在堆上分配。這也是逃逸分析的初衷。

不過(guò)因?yàn)檫@個(gè)函數(shù)太簡(jiǎn)單了,所以總是能內(nèi)聯(lián),一旦內(nèi)聯(lián),這個(gè)make就不再是返回值,所以編譯器有機(jī)會(huì)不讓它逃逸。你可以用上一節(jié)教的//go:noinline試試。

slice的元素?cái)?shù)量和是否逃逸關(guān)系不大

還有的八股會(huì)這么說(shuō):“slice里的元素?cái)?shù)量太多會(huì)導(dǎo)致逃逸”,還有些八股文還會(huì)信誓旦旦地說(shuō)這個(gè)數(shù)量限制是什么10000、十萬(wàn)。

那好,我們看個(gè)例子:

package main
 
import "fmt"
 
func main() {
        a := make([]int64, 10001)
        b := make([]byte, 10001)
        fmt.Println(len(a), len(b))
}

分析結(jié)果:

...
./c.go:6:11: make([]int64, 10001) escapes to heap:
./c.go:6:11:   flow: {heap} = &{storage for make([]int64, 10001)}:
./c.go:6:11:     from make([]int64, 10001) (too large for stack) at ./c.go:6:11
...
./c.go:6:11: make([]int64, 10001) escapes to heap
./c.go:7:11: make([]byte, 10001) does not escape
...

怎么元素?cái)?shù)量一樣,一個(gè)逃逸了一個(gè)沒(méi)有?說(shuō)明了和元素?cái)?shù)量就沒(méi)關(guān)系,只和上一節(jié)說(shuō)的棧上對(duì)內(nèi)存分配大小有限制,超過(guò)了才會(huì)逃逸,沒(méi)超過(guò)你分配一億個(gè)元素都行。

關(guān)鍵是這種無(wú)聊的問(wèn)題出鏡率還不低,我和我朋友都遇到過(guò)這種:

make([]int, 10001)

就問(wèn)你這個(gè)東西逃逸不逃逸,面試官估計(jì)忘了int長(zhǎng)度不是固定的,32位系統(tǒng)上它是4字節(jié),64位上是8字節(jié),所以沒(méi)有更多信息之前這個(gè)問(wèn)題沒(méi)法回答,你就是把Rob Pike抓來(lái)他也只能搖頭。面試遇到了還能和面試官掰扯掰扯,筆試遇到了你怎么辦?

這就是我說(shuō)的倒果為因,slice和數(shù)組會(huì)逃逸不是因?yàn)樵財(cái)?shù)量多,而是消耗的內(nèi)存(元素大小x數(shù)量)超過(guò)了規(guī)定的上限。

new和make在逃逸分析時(shí)幾乎沒(méi)區(qū)別

有的八股文還說(shuō)new的對(duì)象經(jīng)常逃逸而make不會(huì),所以應(yīng)該盡量少用new。

這是篇老八股了,現(xiàn)在估計(jì)沒(méi)人會(huì)看,然而就算在當(dāng)時(shí)這句話也是錯(cuò)的。我想大概是八股作者不經(jīng)驗(yàn)證就把Java/c++里的知識(shí)嫁接過(guò)來(lái)了。

我得澄清一下,new和make確實(shí)非常不同,但只不同在兩個(gè)地方:

  • new(T)返回*T,而make(T, ...)返回T
  • new(T)中T可以是任意類型(但slice呀接口什么的一般不建議),而make(T, ...)的T只能是slice、map或者chan。

就這兩個(gè),另外針對(duì)slice之類的東西它們?cè)诔跏蓟木唧w方式上有一點(diǎn)區(qū)別,但這勉強(qiáng)包含在第二點(diǎn)里了。

所以絕不會(huì)出現(xiàn)new更容易導(dǎo)致逃逸,new和make一樣,會(huì)不會(huì)逃逸只受大小限制以及可達(dá)性的影響。

看個(gè)例子:

package main
 
import "fmt"
 
func f(i int) int {
        ret := new(int)
        *ret = 1
        for j := 1; j <= i; j++ {
                *ret *= j
        }
        return *ret
}
 
func main() {
        num := f(5)
        fmt.Println(num)
}

結(jié)果:

./c.go:5:6: can inline f with cost 20 as: func(int) int { ret := new(int); *ret = 1; for loop; return *ret }
...
./c.go:15:10: inlining call to f
./c.go:16:13: inlining call to fmt.Println
./c.go:6:12: new(int) does not escape
...
./c.go:15:10: new(int) does not escape
./c.go:16:13: ... argument does not escape
./c.go:16:14: num escapes to heap

看到new(int) does not escape了嗎,流言不攻自破。

不過(guò)為了防止有人較真,我得稍微介紹一點(diǎn)實(shí)現(xiàn)細(xì)節(jié):雖然new和make在逃逸分析上差異不大,但當(dāng)前版本的go對(duì)make的大小限制更嚴(yán)格,這么看的話那個(gè)八股還是錯(cuò)的,因?yàn)閙ake導(dǎo)致逃逸的概率稍大于new。所以該用new就用,不需要在意這些東西。

編譯優(yōu)化太弱雞拖累逃逸分析

這兩年go語(yǔ)言有兩個(gè)讓我對(duì)逃逸分析徹底失去興趣的提交,第一個(gè)是:7015ed

改動(dòng)就是給一個(gè)局部變量加了別名,這樣編譯器就不會(huì)讓這個(gè)局部變量錯(cuò)誤地逃逸了。

為啥編譯器會(huì)讓這個(gè)變量逃逸?和編譯器實(shí)現(xiàn)可達(dá)性分析的算法有關(guān),也和編譯器沒(méi)做優(yōu)化導(dǎo)致分析精度降低有關(guān)。

如果你碰到了這種問(wèn)題,你能想出這種修復(fù)手段嗎?我反正是不能,因?yàn)檫@個(gè)提交這么做是有開(kāi)發(fā)和維護(hù)編譯器的大佬深入研究之后才定位問(wèn)題并提出可選方案的,對(duì)普通人來(lái)說(shuō)恐怕都想不明白問(wèn)題出在哪。

另一個(gè)是我在1.24開(kāi)發(fā)周期里遇到的。這個(gè)提交為了添加新功能對(duì)time.Time做了點(diǎn)小修改,以前的代碼這樣:

func (t Time) MarshalText() ([]byte, error) {
        b := make([]byte, 0, len(RFC3339Nano))
        b, err := t.appendStrictRFC3339(b)
        if err != nil {
                return nil, errors.New("Time.MarshalText: " + err.Error())
        }
        return b, nil
}

新的長(zhǎng)這樣:

func (t Time) appendTo(b []byte, errPrefix string) ([]byte, error) {
	b, err := t.appendStrictRFC3339(b)
	if err != nil {
		return nil, errors.New(errPrefix + err.Error())
	}
	return b, nil
}
 
func (t Time) MarshalText() ([]byte, error) {
	return t.appendTo(make([]byte, 0, len(RFC3339Nano)), "Time.MarshalText: ")
}

其實(shí)就是開(kāi)發(fā)者要復(fù)用里面的邏輯,所以抽出來(lái)單獨(dú)做了一個(gè)子函數(shù),核心內(nèi)容都沒(méi)變。

然而看起來(lái)沒(méi)啥本質(zhì)區(qū)別的新代碼,卻顯示MarshalText的性能提升了40%。

怎么回事呢,因?yàn)楝F(xiàn)在MarshalText變簡(jiǎn)單了,所以能在很多地方被內(nèi)聯(lián),而appendTo本身不分配內(nèi)存,這就導(dǎo)致原先作為返回值的buf因?yàn)?code>MarshalText能內(nèi)聯(lián),編譯器發(fā)現(xiàn)它在外部調(diào)用它的地方并不需要作為返回值而且大小已知,因此適用第二節(jié)里我們說(shuō)到的情況,buf并不需要逃逸。不逃逸意味著不需要分配堆內(nèi)存,性能自然就提高了。

這當(dāng)然得賴go的內(nèi)聯(lián)優(yōu)化,它創(chuàng)造出了在c++里幾乎不可能出現(xiàn)的優(yōu)化機(jī)會(huì)(appendTo就是個(gè)包裝,還多了一個(gè)參數(shù),正常內(nèi)聯(lián)展開(kāi)后和原先的代碼幾乎不會(huì)有啥區(qū)別)。這在別的語(yǔ)言里多少有點(diǎn)反常識(shí),所以一開(kāi)始我以為提交里的描述有問(wèn)題,花了大把時(shí)間排查加測(cè)試,才想到是內(nèi)聯(lián)可能影響了逃逸分析,一個(gè)下午都浪費(fèi)在這上面了。

這類問(wèn)題太多太多,issue里就有不少,如果你不了解編譯器具體做了什么工作用了什么算法,排查解決這些問(wèn)題是很困難的。

還記得開(kāi)頭說(shuō)的么,逃逸分析是要減輕程序員的負(fù)擔(dān)的,現(xiàn)在反過(guò)來(lái)要程序員深入了解編譯器,有點(diǎn)本末倒置了。

這兩個(gè)提交最終讓我開(kāi)始重新思考開(kāi)發(fā)者需要對(duì)逃逸分析了解到多深這個(gè)問(wèn)題。

該怎么做

其實(shí)還有很多對(duì)逃逸分析的民間傳說(shuō),我懶得一一證實(shí)/證偽了。下面只說(shuō)在逃逸分析本身就混亂而復(fù)雜的情況下,作為開(kāi)發(fā)者該怎么做。

對(duì)于大多數(shù)開(kāi)發(fā)者:和標(biāo)題一樣,不要過(guò)度關(guān)注逃逸分析。逃逸分析應(yīng)該是提升你效率的翅膀而不是寫(xiě)代碼時(shí)的桎梏。

畢竟光看代碼,你很難分析出個(gè)所以然來(lái),編譯期知道大小可能會(huì)逃逸,看起來(lái)不知道大小的也可能不會(huì)逃逸,看起來(lái)相似的代碼性能卻天差地別,中間還得穿插可達(dá)性分析和一些編譯優(yōu)化,corner case多到超乎想象。寫(xiě)代碼的時(shí)候想著這些東西,效率肯定高不了。

每當(dāng)自己要想逃逸分析如何如何的時(shí)候,可以用下面的步驟幫助自己擺脫對(duì)逃逸分析的依賴:

  • 變量的生命周期是否長(zhǎng)于創(chuàng)建它的函數(shù)?
  • 如果是,那么能選用返回“值”代替返回指針嗎,函數(shù)能被內(nèi)聯(lián)或者值的尺寸比較小時(shí)復(fù)制的開(kāi)銷幾乎是可以忽略不計(jì)的;
  • 如果不是或者你發(fā)現(xiàn)設(shè)計(jì)可以修改使得變量的生命周期沒(méi)有那么長(zhǎng),則往下
  • 函數(shù)是否是性能熱點(diǎn)?
  • 如果不是那么到此為止,否則你需要用memprofile和cpuprofile來(lái)確定逃逸帶來(lái)了多少損失
  • 性能熱點(diǎn)里當(dāng)然越少逃逸越好,但如果逃逸帶來(lái)的損失本身不是很大,那么就不值得繼續(xù)往下了
  • 復(fù)用堆內(nèi)存往往比避免逃逸更簡(jiǎn)單也更直觀,試試sync.Pool之類的東西而不是想著避免逃逸
  • 到了這一步,你不得不用-gcflags=-m=2看看為什么發(fā)生逃逸了,有些原因很明顯,可以被優(yōu)化
  • 對(duì)于那些你看不懂為什么逃逸的,要么就別管了要么用go以外的手段(比如匯編)解決。
  • 求助他人也是可以的,但前提是他們不是機(jī)械式地背背八股文。

總之,遵守一些常見(jiàn)的規(guī)定比如在知道slice大小的情況下提前分配內(nèi)存、設(shè)計(jì)短小精悍的函數(shù)、少用指針等等,你幾乎沒(méi)啥研究逃逸分析的必要。

對(duì)于編譯器、標(biāo)準(zhǔn)庫(kù)、某些性能要求較高的程序的開(kāi)發(fā)者來(lái)說(shuō),了解逃逸分析是必要的。因?yàn)間o的性能不是很理想,所以得抓住一切能利用的優(yōu)化機(jī)會(huì)提升性能。比如我往標(biāo)準(zhǔn)庫(kù)塞新功能的時(shí)候就被要求過(guò)一些函數(shù)得是“零分配”的。當(dāng)然我沒(méi)有上來(lái)就研究逃逸,而是先寫(xiě)了測(cè)試并研究了profile,之后才用逃逸分析的結(jié)果做了更進(jìn)一步的優(yōu)化。

總結(jié)

這篇文章其實(shí)還有一些東西沒(méi)說(shuō),比如數(shù)組和閉包在逃逸分析的表現(xiàn)??傮w上它們的行為沒(méi)有和別的變量差太多,在看看文章的標(biāo)題——所以我不建議過(guò)度關(guān)注它們的逃逸分析。

所以說(shuō),你不應(yīng)該過(guò)度關(guān)心逃逸分析。也應(yīng)該停止背/搬運(yùn)/編寫(xiě)有關(guān)逃逸分析的八股文。

大部分人關(guān)心逃逸分析,除了面試之外就是為了性能,我常說(shuō)的是性能分析一定要結(jié)合profile和benchmark,否則憑空臆斷為了不逃逸而削足適履,不僅浪費(fèi)時(shí)間對(duì)性能問(wèn)題也沒(méi)有絲毫幫助。

話說(shuō)回來(lái),不深入了解逃逸分析和不知道有逃逸分析這東西可是兩回事,后者確實(shí)約等于go白學(xué)了。

以上就是淺析Go語(yǔ)言中的逃逸分析的詳細(xì)內(nèi)容,更多關(guān)于Go逃逸分析的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 圖文詳解Go程序如何編譯并運(yùn)行起來(lái)的

    圖文詳解Go程序如何編譯并運(yùn)行起來(lái)的

    Go語(yǔ)言這兩年在語(yǔ)言排行榜上的上升勢(shì)頭非常猛,Go語(yǔ)言雖然是靜態(tài)編譯型語(yǔ)言,但是它卻擁有腳本化的語(yǔ)法,下面這篇文章主要給大家介紹了關(guān)于Go程序如何編譯并運(yùn)行起來(lái)的相關(guān)資料,需要的朋友可以參考下
    2024-05-05
  • 一文深入探索Go語(yǔ)言中的循環(huán)結(jié)構(gòu)

    一文深入探索Go語(yǔ)言中的循環(huán)結(jié)構(gòu)

    在編程中,循環(huán)結(jié)構(gòu)扮演著重要的角色,它使我們能夠有效地重復(fù)執(zhí)行特定的代碼塊,以實(shí)現(xiàn)各種任務(wù)和邏輯,在Go語(yǔ)言中,for 是 Go 中唯一的循環(huán)結(jié)構(gòu),本文將深入探討Go語(yǔ)言中的for循環(huán)類型以及它們的用法
    2023-08-08
  • Golang?WaitGroup?底層原理及源碼解析

    Golang?WaitGroup?底層原理及源碼解析

    WaitGroup?是?Golang?中最常見(jiàn)的并發(fā)控制技術(shù)之一,它的作用我們可以簡(jiǎn)單類比為其他語(yǔ)言中多線程并發(fā)控制中的?join(),這篇文章主要介紹了Golang?WaitGroup?底層原理及源碼詳解,需要的朋友可以參考下
    2023-04-04
  • Go 庫(kù)性能分析工具pprof

    Go 庫(kù)性能分析工具pprof

    這篇文章主要為大家介紹了Go 庫(kù)性能分析工具pprof,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 利用Go語(yǔ)言初步搭建一個(gè)web應(yīng)用的教程

    利用Go語(yǔ)言初步搭建一個(gè)web應(yīng)用的教程

    這篇文章主要介紹了利用Go語(yǔ)言初步搭建一個(gè)web應(yīng)用的教程,由于很多國(guó)人盲目迷信谷歌,導(dǎo)致Go語(yǔ)言在國(guó)內(nèi)的人氣遠(yuǎn)超國(guó)外...需要的朋友可以參考下
    2015-06-06
  • Golang 操作 Kafka 如何設(shè)置消息的失效時(shí)間

    Golang 操作 Kafka 如何設(shè)置消息的失效時(shí)間

    在使用 Golang 操作 Kafka 時(shí),你可以使用 Sarama 庫(kù)來(lái)設(shè)置消息的失效時(shí)間,這篇文章主要介紹了Golang操作Kafka設(shè)置消息的失效時(shí)間,需要的朋友可以參考下
    2023-06-06
  • 詳解Go語(yǔ)言運(yùn)用廣度優(yōu)先搜索走迷宮

    詳解Go語(yǔ)言運(yùn)用廣度優(yōu)先搜索走迷宮

    廣度優(yōu)先搜索是從圖中的某一頂點(diǎn)出發(fā),遍歷每一個(gè)頂點(diǎn)時(shí),依次遍歷其所有的鄰接點(diǎn),再?gòu)倪@些鄰接點(diǎn)出發(fā),依次訪問(wèn)它們的鄰接點(diǎn),直到圖中所有被訪問(wèn)過(guò)的頂點(diǎn)的鄰接點(diǎn)都被訪問(wèn)到。然后查看圖中是否存在尚未被訪問(wèn)的頂點(diǎn),若有,則以該頂點(diǎn)為起始點(diǎn),重復(fù)上述遍歷的過(guò)程
    2021-06-06
  • 詳解Go語(yǔ)言中調(diào)度器的原理與使用

    詳解Go語(yǔ)言中調(diào)度器的原理與使用

    這篇文章主要介紹了Go語(yǔ)言運(yùn)行時(shí)調(diào)度器的實(shí)現(xiàn)原理,其中包含調(diào)度器的設(shè)計(jì)與實(shí)現(xiàn)原理、演變過(guò)程以及與運(yùn)行時(shí)調(diào)度相關(guān)的數(shù)據(jù)結(jié)構(gòu),希望對(duì)大家有所幫助
    2023-07-07
  • Go語(yǔ)言利用泛型封裝常見(jiàn)的Map操作

    Go語(yǔ)言利用泛型封裝常見(jiàn)的Map操作

    Go?語(yǔ)言在?1.18?版本中引入了泛型,這是?Go?語(yǔ)言發(fā)展的一個(gè)重要里程碑,它極大地增強(qiáng)了語(yǔ)言的表達(dá)能力和靈活性,本文將通過(guò)泛型實(shí)現(xiàn)封裝常見(jiàn)的Map操作,感興趣的可以了解下
    2025-02-02
  • golang快速實(shí)現(xiàn)網(wǎng)頁(yè)截圖的方法

    golang快速實(shí)現(xiàn)網(wǎng)頁(yè)截圖的方法

    這篇文章主要介紹了golang快速實(shí)現(xiàn)網(wǎng)頁(yè)截圖的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03

最新評(píng)論