首先,请原谅我,因为我的问题似乎很愚蠢,但我真的很好奇为什么我会在这个非常简单的代码中获得性能提升。
这是汇编代码:
__asm {
mov eax, 0
mov ecx, 0
jmp startloop
notequal:
inc eax
mov ecx, eax
sub ecx, 2
startloop:
cmp eax, 2000000000
jne notequal
};
这是C代码:
long x = 0;
long ii = 0;
for(; ii < 2000000000; ++ii)
{
x = ii - 2;
};
C代码需要大约1060毫秒(在发布版本中)才能在我的i5 2500k机器上完成,并且组件在780毫秒内完成。它的速度增加了约25%。我不明白为什么我得到这个结果,因为25%是一个很大的区别。编译器是否足够智能以生成我编写的相同汇编代码?
BTW我正在使用MSVC 2010.
由于
这是MSVC生成的(asm)代码
$LL3@main:
; Line 36
lea esi, DWORD PTR [eax-2]
inc eax
cmp eax, 2000000000 ; 77359400H
jl SHORT $LL3@main
lea指令在这种情况下做了什么?
更新2
非常感谢大家。我刚刚在Nehalem xeon cpu上测试了这段代码,结果在这里是相同的。看起来有一个未知的原因,asm代码在Sandy桥上运行得更快。
答案 0 :(得分:2)
@ modelnine的评论是正确的 - lea
用于简化循环中的赋值。你有:
x = ii - 2;
lea
(加载有效地址)指令正在有效地执行:
esi = &(*(eax - 2));
&
和*
互相取消(这很重要 - 在这种情况下取消引用eax
可能会导致问题),所以你得到:
esi = eax - 2;
您的C代码正在尝试做什么。
答案 1 :(得分:2)
我比较了非asm版本:
#include <iostream>
#include <chrono>
int main() {
auto start = std::chrono::high_resolution_clock::now();
long x = 0;
long ii = 0;
for(; ii < 2000000000; ++ii)
{
x = ii - 2;
};
auto finish = std::chrono::high_resolution_clock::now();
std::cout << (finish-start).count() << '\n';
std::cout << x << ii << '\n';
}
使用asm版本:
#include <iostream>
#include <chrono>
int main() {
auto start = std::chrono::high_resolution_clock::now();
asm (R"(
mov $0, %eax
mov $0, %ecx
jmp startloop
notequal:
inc %eax
mov %eax,%ecx
sub $2,%ecx
startloop:
cmp $2000000000,%eax
jne notequal
)");
auto finish = std::chrono::high_resolution_clock::now();
std::cout << (finish-start).count() << '\n';
}
使用clang 3.1
启用优化后,asm版本需要大约1.4秒,而非asm版本需要45纳秒。这适用于大约3200万%的装配版本。
这是为非asm版本生成的程序集:
movl $1999999997, %esi ## imm = 0x773593FD
callq __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
movq %rax, %rdi
movl $2000000000, %esi ## imm = 0x77359400
callq __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
答案 2 :(得分:1)
为什么不尝试gcc -Ofast
或gcc -O1
这是一个预告片:gcc -Q -Ofast --help=optimizers
,直接来自gnu手册!
这是一个比较:
section .text
global _start
_start:
mov eax, 0
mov ecx, 0
jmp startloop
notequal:
inc eax
mov ecx, eax
sub ecx, 2
startloop:
cmp eax, 2000000000
jne notequal
int 0x80
mov ebx,0
mov eax,1
int 0x80
我得到了1.306ms
而C被定时了:
real 0m0.001s
user 0m0.000s
sys 0m0.000s
使用gcc -O1
timed是:
real 0m1.295s
user 0m1.262s
sys 0m0.006s
实际上执行代码。
对于MSVC,应该能够使用/ O2或/ O1编译选项获得类似的结果。详情请http://msdn.microsoft.com/en-us/library/k1ack8f1.aspx