Go defer 會有性能損耗,盡量不要用柑土?

image

上個月在 @polaris @軒脈刃 的全棧技術(shù)群里看到一個小伙伴問 “說 defer 在棧退出時執(zhí)行蜀肘,會有性能損耗,盡量不要用冰单,這個怎么解?”灸促。

恰好前段時間寫了一篇 《深入理解 Go defer》 去詳細(xì)剖析 defer 關(guān)鍵字诫欠。那么這一次簡單結(jié)合前文對這個問題進(jìn)行探討一波,希望對你有所幫助浴栽,但在此之前希望你花幾分鐘荒叼,自己思考一下答案,再繼續(xù)往下看典鸡。

測試

func DoDefer(key, value string) {
    defer func(key, value string) {
        _ = key + value
    }(key, value)
}

func DoNotDefer(key, value string) {
    _ = key + value
}

基準(zhǔn)測試:

func BenchmarkDoDefer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        DoDefer("煎魚", "https://github.com/EDDYCJY/blog")
    }
}

func BenchmarkDoNotDefer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        DoNotDefer("煎魚", "https://github.com/EDDYCJY/blog")
    }
}

輸出結(jié)果:

$ go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: github.com/EDDYCJY/awesomeDefer
BenchmarkDoDefer-4          20000000            91.4 ns/op        48 B/op          1 allocs/op
BenchmarkDoNotDefer-4       30000000            41.6 ns/op        48 B/op          1 allocs/op
PASS
ok      github.com/EDDYCJY/awesomeDefer 3.234s

從結(jié)果上來被廓,使用 defer 后的函數(shù)開銷確實(shí)比沒使用高了不少,這損耗用到哪里去了呢萝玷?

想一下

$ go tool compile -S main.go 
"".main STEXT size=163 args=0x0 locals=0x40
    ...
    0x0059 00089 (main.go:6)    MOVQ    AX, 16(SP)
    0x005e 00094 (main.go:6)    MOVQ    $1, 24(SP)
    0x0067 00103 (main.go:6)    MOVQ    $1, 32(SP)
    0x0070 00112 (main.go:6)    CALL    runtime.deferproc(SB)
    0x0075 00117 (main.go:6)    TESTL    AX, AX
    0x0077 00119 (main.go:6)    JNE    137
    0x0079 00121 (main.go:7)    XCHGL    AX, AX
    0x007a 00122 (main.go:7)    CALL    runtime.deferreturn(SB)
    0x007f 00127 (main.go:7)    MOVQ    56(SP), BP
    0x0084 00132 (main.go:7)    ADDQ    $64, SP
    0x0088 00136 (main.go:7)    RET
    0x0089 00137 (main.go:6)    XCHGL    AX, AX
    0x008a 00138 (main.go:6)    CALL    runtime.deferreturn(SB)
    0x008f 00143 (main.go:6)    MOVQ    56(SP), BP
    0x0094 00148 (main.go:6)    ADDQ    $64, SP
    0x0098 00152 (main.go:6)    RET
    ...

我們在前文提到 defer 關(guān)鍵字其實(shí)涉及了一系列的連鎖調(diào)用嫁乘,內(nèi)部 runtime 函數(shù)的調(diào)用就至少多了三步,分別是 runtime.deferproc 一次和 runtime.deferreturn 兩次球碉。

而這還只是在運(yùn)行時的顯式動作蜓斧,另外編譯器做的事也不少,例如:

  • deferproc 階段(注冊延遲調(diào)用)睁冬,還得獲取/傳入目標(biāo)函數(shù)地址挎春、函數(shù)參數(shù)等等。
  • deferreturn 階段,需要在函數(shù)調(diào)用結(jié)尾處插入該方法的調(diào)用直奋,同時若有被 defer 的函數(shù)能庆,還需要使用 runtime·jmpdefer 進(jìn)行跳轉(zhuǎn)以便于后續(xù)調(diào)用。

這一些動作途中還要涉及最小單元 _defer 的獲取/生成脚线, deferrecover 鏈表的邏輯處理和消耗等動作搁胆。

Q&A

最后討論的時候有提到 “問題指的是本來就是用來執(zhí)行 close() 一些操作的,然后說盡量不能用殉挽,例子就把 defer db.close() 前面的 defer 刪去了” 這個疑問丰涉。

這是一個比較類似 “教科書” 式的說法,在一些入門教程中會潛移默化的告訴你在資源控制后加個 defer 延遲關(guān)閉一下斯碌。例如:

resp, err := http.Get(...)
if err != nil {
    return err
}
defer resp.Body.Close()

