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

相关问题