最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go?內(nèi)聯(lián)優(yōu)化讓程序員愛不釋手

 更新時間:2022年06月21日 08:23:10   作者:??煎魚eddycjy????  
這篇文章主要介紹了Go?內(nèi)聯(lián)優(yōu)化讓程序員愛不釋手,內(nèi)聯(lián)是在編譯過程中自動進(jìn)行的一類基本優(yōu)化之一,文章圍繞主題展開更多詳細(xì)介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言:

這是一篇介紹 Go 編譯器如何實(shí)現(xiàn)內(nèi)聯(lián)的文章,以及這種優(yōu)化將如何影響你的 Go 代碼。

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

內(nèi)聯(lián)是將較小的函數(shù)合并到它們各自的調(diào)用者中的行為。其在不同的計(jì)算歷史時期的做法不一樣,如下:

  • 早期:這種優(yōu)化通常是由手工完成的。
  • 現(xiàn)在:內(nèi)聯(lián)是在編譯過程中自動進(jìn)行的一類基本優(yōu)化之一。

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

內(nèi)聯(lián)是很重要的,每一門語言都必然會有。

具體的原因如下:

  • 它消除了函數(shù)調(diào)用本身的開銷。
  • 它允許編譯器更有效地應(yīng)用其他優(yōu)化策略。

核心來講,就是性能更好了。

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

基本知識

在任何語言中調(diào)用一個函數(shù)都是有代價的。將參數(shù)編入寄存器或堆棧(取決于ABI),并在返回時反轉(zhuǎn)這一過程,這些都是開銷。

調(diào)用一個函數(shù)需要將程序計(jì)數(shù)器從指令流中的一個點(diǎn)跳到另一個點(diǎn),這可能會導(dǎo)致流水線停滯。一旦進(jìn)入函數(shù),通常需要一些前言來為函數(shù)的執(zhí)行準(zhǔn)備一個新的堆棧框架,在返回調(diào)用者之前,還需要一個類似的尾聲來退掉這個框架。

Go 中的開銷

在 Go 中,一個函數(shù)的調(diào)用需要額外的成本來支持動態(tài)堆棧的增長。在進(jìn)入時,goroutine 可用的堆棧空間的數(shù)量與函數(shù)所需的數(shù)量進(jìn)行比較。

如果可用的堆??臻g不足,序言就會跳轉(zhuǎn)到運(yùn)行時邏輯,通過將堆棧復(fù)制到一個新的、更大的位置來增加堆棧。

一旦這樣做了,運(yùn)行時就會跳回到原始函數(shù)的起點(diǎn),再次進(jìn)行堆棧檢查,現(xiàn)在通過了,然后繼續(xù)調(diào)用。通過這種方式,goroutines可以從一個小的堆棧分配開始,只有在需要時才會增加。

這種檢查很便宜,只需要幾條指令,而且由于goroutine的堆棧以幾何級數(shù)增長,檢查很少失敗。因此,現(xiàn)代處理器中的分支預(yù)測單元可以通過假設(shè)堆棧檢查總是成功來隱藏堆棧檢查的成本。在處理器錯誤預(yù)測堆棧檢查并不得不丟棄它在投機(jī)執(zhí)行時所做的工作的情況下,與運(yùn)行時增長goroutine堆棧所需的工作成本相比,管道停滯的成本相對較小。

Go 里的優(yōu)化

雖然每個函數(shù)調(diào)用的通用組件和 Go 特定組件的開銷被使用投機(jī)執(zhí)行技術(shù)的現(xiàn)代處理器很好地優(yōu)化了,但這些開銷不能完全消除,因此每個函數(shù)調(diào)用都帶有性能成本,超過了執(zhí)行有用工作的時間。由于函數(shù)調(diào)用的開銷是固定的,較小的函數(shù)相對于較大的函數(shù)要付出更大的代價,因?yàn)樗鼈兠看握{(diào)用的有用工作往往較少。

因此,消除這些開銷的解決方案必須是消除函數(shù)調(diào)用本身,Go 編譯器在某些條件下通過用函數(shù)的內(nèi)容替換對函數(shù)的調(diào)用來做到這一點(diǎn)。這被稱為內(nèi)聯(lián),因?yàn)樗购瘮?shù)的主體與它的調(diào)用者保持一致。

改善優(yōu)化的機(jī)會

Cliff Click 博士將內(nèi)聯(lián)描述為現(xiàn)代編譯器進(jìn)行的優(yōu)化,因?yàn)樗浅A總鞑ズ退来a消除等優(yōu)化的基礎(chǔ)。

實(shí)際上,內(nèi)聯(lián)允許編譯器看得更遠(yuǎn),允許它在特定函數(shù)被調(diào)用的情況下,觀察到可以進(jìn)一步簡化或完全消除的邏輯。

