亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go語言defer語句的三種機制整理

 更新時間:2020年03月01日 14:32:01   作者:面向人生編程  
在本篇文章里小編給大家分享的是一篇關于Go語言defer語句的三種機制整理,需要的朋友們學習下吧。

Golang 的 1.13 版本 與 1.14 版本對 defer 進行了兩次優(yōu)化,使得 defer 的性能開銷在大部分場景下都得到大幅降低,其中到底經(jīng)歷了什么原理?

這是因為這兩個版本對 defer 各加入了一項新的機制,使得 defer 語句在編譯時,編譯器會根據(jù)不同版本與情況,對每個 defer 選擇不同的機制,以更輕量的方式運行調(diào)用。

堆上分配

在 Golang 1.13 之前的版本中,所有 defer 都是在堆上分配,該機制在編譯時會進行兩個步驟:

  1. 在 defer 語句的位置插入 runtime.deferproc,當被執(zhí)行時,延遲調(diào)用會被保存為一個 _defer 記錄,并將被延遲調(diào)用的入口地址及其參數(shù)復制保存,存入 Goroutine 的調(diào)用鏈表中。
  2. 在函數(shù)返回之前的位置插入 runtime.deferreturn,當被執(zhí)行時,會將延遲調(diào)用從 Goroutine 鏈表中取出并執(zhí)行,多個延遲調(diào)用則以 jmpdefer 尾遞歸調(diào)用方式連續(xù)執(zhí)行。

這種機制的主要性能問題存在于每個 defer 語句產(chǎn)生記錄時的內(nèi)存分配,以及記錄參數(shù)和完成調(diào)用時參數(shù)移動的系統(tǒng)調(diào)用開銷。

棧上分配

Go 1.13 版本新加入 deferprocStack 實現(xiàn)了在棧上分配的形式來取代 deferproc,相比后者,棧上分配在函數(shù)返回后 _defer 便得到釋放,省去了內(nèi)存分配時產(chǎn)生的性能開銷,只需適當維護 _defer 的鏈表即可。

編譯器有自己的邏輯去選擇使用 deferproc 還是 deferprocStack,大部分情況下都會使用后者,性能會提升約 30%。不過在 defer 語句出現(xiàn)在了循環(huán)語句里,或者無法執(zhí)行更高階的編譯器優(yōu)化時,亦或者同一個函數(shù)中使用了過多的 defer 時,依然會使用 deferproc。

開放編碼

Go 1.14 版本繼續(xù)加入了開發(fā)編碼(open coded),該機制會將延遲調(diào)用直接插入函數(shù)返回之前,省去了運行時的 deferproc 或 deferprocStack 操作,在運行時的 deferreturn 也不會進行尾遞歸調(diào)用,而是直接在一個循環(huán)中遍歷所有延遲函數(shù)執(zhí)行。

這種機制使得 defer 的開銷幾乎可以忽略,唯一的運行時成本就是存儲參與延遲調(diào)用的相關信息,不過使用此機制需要一些條件:

  1. 沒有禁用編譯器優(yōu)化,即沒有設置 -gcflags "-N";
  2. 函數(shù)內(nèi) defer 的數(shù)量不超過 8 個,且返回語句與延遲語句個數(shù)的乘積不超過 15;
  3. defer 不是在循環(huán)語句中。

該機制還引入了一種元素 —— 延遲比特(defer bit),用于運行時記錄每個 defer 是否被執(zhí)行(尤其是在條件判斷分支中的 defer),從而便于判斷最后的延遲調(diào)用該執(zhí)行哪些函數(shù)。

延遲比特的原理:

同一個函數(shù)內(nèi)每出現(xiàn)一個 defer 都會為其分配 1 個比特,如果被執(zhí)行到則設為 1,否則設為 0,當?shù)竭_函數(shù)返回之前需要判斷延遲調(diào)用時,則用掩碼判斷每個位置的比特,若為 1 則調(diào)用延遲函數(shù),否則跳過。

為了輕量,官方將延遲比特限制為 1 個字節(jié),即 8 個比特,這就是為什么不能超過 8 個 defer 的原因,若超過依然會選擇堆棧分配,但顯然大部分情況不會超過 8 個。

用代碼演示如下:

deferBits = 0 // 延遲比特初始值 00000000

deferBits |= 1<<0 // 執(zhí)行第一個 defer,設置為 00000001
_f1 = f1 // 延遲函數(shù)
_a1 = a1 // 延遲函數(shù)的參數(shù)
if cond {
  // 如果第二個 defer 被執(zhí)行,則設置為 00000011,否則依然為 00000001
  deferBits |= 1<<1
  _f2 = f2
  _a2 = a2
}
...
exit:
// 函數(shù)返回之前,倒序檢查延遲比特,通過掩碼逐位進行與運算,來判斷是否調(diào)用函數(shù)

