最新国产好看的视频,伊人天堂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)是在編譯過程中自動進行的一類基本優(yōu)化之一,文章圍繞主題展開更多詳細介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言:

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

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

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

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

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

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

具體的原因如下:

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

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

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

基本知識

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

調(diào)用一個函數(shù)需要將程序計數(shù)器從指令流中的一個點跳到另一個點,這可能會導致流水線停滯。一旦進入函數(shù),通常需要一些前言來為函數(shù)的執(zhí)行準備一個新的堆??蚣埽诜祷卣{(diào)用者之前,還需要一個類似的尾聲來退掉這個框架。

Go 中的開銷

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

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

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

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

Go 里的優(yōu)化

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

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

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

Cliff Click 博士將內(nèi)聯(lián)描述為現(xiàn)代編譯器進行的優(yōu)化,因為它是常量傳播和死代碼消除等優(yōu)化的基礎。

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

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

進行內(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
}

運行這個基準可以得到以下結(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)

這些改進從何而來?

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

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

考慮到一旦 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
}

再次運行基準測試,我們看到我們手動內(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ōu)化方法。

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

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

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ù),編譯器可以消除無法到達的路徑,只留下如下代碼:

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)將表達式 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ōu)化,將代碼減少到相同的 r = i 表達式。

總結(jié)

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

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語言學習教程之結(jié)構(gòu)體的示例詳解

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

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

    Go語言原子操作atomic的使用

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

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

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

    Go 修改map slice array元素值操作

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

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

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

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

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

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

    cap 是一個常用的內(nèi)建函數(shù),它用于獲取某些數(shù)據(jù)結(jié)構(gòu)的容量,本文主要介紹了Go語言內(nèi)建函數(shù)cap的實現(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ù)集合。下面我們就一起來學習什么是Go語言之結(jié)構(gòu)體
    2021-10-10
  • Go結(jié)構(gòu)體從基礎到應用深度探索

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

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

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

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

最新評論

赣榆县| 昭通市| 阳新县| 宿州市| 从化市| 阳山县| 新乡县| 永善县| 铁岭县| 台东市| 甘孜县| 广宗县| 杭锦后旗| 项城市| 太仆寺旗| 扎囊县| 靖宇县| 奉新县| 乐业县| 通辽市| 景东| 息烽县| 岳阳市| 长春市| 泽州县| 诏安县| 唐河县| 城口县| 广安市| 临清市| 莲花县| 哈密市| 轮台县| 隆回县| 阳城县| 山东| 池州市| 库尔勒市| 茂名市| 麦盖提县| 商都县|