由于內(nèi)聯(lián)可以遞歸應(yīng)用,優(yōu)化決策不僅可以在每個單獨(dú)的函數(shù)的上下文中做出,還可以應(yīng)用于調(diào)用路徑中的函數(shù)鏈。

進(jìn)行內(nèi)聯(lián)優(yōu)化

不允許內(nèi)聯(lián)

內(nèi)聯(lián)的效果可以通過這個小例子來證明:

package main
import "testing"
//go:noinline
func 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)行這個基準(zhǔn)可以得到以下結(jié)果:

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

從執(zhí)行結(jié)果來看,max(-1, i)的成本大約是 2.24ns,感覺性能不錯。

允許內(nèi)聯(lián)

現(xiàn)在讓我們?nèi)サ?nbsp;//go:noinline pragma 的語句,再看看不允許內(nèi)聯(lián)的情況下,性能是否會改變。

如下結(jié)果:

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

兩個結(jié)果對比一看,2.24ns 和 0.51ns。差距至少一倍以上,根據(jù) benchstat 的建議,內(nèi)聯(lián)情況下,性能提高了 78%。

如下結(jié)果:

% benchstat {old,new}.txt
name   old time/op  new time/op  delta
Max-4  2.21ns ± 1%  0.49ns ± 6%  -77.96%  (p=0.000 n=18+19)

這些改進(jìn)從何而來?

首先,取消函數(shù)調(diào)用和相關(guān)的前導(dǎo)動作是主要的改進(jìn)貢獻(xiàn)者。其將 max 函數(shù)的內(nèi)容拉到它的調(diào)用者中,減少了處理器執(zhí)行的指令數(shù)量,并消除了幾個分支。

現(xiàn)在 max 函數(shù)的內(nèi)容對編譯器來說是可見的,當(dāng)它優(yōu)化 BenchmarkMax 時,它可以做一些額外的改進(jìn)。

考慮到一旦 max 被內(nèi)聯(lián),BenchmarkMax 的主體對編譯器而言就會有所改變,與用戶端看到的并不一樣。

如下代碼:

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)測試,我們看到我們手動內(nèi)聯(lián)的版本與編譯器內(nèi)聯(lián)的版本表現(xiàn)一樣好。

如下結(jié)果:

% benchstat {old,new}.txt
name   old time/op  new time/op  delta
Max-4  2.21ns ± 1%  0.48ns ± 3%  -78.14%  (p=0.000 n=18+18)

現(xiàn)在,編譯器可以獲得 max 內(nèi)聯(lián)到 BenchmarkMax 的結(jié)果,它可以應(yīng)用以前不可能的優(yōu)化方法。

例如:編譯器注意到 i 被初始化為 0,并且只被遞增,所以任何與 i 的比較都可以假定 i 永遠(yuǎn)不會是負(fù)數(shù)。因此,條件 -1 > i 將永遠(yuǎn)不會為真。

在證明了 -1 > i 永遠(yuǎn)不會為真之后,編譯器可以將代碼簡化為:

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

并且由于該分支現(xiàn)在是一個常數(shù),編譯器可以消除無法到達(dá)的路徑,只留下如下代碼:

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

通過內(nèi)聯(lián)和它所釋放的優(yōu)化,編譯器已經(jīng)將表達(dá)式 r = max(-1, i) 簡化為 r = i。

這個例子非常不錯,很好的體現(xiàn)了內(nèi)聯(lián)的優(yōu)化過程和性能提升的緣由。

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

在這篇文章中,討論了所謂的葉子內(nèi)聯(lián):將調(diào)用棧底部的一個函數(shù)內(nèi)聯(lián)到其直接調(diào)用者中的行為。

內(nèi)聯(lián)是一個遞歸的過程,一旦一個函數(shù)被內(nèi)聯(lián)到它的調(diào)用者中,編譯器就可能將產(chǎn)生的代碼內(nèi)聯(lián)到它的調(diào)用者中,依此類推。

例如如下代碼:

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器能夠反復(fù)應(yīng)用上面的優(yōu)化,將代碼減少到相同的 r = i 表達(dá)式。

總結(jié)

這篇文章針對內(nèi)聯(lián)進(jìn)行了基本的概念介紹和分析,并且通過 Go 的例子進(jìn)行了一步步的剖析,讓大家對真實(shí)案例有了一個更貼切的理解。

Go 編譯器的優(yōu)化總是無處不在的。

