为什么Go中的这个简单循环比C中的快?
我试图找出Go的循环性能是否和C的一样好,但令人惊讶的是,对于我的简单测试,C版本花费的时间是Go版本的两倍 C版本:为什么Go中的这个简单循环比C中的快?,c,go,benchmarking,compiler-optimization,computer-architecture,C,Go,Benchmarking,Compiler Optimization,Computer Architecture,我试图找出Go的循环性能是否和C的一样好,但令人惊讶的是,对于我的简单测试,C版本花费的时间是Go版本的两倍 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); } Go版本: package main import "fmt" func mai
#include <stdio.h>
int main() {
int i = 0, a = 0;
while (i < 1e9) {
a = (a + i) % 42;
i = i + 1;
}
printf("%d\n", a);
}
Go版本:
package main
import "fmt"
func main() {
a := int32(0)
for i := int32(0); i < 1e9; i++ {
a = (a + i) % 42
}
fmt.Println(a)
}
在达尔文进行测试,amd64
对于这个简单的算法,它们不应该产生几乎相同的机器代码吗?这是因为编译器优化吗?缓存效率
请帮我理解!谢谢 它们在优化时的时间大致相同。比如说, 去: C:
它们在优化时的时间差不多相同。比如说, 去: C:
这一切归结为生成的程序集 go工具6g-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 ,
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 17说明:
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 ,
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=native-s14指令,在将1e9替换为100000000之后:
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代码更改为用于匹配Go版本会产生不同的程序集,但时间完全相同
int main() {
int32_t i = 0, a = 0;
for (i = 0; i < 1e9; i++) {
a = (a + i) % 42;
}
printf("%d\n", a);
return 0;
}
这一切归结为生成的程序集 go工具6g-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 ,
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 17说明:
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 ,
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=native-s14指令,在将1e9替换为100000000之后:
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代码更改为用于匹配Go版本会产生不同的程序集,但时间完全相同
int main() {
int32_t i = 0, a = 0;
for (i = 0; i < 1e9; i++) {
a = (a + i) % 42;
}
printf("%d\n", a);
return 0;
}
您的C程序是在没有任何优化的情况下编译的。Go编译器可能有不同的默认值,并且在没有指示的情况下进行优化。使用gcc-O2进行编译以进行更公平的比较。@delnan for go:go编译器生成的代码默认情况下是“优化”的:源代码:通过使用asm,两者都可以比较go,这类似于go build-gcflags-s。我很难理解,但你的go使用的是32位整数,而C可能使用的是64位整数,也许64位除法是一种慢得多的指令。或者Go在这里有一些优化,但在gcc的默认优化级别上没有。关于@delnan,。为什么要比较不同的代码???至少你应该对两者使用相同的代码或while。你的C程序是在没有任何优化的情况下编译的。Go编译器可能有不同的默认值,并且在没有指示的情况下进行优化。使用gcc-O2进行编译以进行更公平的比较。@delnan for go:go编译器生成的代码默认情况下是“优化”的:源代码:通过使用asm,两者都可以比较go,这类似于go build-gcflags-s。我很难理解,但你的go使用的是32位整数,而C可能使用的是64位整数,也许64位除法是一种慢得多的指令。或者Go在这里有一些优化,但在gcc的默认优化级别上没有。关于@delnan,。为什么要比较不同的代码???至少您应该为两者使用相同的函数或while。即使在i7上使用gcc-O3-march=native,Go仍然只快一点点。+-Go即使在i7上使用gcc-O3-march=native,仍然只快一点点。+-MOVL$…,BP指令是Go用一系列乘法和移位替换除法模运算符的一部分。GCC在优化C中做了类似的事情。MOVL$…,BP指令是go用乘法和移位序列替换除法模运算符的一部分。GCC在优化的C中做了类似的事情。