0

我試圖找出Go的循環性能是否與C一樣好,但是令人驚訝的是,對於我的簡單測試,C版本花費兩倍於Go版本的時間。爲什麼Go中的這個簡單循環比C中的更快?

C版:

#include <stdio.h> 

int main() { 
    int i = 0, a = 0; 

    while (i < 1e9) { 
    a = (a + i) % 42; 
    i = i + 1; 
    } 
    printf("%d\n", a); 
} 

$ gcc -o main main.c && time ./main # tried -O0 as well; the result is similar 
36 
./main 10.53s user 0.08s system 98% cpu 10.769 total 

轉到版本:

package main 

import "fmt" 

func main() { 
    a := int32(0) 
    for i := int32(0); i < 1e9; i++ { 
     a = (a + i) % 42 
    } 
    fmt.Println(a) 
} 

$ time go run main.go 
36 
colorgo run main.go 5.27s user 0.14s system 93% cpu 5.816 total 

(在達爾文測試,amd64)

對於這個簡單的算法,不應該他們都產生幾乎相同的機器碼?這是由於編譯器優化?緩存效率?

請幫我理解!謝謝!

+9

你的C程序編譯時沒有任何優化。 Go編譯器可能會有一個不同的默認值,並且不經過指示即可進行優化。用'gcc -O2'編譯一個更公平的比較。 – delnan 2014-10-01 21:51:07

+0

@delnan For go:「由[Go]編譯器生成的代碼默認爲'優化':」source:http://plan9.bell-labs.com/sys/doc/comp.pdf通過http:// golang.org/cmd/gc/ – dyp 2014-10-01 21:54:12

+0

你可以得到asm來進行比較(對於Go就像'go build -gcflags -S')。我很困難,但你的Go使用的是32位整數,而C可能使用64,而64位除法是一個非常慢的指令。那或者Go在這裏有一些不在gcc的默認優化級別上的優化。 – twotwotwo 2014-10-01 21:55:07

回答

3

這一切都歸結到所產生的組件。

去工具6克-S(21個指令):

MOVL $0,SI 
MOVL SI,"".a+8(FP) 
MOVL $0,CX 
CMPL CX,$1000000000 
JGE  $0,58 
ADDL CX,SI 
MOVL $818089009,BP 
MOVL SI,AX 
IMULL BP, 
MOVL DX,BX 
SARL $3,BX 
MOVL SI,BP 
SARL $31,BP 
SUBL BP,BX 
IMULL $42,BX 
SUBL BX,SI 
MOVL SI,"".a+8(FP) 
INCL ,CX #point A 
NOP  , 
CMPL CX,$1000000000 
JLT  $0,16 
RET  , 

GCC -O3 -march =天然-S(17個指令):

leal (%rsi,%rcx), %edi 
addl $1, %ecx 
vxorpd %xmm0, %xmm0, %xmm0 
vcvtsi2sd  %ecx, %xmm0, %xmm0 
movl %edi, %eax 
imull %r8d 
movl %edi, %eax 
sarl $31, %eax 
sarl $3, %edx 
movl %edx, %esi 
subl %eax, %esi 
imull $42, %esi, %esi 
subl %esi, %edi 
vucomisd  %xmm0, %xmm1 
movl %edi, %esi 
ja  .L2 
subq $8, %rsp 

GCC -O3 -march =天然 - S(14個指令,以十億替換1E9後):

leal (%rdx,%rcx), %esi 
addl $1, %ecx 
movl %esi, %eax 
imull %edi 
movl %esi, %eax 
sarl $31, %eax 
sarl $3, %edx 
subl %eax, %edx 
imull $42, %edx, %edx 
subl %edx, %esi 
movl %esi, %edx 
cmpl $1000000000, %ecx 
jne  .L2 
subq $8, %rsp 

定時:

$ gcc -O3 -march=native loop.c; and time ./a.out 
36 
2.92user 0.00system 0:02.93elapsed 99%CPU 
$ go build -o loop loop.go; and time ./loop 
36 
2.89user 0.00system 0:02.90elapsed 99%CPU 
$ gcc -O3 -march=native loop_nofp.c; and time ./a.out 
36 
2.92user 0.00system 0:02.94elapsed 99%CPU (0avgtext+0avgdata 1312maxresident) 

我不知道,我現在就離開這個,直到找到正確的答案。

//編輯

改變C代碼以用於匹配進入的版本產生的不同組件,但完全相同的定時。

int main() { 
    int32_t i = 0, a = 0; 
    for (i = 0; i < 1e9; i++) { 
     a = (a + i) % 42; 
    } 
    printf("%d\n", a); 
    return 0; 
} 
+1

'MOVL $ ...,BP'指令是用乘法和移位序列替換除法(模運算符)的一部分.GCC在優化的C中做了類似的事情。 – EOF 2014-10-01 22:56:28

1

它們大約與優化時相同。例如,

轉到:

$ cat t.go 
package main 

import "fmt" 

func main() { 
    a := int32(0) 
    for i := int32(0); i < 1e9; i++ { 
     a = (a + i) % 42 
    } 
    fmt.Println(a) 
} 
$ go version 
go version devel +e1a081e6ddf8 Sat Sep 27 11:56:54 2014 -0700 linux/amd64 
$ go build t.go && time ./t 
36 
real 0m15.809s 
user 0m15.815s 
sys 0m0.061s 

C:

$ cat t.c 
#include <stdio.h> 

int main() { 
    int i = 0, a = 0; 

    while (i < 1e9) { 
    a = (a + i) % 42; 
    i = i + 1; 
    } 
    printf("%d\n", a); 
} 
$ gcc --version 
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 
$ gcc -O3 t.c && time ./a.out 
36 
real 0m16.538s 
user 0m16.528s 
sys 0m0.021s 
+0

Go即使在i7上使用'gcc -O3 -march = native',速度仍然很快(只是一點點)+ – OneOfOne 2014-10-01 22:43:49

相關問題