tim*_*day 9 memory windows cpu 64-bit 32-bit
我们有配备XP64的Core2机器(Dell T5400).
我们观察到,当运行32位进程时,memcpy的性能大约为1.2GByte/s; 然而,64位进程中的memcpy达到大约2.2GByte/s(或英特尔编译器CRT的memcpy为2.4GByte/s).虽然最初的反应可能只是解释这个因为64位代码中可用的寄存器更宽,但我们观察到我们自己的类似memcpy的SSE汇编代码(应该使用128位宽的加载存储而不管32位/ 64位过程)表明它实现的复制带宽有类似的上限.
我的问题是,这个差异究竟是由于什么原因造成的?32位进程是否必须通过一些额外的WOW64箍来获取RAM?这与TLB或prefetchers有什么关系还是......什么?
感谢您的任何见解.
也在英特尔论坛上提出.
我认为以下内容可以解释它:
要将数据从内存复制到寄存器并返回到内存,可以
mov eax, [address]
mov [address2], eax
这会将32位(4字节)从地址移动到地址2.64位模式下的64位也是如此
mov rax, [address]
mov [address2], rax
这将64位,2字节从地址移动到地址2.根据英特尔的规格,"mov"本身,无论是64位还是32位,延迟为0.5,吞吐量为0.5.延迟是指令通过流水线所需的时钟周期数,吞吐量是CPU在再次接受相同指令之前必须等待的时间.正如您所看到的,它可以在每个时钟周期执行两个mov,但是,它必须在两个mov之间等待半个时钟周期,因此它实际上每个时钟周期只能执行一个mov(或者我错在这里并误解了这些术语?有关详细信息,请参见PDF.
当然,a mov reg, mem可以超过0.5个周期,具体取决于数据是在第一级还是第二级缓存中,还是在缓存中根本不需要,并且需要从内存中获取.但是,上面的延迟时间忽略了这个事实(正如我在上面链接的PDF状态),它假设已经存在mov所需的所有数据(否则延迟将增加从任何地方获取数据所需的时间.现在 - 这可能是几个时钟周期,完全独立于正在执行的命令,如第482/C-30页的PDF所示.
有趣的是,mov是32位还是64位都没有任何作用.这意味着除非内存带宽成为限制因素,否则64位mov的速度与32位mov相同,并且由于在使用64位时将相同数量的数据从A移动到B只需要一半的数量,因此吞吐量可以(理论上)是两倍高(事实上它不是因为记忆不是快速无限制).
好的,现在您认为使用更大的SSE寄存器时,您应该获得更快的吞吐量,对吧?AFAIK xmm寄存器不是256,而是128位宽,BTW(维基百科上的参考).但是,您是否考虑过延迟和吞吐量?要移动的数据是否为128位对齐.根据具体情况,您可以使用它移动它
movdqa xmm1, [address]
movdqa [address2], xmm1
或者如果不对齐
movdqu xmm1, [address]
movdqu [address2], xmm1
好吧,movdqa/movdqu的延迟为1,吞吐量为1.因此,指令需要两倍的时间才能执行,指令后的等待时间是普通mov的两倍.
而我们甚至没有考虑到的其他事实是CPU实际上将指令拆分为微操作并且可以并行执行这些操作.现在它变得非常复杂......对我来说太复杂了.
无论如何,我知道从向xmm寄存器加载数据或从xmm寄存器加载数据的速度比从正常寄存器加载数据慢得多,因此从第一秒起就注定要加速使用xmm寄存器进行传输的想法.我真的很惊讶,最后SSE memmove并不比正常情况慢得多.
我终于到底了(并且参议员的回答是正确的,感谢)
在下面,dst和src是512 MByte std :: vector.我正在使用Intel 10.1.029编译器和CRT.
在64位上
memcpy(&dst[0],&src[0],dst.size())
和
memcpy(&dst[0],&src[0],N)
其中N是先前声明的const size_t N=512*(1<<20);
呼叫
__intel_fast_memcpy
其中大部分包括:
  000000014004ED80  lea         rcx,[rcx+40h] 
  000000014004ED84  lea         rdx,[rdx+40h] 
  000000014004ED88  lea         r8,[r8-40h] 
  000000014004ED8C  prefetchnta [rdx+180h] 
  000000014004ED93  movdqu      xmm0,xmmword ptr [rdx-40h] 
  000000014004ED98  movdqu      xmm1,xmmword ptr [rdx-30h] 
  000000014004ED9D  cmp         r8,40h 
  000000014004EDA1  movntdq     xmmword ptr [rcx-40h],xmm0 
  000000014004EDA6  movntdq     xmmword ptr [rcx-30h],xmm1 
  000000014004EDAB  movdqu      xmm2,xmmword ptr [rdx-20h] 
  000000014004EDB0  movdqu      xmm3,xmmword ptr [rdx-10h] 
  000000014004EDB5  movntdq     xmmword ptr [rcx-20h],xmm2 
  000000014004EDBA  movntdq     xmmword ptr [rcx-10h],xmm3 
  000000014004EDBF  jge         000000014004ED80 
并以~2200 MByte/s运行.
但在32位
memcpy(&dst[0],&src[0],dst.size())
电话
__intel_fast_memcpy
其中大部分由
  004447A0  sub         ecx,80h 
  004447A6  movdqa      xmm0,xmmword ptr [esi] 
  004447AA  movdqa      xmm1,xmmword ptr [esi+10h] 
  004447AF  movdqa      xmmword ptr [edx],xmm0 
  004447B3  movdqa      xmmword ptr [edx+10h],xmm1 
  004447B8  movdqa      xmm2,xmmword ptr [esi+20h] 
  004447BD  movdqa      xmm3,xmmword ptr [esi+30h] 
  004447C2  movdqa      xmmword ptr [edx+20h],xmm2 
  004447C7  movdqa      xmmword ptr [edx+30h],xmm3 
  004447CC  movdqa      xmm4,xmmword ptr [esi+40h] 
  004447D1  movdqa      xmm5,xmmword ptr [esi+50h] 
  004447D6  movdqa      xmmword ptr [edx+40h],xmm4 
  004447DB  movdqa      xmmword ptr [edx+50h],xmm5 
  004447E0  movdqa      xmm6,xmmword ptr [esi+60h] 
  004447E5  movdqa      xmm7,xmmword ptr [esi+70h] 
  004447EA  add         esi,80h 
  004447F0  movdqa      xmmword ptr [edx+60h],xmm6 
  004447F5  movdqa      xmmword ptr [edx+70h],xmm7 
  004447FA  add         edx,80h 
  00444800  cmp         ecx,80h 
  00444806  jge         004447A0
并且仅以~1350 MByte/s运行.
然而
memcpy(&dst[0],&src[0],N)
其中N先前被声明const size_t N=512*(1<<20);编译(在32位上)直接调用a
__intel_VEC_memcpy
其中大部分由
  0043FF40  movdqa      xmm0,xmmword ptr [esi] 
  0043FF44  movdqa      xmm1,xmmword ptr [esi+10h] 
  0043FF49  movdqa      xmm2,xmmword ptr [esi+20h] 
  0043FF4E  movdqa      xmm3,xmmword ptr [esi+30h] 
  0043FF53  movntdq     xmmword ptr [edi],xmm0 
  0043FF57  movntdq     xmmword ptr [edi+10h],xmm1 
  0043FF5C  movntdq     xmmword ptr [edi+20h],xmm2 
  0043FF61  movntdq     xmmword ptr [edi+30h],xmm3 
  0043FF66  movdqa      xmm4,xmmword ptr [esi+40h] 
  0043FF6B  movdqa      xmm5,xmmword ptr [esi+50h] 
  0043FF70  movdqa      xmm6,xmmword ptr [esi+60h] 
  0043FF75  movdqa      xmm7,xmmword ptr [esi+70h] 
  0043FF7A  movntdq     xmmword ptr [edi+40h],xmm4 
  0043FF7F  movntdq     xmmword ptr [edi+50h],xmm5 
  0043FF84  movntdq     xmmword ptr [edi+60h],xmm6 
  0043FF89  movntdq     xmmword ptr [edi+70h],xmm7 
  0043FF8E  lea         esi,[esi+80h] 
  0043FF94  lea         edi,[edi+80h] 
  0043FF9A  dec         ecx  
  0043FF9B  jne         ___intel_VEC_memcpy+244h (43FF40h) 
并以~2100MByte/s运行(证明32位不是以某种方式限制带宽).
我撤回了我的说法,即我自己的类似memcpy的SSE代码在32位版本中遭受类似的~1300 MByte /限制; 我现在在32或64bit上获得> 2GByte/s时没有任何问题; 诀窍(如上面的结果提示)是使用非时间("流")存储(例如_mm_stream_ps内在).
32bit" dst.size()"memcpy最终没有调用更快的" movnt"版本似乎有点奇怪(如果你进入memcpy,那里有最令人难以置信的CPUID检查量和启发式逻辑,例如比较要复制的字节数和缓存大小等在它接近你的实际数据之前的任何地方)但至少我现在理解观察到的行为(并且它不是SysWow64或H/W相关).
当然,您确实需要通过使用调试器单步执行机器代码来查看在 memcpy 最内层循环内执行的实际机器指令。其他任何事情都只是猜测。
我的问题是,它可能与 32 位与 64 位本身没有任何关系;我的猜测是更快的库例程是使用 SSE 非临时存储编写的。
如果内部循环包含传统加载-存储指令的任何变体,则必须将目标存储器读入机器的高速缓存、修改并写回。由于该读取完全没有必要——正在读取的位会立即被覆盖——您可以通过使用绕过缓存的“非临时”写入指令来节省一半的内存带宽。这样,目标内存就被写入到内存的单向行程而不是往返。
我不知道Intel编译器的CRT库,所以这只是一个猜测。没有什么特别的原因为什么 32 位 libCRT 不能做同样的事情,但是你引用的加速是在我所期望的范围内,只需将 movdqa 指令转换为 movnt...
由于 memcpy 不执行任何计算,因此它始终受读写内存的速度限制。
| 归档时间: | 
 | 
| 查看次数: | 8592 次 | 
| 最近记录: |