成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

Go中的內(nèi)聯(lián)優(yōu)化方法是什么

本篇內(nèi)容主要講解“Go中的內(nèi)聯(lián)優(yōu)化方法是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Go中的內(nèi)聯(lián)優(yōu)化方法是什么”吧!

創(chuàng)新互聯(lián)是一家專業(yè)提供九江企業(yè)網(wǎng)站建設(shè),專注與做網(wǎng)站、網(wǎng)站制作、HTML5建站、小程序制作等業(yè)務(wù)。10年已為九江眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡(luò)公司優(yōu)惠進(jìn)行中。

內(nèi)聯(lián)是什么?

內(nèi)聯(lián)inlining就是把簡(jiǎn)短的函數(shù)在調(diào)用它的地方展開。在計(jì)算機(jī)發(fā)展歷程的早期,這個(gè)優(yōu)化是由程序員手動(dòng)實(shí)現(xiàn)的。現(xiàn)在,內(nèi)聯(lián)已經(jīng)成為編譯過(guò)程中自動(dòng)實(shí)現(xiàn)的基本優(yōu)化過(guò)程的其中一步。

為什么內(nèi)聯(lián)很重要?

有兩個(gè)原因。第一個(gè)是它消除了函數(shù)調(diào)用本身的開銷。第二個(gè)是它使得編譯器能更高效地執(zhí)行其他的優(yōu)化策略。

函數(shù)調(diào)用的開銷

在任何語(yǔ)言中,調(diào)用一個(gè)函數(shù) 1 都會(huì)有消耗。把參數(shù)編組進(jìn)寄存器或放入棧中(取決于 ABI),在返回結(jié)果時(shí)的逆反過(guò)程都會(huì)有開銷。引入一次函數(shù)調(diào)用會(huì)導(dǎo)致程序計(jì)數(shù)器從指令流的一點(diǎn)跳到另一點(diǎn),這可能導(dǎo)致管道滯后。函數(shù)內(nèi)部通常有前置處理preamble,需要為函數(shù)執(zhí)行準(zhǔn)備新的棧幀,還有與前置相似的后續(xù)處理epilogue,需要在返回給調(diào)用方之前釋放棧幀空間。

在 Go 中函數(shù)調(diào)用會(huì)消耗額外的資源來(lái)支持棧的動(dòng)態(tài)增長(zhǎng)。在進(jìn)入函數(shù)時(shí),goroutine 可用的??臻g與函數(shù)需要的空間大小進(jìn)行比較。如果可用空間不同,前置處理就會(huì)跳到運(yùn)行時(shí)runtime的邏輯中,通過(guò)把數(shù)據(jù)復(fù)制到一塊新的、更大的空間的來(lái)增長(zhǎng)棧空間。當(dāng)這個(gè)復(fù)制完成后,運(yùn)行時(shí)就會(huì)跳回到原來(lái)的函數(shù)入口,再執(zhí)行棧空間檢查,現(xiàn)在通過(guò)了檢查,函數(shù)調(diào)用繼續(xù)執(zhí)行。這種方式下,goroutine 開始時(shí)可以申請(qǐng)很小的??臻g,在有需要時(shí)再申請(qǐng)更大的空間。2

這個(gè)檢查消耗很小,只有幾個(gè)指令,而且由于 goroutine  的棧是成幾何級(jí)數(shù)增長(zhǎng)的,因此這個(gè)檢查很少失敗。這樣,現(xiàn)代處理器的分支預(yù)測(cè)單元可以通過(guò)假定檢查肯定會(huì)成功來(lái)隱藏棧空間檢查的消耗。當(dāng)處理器預(yù)測(cè)錯(cuò)了??臻g檢查,不得不放棄它在推測(cè)性執(zhí)行所做的操作時(shí),與為了增加  goroutine 的棧空間運(yùn)行時(shí)所需的操作消耗的資源相比,管道滯后的代價(jià)更小。

