为什么内存往返速度比不执行往返更快?

时间:2015-08-07 03:08:31

标签: performance assembly x86

我有一些简单的32位代码,它可以计算32位整数数组的乘积。内循环如下所示:

@@loop:
mov esi,[ebx]
mov [esp],esi
imul eax,[esp]
add ebx, 4
dec edx
jnz @@loop

我想要了解的是为什么上面的代码比这两个版本的代码快6%,这些代码不执行冗余内存往返:

@@loop:
mov esi,[ebx]
imul eax,esi
add ebx, 4
dec edx
jnz @@loop

@@loop:
imul eax,[ebx]
add ebx, 4
dec edx
jnz @@loop

后两段代码几乎同时执行,如前所述,它们比第一段慢了6%(165ms vs 155ms,2亿个元素)。

我尝试将跳转目标手动对齐到16字节边界,但没有区别。

我在英特尔i7 4770k,Windows 10 x64上运行。

注意:我知道可以通过各种优化来改进代码,但是我只对上面代码之间的性能差异感兴趣。

1 个答案:

答案 0 :(得分:1)

我怀疑但无法确定您是否阻止了数据依赖的停顿:

代码如下所示:

@@loop:
    mov esi,[ebx]    # (1)Load the memory location to esi reg
    (mov [esp],esi)  # (1)optionally store the location on the stack      
    imul eax,[esp]   # (3) Perform the multiplication
    add ebx, 4       # (1) Add 4
    dec edx          # (1)decrement counter
    jnz @@loop       # (0**) loop 

括号中的数字是指令的延迟......如果分支预测器正确猜测,则跳转为0(因为它大部分时间都会主要循环)。

所以:当乘法仍在进行时(3条指令),我们在2之后回到循环的顶部并尝试加载到内存中并且必须停止。或者我们可以做一个商店...我们可以在乘法的同时做,然后根本不停顿。

你问的虚拟商店怎么样?为什么这样做?请注意,您正在存储我们用于乘以内存的临界值。因此,处理器可以使用存储在存储器中的该值并破坏寄存器。

那么为什么处理器无法做到这一点呢?处理器无法产生比你要求更多的内存访问,或者它可能会干扰多处理器程序(想象你正在写入的缓存行是共享的,你必须通过写入它来在每个循环的其他CPU上使它无效......哎哟!)。

所有这些都是纯粹的推测,但它似乎与所有证据相符(您的代码和我对intel架构的知识......和x86汇编)。希望有人可以指出我是否有错误。