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

8 performance x86 assembly

我有一些简单的32位代码,它可以计算32位整数数组的乘积.内循环看起来像这样:

@@loop:
mov esi,[ebx]
mov [esp],esi
imul eax,[esp]
add ebx, 4
dec edx
jnz @@loop
Run Code Online (Sandbox Code Playgroud)

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

@@loop:
mov esi,[ebx]
imul eax,esi
add ebx, 4
dec edx
jnz @@loop
Run Code Online (Sandbox Code Playgroud)

@@loop:
imul eax,[ebx]
add ebx, 4
dec edx
jnz @@loop
Run Code Online (Sandbox Code Playgroud)

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

我已经尝试将跳转目标手动对齐到16字节边界,但它没有任何区别.

我在Intel i7 4770k,Windows 10 x64上运行它.

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

dav*_*ave 0

我怀疑但不能确定您是否正在防止数据依赖性停滞:

代码如下所示:

@@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 
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

所有这些都是纯粹的猜测,但它似乎与所有证据相匹配(你的代码和我对英特尔架构的了解......以及 x86 汇编)。如果我有什么不对的地方,希望有人能指出。