雖然現(xiàn)代處理器可以用預(yù)測(cè)性執(zhí)行技術(shù)優(yōu)化每次函數(shù)調(diào)用中的泛型和 Go  特定的元素的開銷,但那些開銷不能被完全消除,因此在每次函數(shù)調(diào)用執(zhí)行必要的工作過(guò)程中都會(huì)有性能消耗。一次函數(shù)調(diào)用本身的開銷是固定的,與更大的函數(shù)相比,調(diào)用小函數(shù)的代價(jià)更大,因?yàn)樵诿看握{(diào)用過(guò)程中它們做的有用的工作更少。

因此,消除這些開銷的方法必須是要消除函數(shù)調(diào)用本身,Go 的編譯器就是這么做的,在某些條件下通過(guò)用函數(shù)的內(nèi)容來(lái)替換函數(shù)調(diào)用來(lái)實(shí)現(xiàn)。這個(gè)過(guò)程被稱為內(nèi)聯(lián),因?yàn)樗诤瘮?shù)調(diào)用處把函數(shù)體展開了。

改進(jìn)的優(yōu)化機(jī)會(huì)

Cliff Click 博士把內(nèi)聯(lián)描述為現(xiàn)代編譯器做的優(yōu)化措施,像常量傳播(LCTT 譯注:此處作者筆誤,原文為 constant  proportion,修正為 constant  propagation)和死代碼消除一樣,都是編譯器的基本優(yōu)化方法。實(shí)際上,內(nèi)聯(lián)可以讓編譯器看得更深,使編譯器可以觀察調(diào)用的特定函數(shù)的上下文內(nèi)容,可以看到能繼續(xù)簡(jiǎn)化或徹底消除的邏輯。由于可以遞歸地執(zhí)行內(nèi)聯(lián),因此不僅可以在每個(gè)獨(dú)立的函數(shù)上下文處進(jìn)行這種優(yōu)化決策,也可以在整個(gè)函數(shù)調(diào)用鏈中進(jìn)行。

實(shí)踐中的內(nèi)聯(lián)

下面這個(gè)例子可以演示內(nèi)聯(lián)的影響:

package main import "testing" //go:noinlinefunc max(a, b int) int {    if a > b {        return a    }    return b} var Result int func BenchmarkMax(b *testing.B) {    var r int    for i := 0; i < b.N; i++ {        r = max(-1, i)    }    Result = r}

運(yùn)行這個(gè)基準(zhǔn),會(huì)得到如下結(jié)果:3

% go test -bench=. BenchmarkMax-4   530687617         2.24 ns/op

在我的 2015 MacBook Air 上 max(-1, i) 的耗時(shí)約為 2.24 納秒。現(xiàn)在去掉 //go:noinline 編譯指令,再看下結(jié)果:

% go test -bench=. BenchmarkMax-4   1000000000         0.514 ns/op

從 2.24 納秒降到了 0.51 納秒,或者從 benchstat 的結(jié)果可以看出,有 78% 的提升。

% benchstat {old,new}.txtname   old time/op  new time/op  deltaMax-4  2.21ns &plusmn; 1%  0.49ns &plusmn; 6%  -77.96%  (p=0.000 n=18+19)

這個(gè)提升是從哪兒來(lái)的呢?

首先,移除掉函數(shù)調(diào)用以及與之關(guān)聯(lián)的前置處理 4 是主要因素。把 max 函數(shù)的函數(shù)體在調(diào)用處展開,減少了處理器執(zhí)行的指令數(shù)量并且消除了一些分支。

現(xiàn)在由于編譯器優(yōu)化了 BenchmarkMax,因此它可以看到 max 函數(shù)的內(nèi)容,進(jìn)而可以做更多的提升。當(dāng) max 被內(nèi)聯(lián)后,BenchmarkMax 呈現(xiàn)給編譯器的樣子,看起來(lái)是這樣的:

func BenchmarkMax(b *testing.B) {    var r int    for i := 0; i < b.N; i++ {        if -1 > i {            r = -1        } else {            r = i        }    }    Result = r}

