• <em id="6vhwh"><rt id="6vhwh"></rt></em>

    <style id="6vhwh"></style>

    <style id="6vhwh"></style>
    1. <style id="6vhwh"></style>
        <sub id="6vhwh"><p id="6vhwh"></p></sub>
        <p id="6vhwh"></p>
          1. 国产亚洲欧洲av综合一区二区三区 ,色爱综合另类图片av,亚洲av免费成人在线,久久热在线视频精品视频,成在人线av无码免费,国产精品一区二区久久毛片,亚洲精品成人片在线观看精品字幕 ,久久亚洲精品成人av秋霞

            000006be(000006be無法安裝)

            更新時間:2023-03-01 16:54:44 閱讀: 評論:0

            前言

            今天與大家聊一聊 Go 語言的函數調用慣例,調用慣例是調用方和被調用方對于參數和返回值傳遞的約定,Go語言的調用慣例在1.17版本進行了優化,本文我們就看一下兩個版本的調用慣例是什么樣的吧~。

            1.17版本前棧傳遞

            在 Go1.17 版本之前, Go 語言函數調用是通過系統來傳遞的,我們使用 Go1.12 版本寫個例子來看一下:

            package mainfunc Test(a, b int) (int, int) { return a + b, a - b}func main() { Test(10, 20)}

            執行 go tool compile -S -N -l main.go 可以看到其匯編指令,我們分兩部分來看,先看 main 函數部分:

            "".main STEXT size=68 args=0x0 locals=0x28 0x0000 00000 (main.go:7) TEXT "".main(SB), ABIInternal, $40-0 0x0000 00000 (main.go:7) MOVQ (TLS), CX 0x0009 00009 (main.go:7) CMPQ SP, 16(CX) 0x000d 00013 (main.go:7) JLS 61 0x000f 00015 (main.go:7) SUBQ $40, SP // 分配40字節棧空間 0x0013 00019 (main.go:7) MOVQ BP, 32(SP) // 基址指針存儲到棧上 0x0018 00024 (main.go:7) LEAQ 32(SP), BP 0x001d 00029 (main.go:7) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (main.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (main.go:7) FUNCDATA $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (main.go:8) PCDATA $2, $0 0x001d 00029 (main.go:8) PCDATA $0, $0 0x001d 00029 (main.go:8) MOVQ $10, (SP) // 第一個參數壓棧 0x0025 00037 (main.go:8) MOVQ $20, 8(SP) // 第二個參數壓棧 0x002e 00046 (main.go:8) CALL "".Test(SB) // 調用函數Test 0x0033 00051 (main.go:9) MOVQ 32(SP), BP // Test函數返回后恢復棧基址指針 0x0038 00056 (main.go:9) ADDQ $40, SP // 銷毀40字節棧內存 0x003c 00060 (main.go:9) RET // 返回 0x003d 00061 (main.go:9) NOP 0x003d 00061 (main.go:7) PCDATA $0, $-1 0x003d 00061 (main.go:7) PCDATA $2, $-1 0x003d 00061 (main.go:7) CALL runtime.morestack_noctxt(SB) 0x0042 00066 (main.go:7) JMP 0 0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 2e 48 eH..%....H;a.v.H 0x0010 83 ec 28 48 89 6c 24 20 48 8d 6c 24 20 48 c7 04 ..(H.l$ H.l$ H.. 0x0020 24 0a 00 00 00 48 c7 44 24 08 14 00 00 00 e8 00 $....H.D$....... 0x0030 00 00 00 48 8b 6c 24 20 48 83 c4 28 c3 e8 00 00 ...H.l$ H..(.... 0x0040 00 00 eb bc .... rel 5+4 t=16 TLS+0 rel 47+4 t=8 "".Test+0 rel 62+4 t=8 runtime.morestack_noctxt+0

            通過上面的匯編指令我們可以分析出,參數 10 、 20 按照從右向左進行壓棧,所以第一個參數在棧頂的位置 SP~SP+8 ,第二個參數存儲在 SP+8 ~ SP+16 ,參數準備完畢后就去調用 TEST 函數,對應的匯編指令: CALL "".Test(SB) ,對應的匯編指令如下:

            "".Test STEXT nosplit size=49 args=0x20 locals=0x0 0x0000 00000 (main.go:3) TEXT "".Test(SB), NOSPLIT|ABIInternal, $0-32 0x0000 00000 (main.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (main.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (main.go:3) FUNCDATA $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (main.go:3) PCDATA $2, $0 0x0000 00000 (main.go:3) PCDATA $0, $0 0x0000 00000 (main.go:3) MOVQ $0, "".~r2+24(SP)// SP+16 ~ SP+24 存儲第一個返回值 0x0009 00009 (main.go:3) MOVQ $0, "".~r3+32(SP)// SP+24 ~ SP+32 存儲第二個返回值 0x0012 00018 (main.go:4) MOVQ "".a+8(SP), AX // 第一個參數放入AX寄存器 AX = 10 0x0017 00023 (main.go:4) ADDQ "".b+16(SP), AX // 第二個參數放入AX寄存器做加法 AX = AX + 20 = 30 0x001c 00028 (main.go:4) MOVQ AX, "".~r2+24(SP)// AX寄存器中的值在存回棧中:24(SP) 0x0021 00033 (main.go:4) MOVQ "".a+8(SP), AX// 第一個參數放入AX寄存器 AX = 10 0x0026 00038 (main.go:4) SUBQ "".b+16(SP), AX// 第二個參數放入AX寄存器做減法 AX = AX - 20 = -10 0x002b 00043 (main.go:4) MOVQ AX, "".~r3+32(SP)// AX寄存器中的值在存回棧中:32(SP) 0x0030 00048 (main.go:4) RET // 函數返回

            通過以上的匯編指令我們可以得出結論: Go 語言使用棧傳遞參數和接收返回值,多個返回值也是通過多分配一些內存來完成的。

            這種基于函數傳遞參數和接收返回值的設計大大降低了實現的復雜度,但是犧牲了函數調用的性能,像 C 語言采用同時使用棧和寄存器傳遞參數,在性能上是優于 Go 語言的,下面我們就來看一看 Go1.17 引入的寄存器傳參。

            為什么寄存器傳參性能優于棧傳參

            我們都知道 CPU 是一臺計算機的運算核心和控制核心,其主要功能是解釋計算機指令以及處理計算機軟件中的數據, CPU 大致內部結構如下:

            圖片來自于網絡

            主要由運算器和控制器組成,運算器負責完成算術運算和邏輯運算,寄存器臨時保存將要被運算器處理的數據和處理后的結果,回到主題上,寄存器是 CPU 內部組件,存儲一般在外部, CPU 操作寄存器與讀取內存的速度差距是數量級別的,當要進行數據計算時,如果數據處于內存中, CPU 需要先將數據從內存拷貝到寄存器進行計算,所以對于棧傳遞參數與接收返回值這種調用規約,每次計算都需要從內存拷貝到寄存器,計算完畢在拷貝回內存,如果使用寄存器傳參的話,參數就已經按順序放在特定寄存器了,這樣就減少了內存和寄存器之間的數據拷貝,從而改善了性能,提供程序運行效率。

            既然寄存器傳參性能高于棧傳遞參數,為什么所有語言不都使用寄存器傳遞參數呢?因為不同架構上的寄存器差異不同,所以要支持寄存器傳參就要在編譯器上進行支持,這要就使編譯器變得更加復雜且不易維護,并且寄存器的數量也是有限的,還要考慮超過寄存器數量的參數應該如何傳遞。

            1.17基于寄存器傳遞

            Go 語言在 1.17 版本設計了一套基于寄存器傳參的調用規約,目前也只支持 x86 平臺,我們也是通過一個簡單的例子來看一下:

            func Test(a, b, c, d int) (int,int,int,int) { return a, b, c, d}func main() { Test(1, 2, 3 ,4)}

            執行 go tool compile -S -N -l main.go 可以看到其匯編指令,我們分兩部分來看,先看 main 函數部分:

            "".main STEXT size=62 args=0x0 locals=0x28 funcid=0x0 0x0000 00000 (main.go:7) TEXT "".main(SB), ABIInternal, $40-0 0x0000 00000 (main.go:7) CMPQ SP, 16(R14) 0x0004 00004 (main.go:7) PCDATA $0, $-2 0x0004 00004 (main.go:7) JLS 55 0x0006 00006 (main.go:7) PCDATA $0, $-1 0x0006 00006 (main.go:7) SUBQ $40, SP// 分配40字節棧空間,基址指針存儲到棧上 0x000a 00010 (main.go:7) MOVQ BP, 32(SP)// 基址指針存儲到棧上 0x000f 00015 (main.go:7) LEAQ 32(SP), BP 0x0014 00020 (main.go:7) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0014 00020 (main.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0014 00020 (main.go:8) MOVL $1, AX // 參數1使用AX寄存器傳遞 0x0019 00025 (main.go:8) MOVL $2, BX // 參數2使用BX寄存器傳遞 0x001e 00030 (main.go:8) MOVL $3, CX // 參數3使用CX寄存器傳遞 0x0023 00035 (main.go:8) MOVL $4, DI // 參數4使用DI寄存器傳遞 0x0028 00040 (main.go:8) PCDATA $1, $0 0x0028 00040 (main.go:8) CALL "".Test(SB) // 調用Test函數 0x002d 00045 (main.go:9) MOVQ 32(SP), BP // Test函數返回后恢復棧基址指針 0x0032 00050 (main.go:9) ADDQ $40, SP // 銷毀40字節棧內存 0x0036 00054 (main.go:9) RET // 返回

            通過上面的匯編指令我們可以分析出,現在參數已經不是從右向左進行壓縮了,參數直接在寄存器上了,參數準備完畢后就去調用 TEST 函數,對應的匯編指令: CALL "".Test(SB) ,對應的匯編指令如下:

            "".Test STEXT nosplit size=133 args=0x20 locals=0x28 funcid=0x0 0x0000 00000 (main.go:3) TEXT "".Test(SB), NOSPLIT|ABIInternal, $40-32 0x0000 00000 (main.go:3) SUBQ $40, SP 0x0004 00004 (main.go:3) MOVQ BP, 32(SP) 0x0009 00009 (main.go:3) LEAQ 32(SP), BP 0x000e 00014 (main.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x000e 00014 (main.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x000e 00014 (main.go:3) FUNCDATA $5, "".Test.arginfo1(SB)0x000e 00014 (main.go:3) MOVQ AX, "".a+48(SP) // 從寄存器AX獲取參數 1 放入棧中 48(SP)0x0013 00019 (main.go:3) MOVQ BX, "".b+56(SP) // 從寄存器BX獲取參數 2 放入棧中 56(SP)0x0018 00024 (main.go:3) MOVQ CX, "".c+64(SP) // 從寄存器CX獲取參數 3 放入棧中 64(SP)0x001d 00029 (main.go:3) MOVQ DI, "".d+72(SP) // 從寄存器DI獲取參數 4 放入棧中 72(SP) 0x0022 00034 (main.go:3) MOVQ $0, "".~r4+24(SP) 0x002b 00043 (main.go:3) MOVQ $0, "".~r5+16(SP) 0x0034 00052 (main.go:3) MOVQ $0, "".~r6+8(SP) 0x003d 00061 (main.go:3) MOVQ $0, "".~r7(SP) 0x0045 00069 (main.go:4) MOVQ "".a+48(SP), DX // 以下操作是返回值放到寄存器中返回 0x004a 00074 (main.go:4) MOVQ DX, "".~r4+24(SP) 0x004f 00079 (main.go:4) MOVQ "".b+56(SP), DX 0x0054 00084 (main.go:4) MOVQ DX, "".~r5+16(SP) 0x0059 00089 (main.go:4) MOVQ "".c+64(SP), DX 0x005e 00094 (main.go:4) MOVQ DX, "".~r6+8(SP) 0x0063 00099 (main.go:4) MOVQ "".d+72(SP), DI 0x0068 00104 (main.go:4) MOVQ DI, "".~r7(SP) 0x006c 00108 (main.go:4) MOVQ "".~r4+24(SP), AX 0x0071 00113 (main.go:4) MOVQ "".~r5+16(SP), BX 0x0076 00118 (main.go:4) MOVQ "".~r6+8(SP), CX 0x007b 00123 (main.go:4) MOVQ 32(SP), BP 0x0080 00128 (main.go:4) ADDQ $40, SP 0x0084 00132 (main.go:4) RET

            傳參和返回都采用了寄存器進行傳遞,并且返回值和輸入都使用了完全相同的寄存器序列,并且使用的順序也是一致的。

            因為這個優化,在一些函數調用嵌套層次較深的場景下,內存有一定概率會降低,有機會做壓測可以試一試~。

            總結

            熟練掌握并理解函數的調用過程是我們深入學習 Go 語言的重要一課,看完本文希望你已經熟練掌握了函數的調用慣例~。

            本文發布于:2023-02-28 20:11:00,感謝您對本站的認可!

            本文鏈接:http://www.newhan.cn/zhishi/a/167766088480929.html

            版權聲明:本站內容均來自互聯網,僅供演示用,請勿用于商業和其他非法用途。如果侵犯了您的權益請與我們聯系,我們將在24小時內刪除。

            本文word下載地址:000006be(000006be無法安裝).doc

            本文 PDF 下載地址:000006be(000006be無法安裝).pdf

            標簽:
            相關文章
            留言與評論(共有 0 條評論)
               
            驗證碼:
            推薦文章
            排行榜
            Copyright ?2019-2022 Comsenz Inc.Powered by ? 實用文體寫作網旗下知識大全大全欄目是一個全百科類寶庫! 優秀范文|法律文書|專利查詢|
            主站蜘蛛池模板: 伦精品一区二区三区视频| 精品视频一区二区福利午夜| 免费看国产精品3a黄的视频| 亚洲一区成人av在线| 久久人妻少妇嫩草av无码专区 | 亚洲一区二区三区丝袜| 日本九州不卡久久精品一区| 久久69国产精品久久69软件| 五月婷婷导航| 男人狂桶女人出白浆免费视频| 国产午夜福利小视频在线| 自拍视频一区二区三区四区| 厨房与子乱在线观看| 久久精品熟女亚洲av艳妇| 日本做受高潮好舒服视频| 国产成 人 综合 亚洲奶水| 少妇人妻偷人偷人精品| 亚洲人成色7777在线观看| 九九久久亚洲精品美国国内| 在线 欧美 中文 亚洲 精品| 鲁丝片一区二区三区免费| 国产偷窥熟女高潮精品视频| 闷骚的老熟女人15p| 国产一国产精品免费播放| 免费人成在线观看网站| 亚洲人黑人一区二区三区| 国产偷窥熟女精品视频大全| 国产99视频精品免费视频6| 亚洲国产成人久久综合三区| XXXXXHD亚洲日本HD| 国产精品中文字幕二区| 看国产黄大片在线观看| 亚洲一区二区三区18禁| 老汉色老汉首页a亚洲| 风骚少妇久久精品在线观看| 永久免费av无码网站直播| 久久精品国产亚洲精品色婷婷| 在线精品国产成人综合| 91精品蜜臀国产综合久久| 亚洲成人av综合一区| 国产成人精品视频一区二区三|