<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang 函數(shù)和 C 函數(shù)深度對比

          共 7163字,需瀏覽 15分鐘

           ·

          2021-07-27 06:49

          無論是什么語言,函數(shù)都是最常被使用到的東西。

          我們對比一下 Golang 和 C 這兩種語言的函數(shù)實現(xiàn),進而我們能真正理解以下兩個問題。

          • 為什么 C 語言只能有一個返回值,而 Golang 中可以返回多個?
          • Golang 函數(shù)調(diào)用在性能上和 C 比有何差異?

          一、C 語言函數(shù)深究

          我們準備一段簡單的函數(shù)調(diào)用代碼。

          #include <stdio.h>  
          int func(int p){   
              return 1;
          }  
          int main()  
          {  
              int i;  
              for(i=0; i<100000000; i++){  
                  func(2);  
              }  
              return 0;  
          }

          用 gcc 來查看下匯編代碼。

          # gcc -S main.c

          匯編源碼如下:

          func:
                  movl    %edi, -4(%rbp)
                  movl    %esi, -8(%rbp)
                  movl    %edx, -12(%rbp)
                  movl    %ecx, -16(%rbp)
                  movl    %r8d, -20(%rbp)
                  movl    $1, %eax
                  
          main:
                  movl    $5, %r8d
                  movl    $4, %ecx
                  movl    $3, %edx
                  movl    $2, %esi
                  movl    $1, %edi
                  call    func
                 
                  movl    $0, %eax

          可以看到,在C語言中:

          主要通過寄存器傳遞參數(shù)
          所以,C 語言函數(shù)的性能杠杠的。寄存器是整個計算機體系結(jié)構(gòu)中訪問最最快的存儲了。只有當(dāng)參數(shù)數(shù)量大于 6 的時候,才開始使用棧。

          固定 eax 寄存器返回數(shù)據(jù)
          因為固定使用 eax 寄存器做返回數(shù)據(jù)之用,所以在 C 語言中無法支持多個返回值。我們接下來看看 Golang 是如何支持多個返回值的。

          二、Golang 函數(shù)深究

          同樣先寫一段最簡單的函數(shù)調(diào)用代碼。

          package main

          func myFunction(p1, p2, p3,p4, p5 int) (int,int) {
           var a int = p1+p2+p3+p4+p5
           var b int = 3
           return a,b
          }

          func main() {
           myFunction(12345)
          }

          然后查看其匯編代碼。

          //為了方便查看,使用-N -l 參數(shù),能阻止編譯器對匯編代碼的優(yōu)化
          #go tool compile -S -N -l main.go > main.s

          結(jié)果是這樣的:

          "".main STEXT size=95 args=0x0 locals=0x38
            0x000f 00015 (main.go:7)  SUBQ $56, SP  //在棧上分配56字節(jié)
            0x0013 00019 (main.go:7)  MOVQ BP, 48(SP) //保存BP
            0x0018 00024 (main.go:7)  LEAQ 48(SP), BP

                  0x001d 00029 (main.go:8)        MOVQ    $1, (SP) //第一個參數(shù)入棧
                  0x0025 00037 (main.go:8)        MOVQ    $28(SP) //第二個參數(shù)入棧
                  0x002e 00046 (main.go:8)        MOVQ    $316(SP) //第三個參數(shù)入棧
                  0x0037 00055 (main.go:8)        MOVQ    $424(SP) //第四個參數(shù)入棧
                  0x0040 00064 (main.go:8)        MOVQ    $532(SP) //第五個參數(shù)入棧
                  0x0049 00073 (main.go:8)        CALL    "".myFunction(SB)

          "".myFunction STEXT nosplit size=99 args=0x38 locals=0x18
                  0x000e 00014 (main.go:3) MOVQ $0"".~r5+72(SP)
                  0x0017 00023 (main.go:3) MOVQ $0"".~r6+80(SP)
                  0x0020 00032 (main.go:4) MOVQ "".p1+32(SP), AX
                  0x0025 00037 (main.go:4) ADDQ "".p2+40(SP), AX
                  0x002a 00042 (main.go:4) ADDQ "".p3+48(SP), AX
                  0x002f 00047 (main.go:4) ADDQ "".p4+56(SP), AX
                  0x0034 00052 (main.go:4) ADDQ "".p5+64(SP), AX
                  0x004b 00075 (main.go:6) MOVQ AX, "".~r5+72(SP)
                  0x0054 00084 (main.go:6) MOVQ AX, "".~r6+80(SP)

          可以看到,在Golang中:

          使用棧來傳遞參數(shù)
          棧是位于內(nèi)存之中的,雖然有 CPU 中 L1、L2、L3的幫助,但平均每次訪問性能仍然和寄存器沒法比。所以 Golang 的函數(shù)調(diào)用開銷肯定會比 C 語言要高。后面我們將用一個實驗來進行量化的比較。

          使用棧來返回數(shù)據(jù)
          不像 C 語言那樣固定使用一個 eax 寄存器,Golang 是使用棧來返回值的。這就是為啥 Golang 可以返回多個值的根本原因。

          最后,性能開銷對比

          我們的測試方法簡單粗暴,直接調(diào)用空函數(shù) 1 億次,再統(tǒng)計計算平均耗時。

          C函數(shù)編譯運行測試:

          #include <stdio.h>  
          int func(int p){   
              return 1;
          }  
          int main()  
          {  
              int i;  
              for(i=0; i<100000000; i++){  
                  func(2);  
              }  
              return 0;  
          }
          # gcc main.c -o main
          # time ./main

          第一次執(zhí)行耗時大約是 0.339 s。

          但這個耗時中包含了兩塊。一塊是函數(shù)調(diào)用開銷,另外一塊是 for 循環(huán)的開銷(其它的代碼調(diào)用因為只有 1 次,而函數(shù)調(diào)用和 for 循環(huán)都有 1 億次,所以直接就可以忽略了)。

          所以我們得減去 for 循環(huán)的開銷。接著我手工注釋掉對函數(shù)的調(diào)用,只是空循環(huán) 100000000 次。

          int main()  
          {  
              int i;  
              for(i=0; i<100000000; i++){  
              //    func(2);  
              }  
              return 0;  
          }

          這次總耗時是 0.314 s。

          這樣就計算出平均每次函數(shù)調(diào)用耗時 = (0.339s - 0.314s) / 100000000 = 0.25ns

          Golang函數(shù)編譯運行

          func hello(a int) int {
           return 2
          }

          func main(){
           for i:=0; i<100000000; i++ {
            hello(1)
           }
          }
          # go build -gcflags="-m -l" main.go

          同樣采用上述方法測出平均每次函數(shù)調(diào)用耗時 = (0.302s - 0.056 s) / 100000000 = 2.46ns

          可見 Golang 的函數(shù)調(diào)用性能還是比 C 要差一些。但再給大家個參考一下 PHP 的數(shù)據(jù),之前我測過 PHP7 每次函數(shù)調(diào)用開銷大約在 50 ns 左右。所以 Golang 雖然比不上 C,但總的來說性能還是不錯的。



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進階看什么。關(guān)注公眾號 「polarisxu」,回復(fù) ebook 獲?。贿€可以回復(fù)「進群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 71
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一级特黄A片免费看视频小说 | 天天日天天日天天日 | 亚洲福利视频网站 | 国V精品秘 久久久网 | 私人女仆扫地偷懒被主人颜色吃现在被喷尿洗脸 |