再運(yùn)行一次基準(zhǔn),我們看一下手動(dòng)內(nèi)聯(lián)的版本和編譯器內(nèi)聯(lián)的版本的表現(xiàn):

% benchstat {old,new}.txtname   old time/op  new time/op  deltaMax-4  2.21ns &plusmn; 1%  0.48ns &plusmn; 3%  -78.14%  (p=0.000 n=18+18)

現(xiàn)在編譯器能看到在 BenchmarkMax 里內(nèi)聯(lián) max 的結(jié)果,可以執(zhí)行以前不能執(zhí)行的優(yōu)化措施。例如,編譯器注意到 i 初始值為 0,僅做自增操作,因此所有與 i 的比較都可以假定 i 不是負(fù)值。這樣條件表達(dá)式 -1 > i 永遠(yuǎn)不是 true。5

證明了 -1 > i 永遠(yuǎn)不為 true 后,編譯器可以把代碼簡(jiǎn)化為:

func BenchmarkMax(b *testing.B) {    var r int    for i := 0; i < b.N; i++ {        if false {            r = -1        } else {            r = i        }    }    Result = r}

并且因?yàn)榉种Ю锸莻€(gè)常量,編譯器可以通過(guò)下面的方式移除不會(huì)走到的分支:

func BenchmarkMax(b *testing.B) {    var r int    for i := 0; i < b.N; i++ {        r = i    }    Result = r}

這樣,通過(guò)內(nèi)聯(lián)和由內(nèi)聯(lián)解鎖的優(yōu)化過(guò)程,編譯器把表達(dá)式 r = max(-1, i)) 簡(jiǎn)化為 r = i

內(nèi)聯(lián)的限制

本文中我論述的內(nèi)聯(lián)稱作葉子內(nèi)聯(lián)leaf inlining:把函數(shù)調(diào)用棧中最底層的函數(shù)在調(diào)用它的函數(shù)處展開的行為。內(nèi)聯(lián)是個(gè)遞歸的過(guò)程,當(dāng)把函數(shù)內(nèi)聯(lián)到調(diào)用它的函數(shù) A 處后,編譯器會(huì)把內(nèi)聯(lián)后的結(jié)果代碼再內(nèi)聯(lián)到 A 的調(diào)用方,這樣持續(xù)內(nèi)聯(lián)下去。例如,下面的代碼:

func BenchmarkMaxMaxMax(b *testing.B) {    var r int    for i := 0; i < b.N; i++ {        r = max(max(-1, i), max(0, i))    }    Result = r}

與之前的例子中的代碼運(yùn)行速度一樣快,因?yàn)榫幾g器可以對(duì)上面的代碼重復(fù)地進(jìn)行內(nèi)聯(lián),也把代碼簡(jiǎn)化到 r = i 表達(dá)式。

到此,相信大家對(duì)“Go中的內(nèi)聯(lián)優(yōu)化方法是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

文章標(biāo)題:Go中的內(nèi)聯(lián)優(yōu)化方法是什么
標(biāo)題URL:http://jinyejixie.com/article12/poeegc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營(yíng)銷型網(wǎng)站建設(shè)品牌網(wǎng)站制作、商城網(wǎng)站網(wǎng)站收錄、、全網(wǎng)營(yíng)銷推廣

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)
肥西县| 清远市| 博白县| 宜都市| 区。| 赤峰市| 新郑市| 荆州市| 沂南县| 石屏县| 南木林县| 饶阳县| 阿克陶县| 寻甸| 肇州县| 肃北| 宜章县| 雷州市| 江阴市| 温泉县| 乐陵市| 耿马| 高淳县| 宾阳县| 栾城县| 克拉玛依市| 黄大仙区| 白玉县| 庆阳市| 五寨县| 抚顺市| 福清市| 诸城市| 昆明市| 乐山市| 昂仁县| 肇东市| 饶河县| 九台市| 渭源县| 竹溪县|