我有一些简单的 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%(165 毫秒对 155 毫秒,2 亿个元素)。
我试过手动将跳转目标对齐到 16 字节的边界,但这没有区别。
我在 Intel i7 4770k、Windows 10 x64 上运行它。
注意:我知道可以通过进行各种优化来改进代码,但是我只对上述代码段之间的性能差异感兴趣。
最佳答案
我怀疑但不能确定您是否阻止了数据依赖项的停顿:
代码如下所示:
@@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 上无效……哎哟!)。
所有这些都是纯粹的猜测,但它似乎与所有证据相符(您的代码和我对英特尔架构……和 x86 程序集的了解)。如果我有什么不对的地方,希望有人能指出。
关于performance - 为什么内存往返比不执行往返更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31869079/