与 C 相比,内联装配性能优势
Inline assembly performance advantage over C
首先,请原谅我,因为我的问题可能看起来很愚蠢,但我真的很好奇为什么我在这个非常简单的代码中获得了性能提升。
下面是汇编代码:
__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 代码在我的 i5 2500k 机器上大约需要 1060 毫秒(在发布版本中)才能完成,程序集在 780 毫秒内完成。速度提高了~25%。我不明白为什么我会得到这个结果,因为 25% 是一个很大的差异。编译器不够聪明,无法生成我编写的相等汇编代码吗?
顺便说一句,我正在使用 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 代码在桑迪桥上运行得更快。
@modelnine的注释是正确的 - lea
用于简化循环中的分配。 你有:
x = ii - 2;
lea
(加载有效地址)指令正在有效地执行:
esi = &(*(eax - 2));
&
和*
相互抵消(这很重要 - 在这种情况下取消引用eax
可能会导致问题),因此您可以得到:
esi = eax - 2;
正是您的 C 代码试图做的事情。
我比较了非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
你为什么不试试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
定时是:
real 0m1.295s
user 0m1.262s
sys 0m0.006s
这实际上执行了代码。
对于 MSVC,应该能够使用/O2 或/O1 编译选项获得类似的结果。详情请点击此处 http://msdn.microsoft.com/en-us/library/k1ack8f1.aspx
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 与普通变量相比,仅仅读取原子变量的性能有什么不同吗
- 与纯 V8 相比,NodeJS 是否有任何性能缺陷或显著开销?
- 与libc相比,Gnu科学图书馆的性能较差
- 与其他输入相比,达到 65535 时的性能大幅跳跃
- 内存移动的性能与memcpy相比是两倍?
- 与保留模式GUI相比,使用直接模式GUI的性能含义是什么?
- iMX6上的Qt5:在平台eglfs上运行的性能优势与xcb相比
- 与C-string('malloc'+'memcpy')相比,'std::string'的性能真的很差
- 与英特尔相比,GNU C++编译器在对向量进行排序时性能较差
- 与直接编译的可执行文件相比,linux.so的性能有所下降
- 与天真计算相比,FMA性能
- 与单线程相比,c++/java的多线程性能结果参差不齐
- 在填充Int32Array时,Node.JS的性能与本机C++插件相比
- 与 C 相比,内联装配性能优势
- 与纹理阵列相比,使用TextureArray的性能优势
- 与指针相比,链式公共成员访问的性能
- 多维数组的性能与对象数组的性能相比如何?
- 与ppl或OpenMP相比,使用boost::thread可以获得更好的并行任务性能
- Julia集的CUDA内核与CPU版本相比性能较慢