【发布时间】:2012-08-30 14:14:57
【问题描述】:
我正在编写一个执行数百万个模块化添加的程序。为了提高效率,我开始思考如何使用机器级指令来实现模块化加法。
设 w 为机器的字长(通常为 32 或 64 位)。如果取模数为 2^w,那么模加法可以非常快地执行:只需将加数相加,丢弃进位即可。
我使用以下 C 代码测试了我的想法:
#include <stdio.h>
#include <time.h>
int main()
{
unsigned int x, y, z, i;
clock_t t1, t2;
x = y = 0x90000000;
t1 = clock();
for(i = 0; i <20000000 ; i++)
z = (x + y) % 0x100000000ULL;
t2 = clock();
printf("%x\n", z);
printf("%u\n", (int)(t2-t1));
return 0;
}
使用带有以下选项的 GCC 进行编译(我使用 -O0 来防止 GCC 展开循环):
-S -masm=intel -O0
生成的汇编代码的相关部分是:
mov DWORD PTR [esp+36], -1879048192
mov eax, DWORD PTR [esp+36]
mov DWORD PTR [esp+32], eax
call _clock
mov DWORD PTR [esp+28], eax
mov DWORD PTR [esp+40], 0
jmp L2
L3:
mov eax, DWORD PTR [esp+36]
mov edx, DWORD PTR [esp+32]
add eax, edx
mov DWORD PTR [esp+44], eax
inc DWORD PTR [esp+40]
L2:
cmp DWORD PTR [esp+40], 19999999
jbe L3
call _clock
显然,不涉及任何模运算。
现在,如果我们将 C 代码的模加行改为:
z = (x + y) % 0x0F0000000ULL;
汇编代码更改为(仅显示相关部分):
mov DWORD PTR [esp+36], -1879048192
mov eax, DWORD PTR [esp+36]
mov DWORD PTR [esp+32], eax
call _clock
mov DWORD PTR [esp+28], eax
mov DWORD PTR [esp+40], 0
jmp L2
L3:
mov eax, DWORD PTR [esp+36]
mov edx, DWORD PTR [esp+32]
add edx, eax
cmp edx, -268435456
setae al
movzx eax, al
mov DWORD PTR [esp+44], eax
mov ecx, DWORD PTR [esp+44]
mov eax, 0
sub eax, ecx
sal eax, 28
mov ecx, edx
sub ecx, eax
mov eax, ecx
mov DWORD PTR [esp+44], eax
inc DWORD PTR [esp+40]
L2:
cmp DWORD PTR [esp+40], 19999999
jbe L3
call _clock
显然,在对_clock 的两次调用之间添加了大量指令。
考虑到汇编指令数量的增加, 我期望通过正确选择模数来获得至少 100% 的性能增益。但是,在运行输出时,我注意到速度仅提高了 10%。我怀疑操作系统正在使用多核 CPU 来并行运行代码,但即使将进程的 CPU 亲和性设置为 1 也没有任何改变。
你能解释一下吗?
编辑:使用 VC++ 2010 运行示例,我得到了我的预期:第二个代码比第一个示例慢了大约 12 倍!
【问题讨论】:
-
操作系统不会自动并行化。
-
某些模数比其他模数更难。仅此而已。
-
@ThomSmith:是的。正如我在上面指出的,我的测试也说了同样的话。
-
@SadeqDousti 如果系统足够空闲,上下文切换非常罕见,以至于更大的循环计数会增加计时的意义(我系统上的
clock()的分辨率为 10000 滴答声,所以简称-运行基准,小的差异不会可靠地显示出来)。此外,您应该使 x、y、z 易变并进行优化编译。我对此有很大的不同(令人惊讶的是,不是 -O0,我不明白为什么)。 -
@SadeqDousti 因此我将 x、y 和 z 设为易失性,因此编译器无法消除循环,必须在每次迭代中执行计算。如果没有 volatile,则完全删除循环,因此两者都产生 0 次计时。问题是,使用 -O0,两个循环之间的时间差很小,而通过优化,它大约是 50%(如果我让 x,y,z
unsigned long获得 64 位,则超过 100%,所以功率-of-2 模数不能完全去除)。毫不奇怪 -O1 使代码更快,它使差异更大(在这种情况下)。
标签: c++ c performance math assembly