到此這篇關(guān)于Go 內(nèi)聯(lián)優(yōu)化讓程序員愛不釋手的文章就介紹到這了,更多相關(guān)Go 內(nèi)聯(lián)優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言學(xué)習(xí)教程之結(jié)構(gòu)體的示例詳解

    Go語言學(xué)習(xí)教程之結(jié)構(gòu)體的示例詳解

    結(jié)構(gòu)體是一個序列,包含一些被命名的元素,這些被命名的元素稱為字段(field),每個字段有一個名字和一個類型。本文通過一些示例帶大家深入了解Go語言中結(jié)構(gòu)體的使用,需要的可以參考一下
    2022-09-09
  • Go語言原子操作atomic的使用

    Go語言原子操作atomic的使用

    本文介紹了Go語言原子操作的使用方法,原子操作是一種無鎖的技術(shù),可通過CPU指令實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-10-10
  • golang調(diào)試bug及性能監(jiān)控方式實(shí)踐總結(jié)

    golang調(diào)試bug及性能監(jiān)控方式實(shí)踐總結(jié)

    這篇文章主要為大家介紹了golang調(diào)試bug及性能監(jiān)控方式實(shí)踐是總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • Go 修改map slice array元素值操作

    Go 修改map slice array元素值操作

    這篇文章主要介紹了Go 修改map slice array元素值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • golang優(yōu)化目錄遍歷的實(shí)現(xiàn)方法

    golang優(yōu)化目錄遍歷的實(shí)現(xiàn)方法

    對于go1.16的新變化,大家印象最深的可能是io包的大規(guī)模重構(gòu),但這個重構(gòu)實(shí)際上還引進(jìn)了一個優(yōu)化,這篇文章要說的就是這個優(yōu)化,所以本將給大家介紹golang是如何優(yōu)化目錄遍歷的,需要的朋友可以參考下
    2024-08-08
  • 使用Go語言提高圖片分辨率的方法與實(shí)踐

    使用Go語言提高圖片分辨率的方法與實(shí)踐

    在圖像處理和計(jì)算機(jī)視覺領(lǐng)域,提高圖片分辨率是一個常見的問題,隨著高分辨率顯示設(shè)備的普及,如4K、8K電視以及高像素手機(jī)攝像頭的應(yīng)用,用戶對高質(zhì)量圖片的需求也越來越高,本文將介紹使用Golang語言提高圖片分辨率的方法與實(shí)踐,需要的朋友可以參考下
    2023-12-12
  • Go語言內(nèi)建函數(shù)cap的實(shí)現(xiàn)示例

    Go語言內(nèi)建函數(shù)cap的實(shí)現(xiàn)示例

    cap 是一個常用的內(nèi)建函數(shù),它用于獲取某些數(shù)據(jù)結(jié)構(gòu)的容量,本文主要介紹了Go語言內(nèi)建函數(shù)cap的實(shí)現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-08-08
  • Go語言之結(jié)構(gòu)體與方法

    Go語言之結(jié)構(gòu)體與方法

    這篇文章主要介紹了Go語言之結(jié)構(gòu)體與方法,結(jié)構(gòu)體是由一系列具有相同類型或不同類型的數(shù)據(jù)構(gòu)成的數(shù)據(jù)集合。下面我們就一起來學(xué)習(xí)什么是Go語言之結(jié)構(gòu)體
    2021-10-10
  • Go結(jié)構(gòu)體從基礎(chǔ)到應(yīng)用深度探索

    Go結(jié)構(gòu)體從基礎(chǔ)到應(yīng)用深度探索

    本文深入探討了結(jié)構(gòu)體的定義、類型、字面量表示和使用方法,旨在為讀者呈現(xiàn)Go結(jié)構(gòu)體的全面視角,通過結(jié)構(gòu)體,開發(fā)者可以實(shí)現(xiàn)更加模塊化、高效的代碼設(shè)計(jì),這篇文章旨在為您提供關(guān)于結(jié)構(gòu)體的深入理解,助您更好地利用Go語言的強(qiáng)大功能
    2023-10-10
  • Go標(biāo)準(zhǔn)庫strconv實(shí)現(xiàn)string類型與其他基本數(shù)據(jù)類型之間轉(zhuǎn)換

    Go標(biāo)準(zhǔn)庫strconv實(shí)現(xiàn)string類型與其他基本數(shù)據(jù)類型之間轉(zhuǎn)換

    這篇文章主要為大家介紹了Go標(biāo)準(zhǔn)庫strconv實(shí)現(xiàn)string類型與其他基本數(shù)據(jù)類型之間轉(zhuǎn)換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11

最新評論

星座| 通道| 临潭县| 木兰县| 米林县| 瓦房店市| 确山县| 文安县| 中方县| 曲麻莱县| 吕梁市| 许昌县| 新蔡县| 临沭县| 凭祥市| 成都市| 新田县| 河池市| 开阳县| 仁怀市| 当涂县| 江山市| 沈阳市| 怀柔区| 岳普湖县| 洛南县| 无锡市| 田东县| 武清区| 富阳市| 左权县| 徐汇区| 永寿县| 五莲县| 常熟市| 措美县| 云霄县| 贵德县| 盘锦市| 威信县| 平遥县|