// 假如 deferBits 為 00000011,則 00000011 & 00000010 != 0,因此調(diào)用 f2
// 否則 00000001 & 00000010 == 0,不調(diào)用 f2
if deferBits & 1<<1 != 0 {
  deferBits &^= 1<<1 // 移位為下次判斷準備
  _f2(_a2)
}
// 同理,由于 00000001 & 00000001 != 0,調(diào)用 f1
if deferBits && 1<<0 != 0 {
  deferBits &^= 1<<0
  _f1(_a1)
}

總結(jié)

以往 Golang defer 語句的性能問題一直飽受詬病,最近正式發(fā)布的 1.14 版本終于為這個爭議畫上了階段性的句號。如果不是在特殊情況下,我們不需要再計較 defer 的性能開銷。

參考資料

[1] Ou Changkun - Go 語言原本

[2] 峰云就她了 - go1.14實現(xiàn)defer性能大幅度提升原理

[3] 34481-opencoded-defers

到此這篇關于Go語言defer語句的三種機制整理的文章就介紹到這了,更多相關探究Go語言defer語句的三種機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 一文帶你了解Go中的內(nèi)存對齊

    一文帶你了解Go中的內(nèi)存對齊

    一旦涉及到較為底層的編程,特別是與硬件交互,內(nèi)存對齊是一個必修的課題,所以這篇文章小編就想來和大家聊一聊Go語言中的內(nèi)存對齊,希望對大家有所幫助
    2023-10-10
  • 詳解Go語言如何實現(xiàn)并發(fā)安全的map

    詳解Go語言如何實現(xiàn)并發(fā)安全的map

    go語言提供的數(shù)據(jù)類型中,只有channel是并發(fā)安全的,基礎map并不是并發(fā)安全的,本文為大家整理了三種實現(xiàn)了并發(fā)安全的map的方案,有需要的可以參考下
    2023-12-12
  • Go語言實現(xiàn)UDP協(xié)議及TCP通訊

    Go語言實現(xiàn)UDP協(xié)議及TCP通訊

    這篇文章介紹了Go語言實現(xiàn)UDP協(xié)議及TCP通訊的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • Go語言range關鍵字循環(huán)時的坑

    Go語言range關鍵字循環(huán)時的坑

    今天小編就為大家分享一篇關于Go語言range關鍵字循環(huán)時的坑,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • go語言實現(xiàn)字符串base64編碼的方法

    go語言實現(xiàn)字符串base64編碼的方法

    這篇文章主要介紹了go語言實現(xiàn)字符串base64編碼的方法,實例分析了Go語言操作字符串的技巧及base64編碼的使用技巧,需要的朋友可以參考下
    2015-03-03
  • 十個Golang開發(fā)中應該避免的錯誤總結(jié)

    十個Golang開發(fā)中應該避免的錯誤總結(jié)

    Go是一種靜態(tài)類型的、并發(fā)的、垃圾收集的編程語言,由谷歌開發(fā)。開發(fā)人員在編寫Go代碼時總會有一些常見的錯誤,下面是Go語言中需要避免的十大壞錯誤,希望對大家有所幫助
    2023-03-03
  • 詳解如何保留Go程序崩潰現(xiàn)場

    詳解如何保留Go程序崩潰現(xiàn)場

    這篇文章主要為大家介紹了如何保留Go程序崩潰現(xiàn)場示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • GoLang基礎學習之go?test測試

    GoLang基礎學習之go?test測試

    相信每位編程開發(fā)者們應該都知道,Golang作為一門標榜工程化的語言,提供了非常簡便、實用的編寫單元測試的能力,下面這篇文章主要給大家介紹了關于GoLang基礎學習之go?test測試的相關資料,需要的朋友可以參考下
    2022-08-08
  • Go語言中的通道chan使用指南

    Go語言中的通道chan使用指南

    Go語言的通道chan是實現(xiàn)并發(fā)編程的關鍵工具,主要用于goroutine之間的數(shù)據(jù)傳輸,本文主要介紹了通道的基本操作如創(chuàng)建、發(fā)送、接收和關閉數(shù)據(jù),以及使用select語句進行多路復用和超時控制,感興趣的可以了解一下
    2024-10-10
  • GoLang中Module的基本使用方法

    GoLang中Module的基本使用方法

    Go module是從Go 1.11版本才引入的新功能,下面這篇文章主要給大家介紹了關于GoLang中Module的基本使用方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-01-01

最新評論