但是一定得這么寫嗎一死?其實(shí)并不,很多人給出的理由都是 “怕你忘記” 這種說辭傻唾,這沒有毛病投慈。但需要認(rèn)清場景,假設(shè)我的應(yīng)用場景如下:

resp, err := http.Get(...)
if err != nil {
    return err
}
defer resp.Body.Close()
// do something
time.Sleep(time.Second * 60)

嗯冠骄,一個請求當(dāng)然沒問題伪煤,流量、并發(fā)一下子大了呢凛辣,那可能就是個災(zāi)難了抱既。你想想為什么?從常見的 defer + close 的使用組合來講扁誓,用之前建議先看清楚應(yīng)用場景防泵,在保證無異常的情況下確保盡早關(guān)閉才是首選。如果只是小范圍調(diào)用很快就返回的話蝗敢,偷個懶直接一套組合拳出去也未嘗不可捷泞。

結(jié)論

一個 defer 關(guān)鍵字實(shí)際上包含了不少的動作和處理,和你單純調(diào)用一個函數(shù)一條指令是沒法比的寿谴。而與對照物相比锁右,它確確實(shí)實(shí)是有性能損耗,目前延遲調(diào)用的全部開銷大約在 50ns讶泰,但 defer 所提供的作用遠(yuǎn)遠(yuǎn)大于此咏瑟,你從全局來看,它的損耗非常小痪署,并且官方還不斷地在優(yōu)化中响蕴。

因此,對于 “Go defer 會有性能損耗惠桃,盡量不能用浦夷?” 這個問題辖试,我認(rèn)為該用就用,應(yīng)該及時關(guān)閉就不要延遲劈狐,在 hot paths 用時一定要想清楚場景罐孝。

補(bǔ)充

最后補(bǔ)充上柴大的回復(fù):“不是性能問題,defer 最大的功能是 Panic 后依然有效肥缔。如果沒有 defer莲兢,Panic 后就會導(dǎo)致 unlock 丟失,從而導(dǎo)致死鎖了”续膳,非常經(jīng)典改艇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市坟岔,隨后出現(xiàn)的幾起案子谒兄,更是在濱河造成了極大的恐慌,老刑警劉巖社付,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件承疲,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸥咖,警方通過查閱死者的電腦和手機(jī)燕鸽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啼辣,“玉大人啊研,你說我怎么就攤上這事∨概。” “怎么了党远?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長住涉。 經(jīng)常有香客問我麸锉,道長钠绍,這世上最難降的妖魔是什么舆声? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮柳爽,結(jié)果婚禮上媳握,老公的妹妹穿的比我還像新娘。我一直安慰自己磷脯,他們只是感情好蛾找,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赵誓,像睡著了一般打毛。 火紅的嫁衣襯著肌膚如雪柿赊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天幻枉,我揣著相機(jī)與錄音碰声,去河邊找鬼。 笑死熬甫,一個胖子當(dāng)著我的面吹牛胰挑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播椿肩,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼瞻颂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了郑象?” 一聲冷哼從身側(cè)響起贡这,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扣唱,沒想到半個月后藕坯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡噪沙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年炼彪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片正歼。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡辐马,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出局义,到底是詐尸還是另有隱情喜爷,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布萄唇,位于F島的核電站檩帐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏另萤。R本人自食惡果不足惜湃密,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望四敞。 院中可真熱鬧泛源,春花似錦、人聲如沸忿危。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铺厨。三九已至缎玫,卻和暖如春硬纤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赃磨。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工咬摇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人煞躬。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓肛鹏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親恩沛。 傳聞我的和親對象是個殘疾皇子在扰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文將會講解defer, recover雷客,panic相關(guān)的知識芒珠。主要內(nèi)容包括: defer的原理panic與rec...
    鏈人成長chainerup閱讀 1,060評論 0 0
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,092評論 1 32
  • go語言defer語句的用法 defer的語法 defer后面必須是函數(shù)調(diào)用語句,不能是其他語句搅裙,否則編譯器會出錯...
    CodingCode閱讀 33,819評論 3 17
  • defer語句是Go中一個非常有用的特性皱卓,可以將一個方法延遲到包裹該方法的方法返回時執(zhí)行,在實(shí)際應(yīng)用中部逮,defer...
    simpleapples閱讀 39,961評論 2 34
  • 女生應(yīng)不應(yīng)該買花給自己 當(dāng)然應(yīng)該娜汁。 沒人愛你,你要愛你兄朋。 事情從何說起呢掐禁? 從2018年8月15日說起,這是一個會...
    劉浪小姐閱讀 231評論 0 3