gpt4 book ai didi

performance - MOVSD的性能取决于参数

转载 作者:行者123 更新时间:2023-12-03 14:44:04 26 4
gpt4 key购买 nike

我只是注意到复制内存时,我的代码片段表现出不同的性能。测试表明,如果目标缓冲区的地址大于源缓冲区的地址,则内存复制性能会下降。听起来很荒谬,但是以下代码显示了区别(Delphi):

  const MEM_CHUNK = 50 * 1024 * 1024;
ROUNDS_COUNT = 100;


LpSrc := VirtualAlloc(0,MEM_CHUNK,MEM_COMMIT,PAGE_READWRITE);
LpDest := VirtualAlloc(0,MEM_CHUNK,MEM_COMMIT,PAGE_READWRITE);

QueryPerformanceCounter(LTick1);
for i := 0 to ROUNDS_COUNT - 1 do
CopyMemory(LpDest,LpSrc,MEM_CHUNK);
QueryPerformanceCounter(LTick2);
// show timings

QueryPerformanceCounter(LTick1);
for i := 0 to ROUNDS_COUNT - 1 do
CopyMemory(LpSrc,LpDest,MEM_CHUNK);
QueryPerformanceCounter(LTick2);
// show timings


在这里,CopyMemory基于MOVSD。结果 :


  正在启动内存带宽测试...
  
  LpSrc 0x06FC0000
  
  LpDest 0x0A1C0000
  
  src->目标传输:1,188秒@ 4,110 GB / s,5242880000字节。
  
  dest-> src传输:在6,805 GB / s下的0,805秒内5242880000字节。
  
  src-> dest传输:1,142秒(4,275 GB / s)时5242880000字节。
  
  dest-> src传输:5242880000字节,每秒0.832秒@ 5,871 GB / s。


在两个系统上尝试过,无论重复多少次,结果都是一致的。

从来没有见过那样的东西。无法谷歌它。这是已知行为吗?这仅仅是与缓存相关的另一个特性吗?

更新:

这是页面对齐的缓冲区和MOVSD的向前方向(DF = 0)的最终结果:


  正在启动内存带宽测试...
  
  LpSrc 0x06F70000
  
  LpDest 0x0A170000
  
  src-> dest传输:5242880000字节,在0,781秒内以6,250 GB / s传输。
  
  dest-> src传输:5242880000字节,以0,731秒@ 6,676 GB / s
  
  src->目标传输:在6,750 GB / s下的0,750秒内5242880000字节。
  
  dest-> src传输:在6,735 GB / s下的0,735秒内5242880000字节
  
  src->目标传输:5242880000字节,以0,742秒@ 6,585 GB / s为单位。
  
  dest-> src传输:0,750秒@ 6,515 GB / s的5242880000字节
  
  ... 等等。


在这里,传输速率是恒定的。

最佳答案

通常,快速字符串或ERMSB微码可使rep movsb/w/d/qrep stosb/w/d/q快速进行大量计数(以16、32甚至64字节的块进行复制)。并可能为商店提供了避免RFO的协议。 (其他repe/repne scas/cmps总是很慢)。

输入的某些条件可能会干扰该最佳情况,尤其是使DF = 1(向后)而不是正常的DF = 0。

rep movsd性能可能取决于src和dst的对齐方式,包括它们的相对对齐方式。显然,两个指针都为32*n + same并不算太糟糕,因此大多数复制可以在到达对齐边界后完成。 (绝对未对齐,但是指针彼此相对对齐。即dst-src是32或64字节的倍数)。

性能不依赖于src > dstsrc < dst每秒。如果指针在重叠的16或32字节之内,则也可能一次强制回退到1个元素。

英特尔的优化手册中有一节介绍了memcpy的实现,并将rep movs与经过优化的SIMD循环进行了比较。启动开销是rep movs的最大缺点之一,但错位也不能很好地解决。 (IceLake的“快速短路rep”功能可以解决此问题。)


  我没有公开CopyMemory主体-的确在避免重叠时确实使用了向后复制(df = 1)。


是的,这是您的问题。仅在需要避免实际重叠时才向后复制,而不仅仅是根据哪个地址更高。然后使用SIMD向量而不是rep movsd



rep movsd仅在DF = 0(升序地址)时才快速运行,至少在Intel CPU上如此。我刚刚在Skylake上进行了检查:使用rep movsb从页面对齐的缓冲区复制4096个非重叠字节的1000000次运行在:


cld循环174M(DF = 0向前)。在约4.1GHz时约为42ms,或约90GiB / s L1d读写带宽。每个周期大约23个字节,因此每个rep movsb的启动开销似乎在伤害我们。在这种简单的L1d高速缓存命中的简单情况下,AVX复制循环应达到接近32B / s的速度,即使分支从内部循环退出时分支预测错误。
std(DF = 1向后)循环4161M。在约4.1GHz时约为1010ms,或约3.77GiB / s读写。大约0.98个字节/周期,与rep movsb完全未优化的一致。 (每个周期1个计数,因此rep movsd大约是缓存命中带宽的4倍。)


uops_executed perf计数器还确认,向后复制时它的花费更多。 (这是Linux下长模式下的dec ebp / jnz循环内。与使用NASM构建的Can x86's MOV really be "free"? Why can't I reproduce this at all?相同的测试循环,带有BSS的缓冲区。该循环执行cldstd / 2x lea / mov ecx, 4096 / rep movsb。将cld吊出循环并没有太大区别。)

您正在使用rep movsd一次复制4个字节,因此对于向后复制,如果它们在高速缓存中命中,我们可以期望每个周期4个字节。而且您可能正在使用大缓冲区,因此高速缓存错过了前进方向的瓶颈,而瓶颈并没有比倒退更快。但是,向后复制产生的额外操作会损害内存并行性:适合乱序窗口的加载操作会触摸较少的缓存行。此外,在Intel CPU中,某些预取器向后运行的效果也较差。 L2流媒体可在任一方向上工作,但我认为L1d预取只会向前进行。

相关:Enhanced REP MOVSB for memcpy您的Sandybridge对于ERMSB来说太旧了,但是自原始P6起,已经存在rep movs / rep stos的快速字符串。从今天的标准来看,您〜2006年的Clovertown Xeon几乎是古老的。 (Conroe / Merom微体系结构)。这些CPU可能太旧了,以至于与当今的多核Xeon不同,Xeon的单核可以饱和微薄的内存带宽。



我的缓冲区是页面对齐的。对于向下,我尝试将初始RSI / RDI指向页面的最后一个字节,以便初始指针未对齐,但要复制的总区域是对齐的。我还尝试了lea rdi, [buf+4096],所以起始指针是页面对齐的,所以[buf+0]没有写出来。两者都不能使向后复制更快。 rep movs只是DF = 1的垃圾;如果需要向后复制,请使用SIMD向量。

如果您可以使用机器支持的最大宽度,则通常SIMD向量循环的速度至少可以与rep movs一样快。这意味着拥有SSE,AVX和AVX512版本...在无需运行时调度到针对特定CPU调整的memcpy实现的可移植代码中,rep movsd通常非常好,并且在像IceLake这样的未来CPU上应该会更好。



您实际上不需要页面对齐即可使rep movs快速。 IIRC,32字节对齐的源和目标就足够了。但是4k别名也可能是一个问题:如果dst & 4095稍高于src & 4095,则负载uops可能在内部不得不为存储uops等待一些额外的周期,因为用于检测负载何时重新加载负载的快速路径机制。最近存储区仅查看页面偏移量位。

但是,页面对齐是确保获得rep movs最佳情况的一种方法。

通常,您可以从SIMD循环中获得最佳性能,但前提是您使用的是与计算机支持的宽度相同的SIMD向量(例如AVX,甚至AVX512)。而且,您应该根据硬件和周围的代码选择NT存储区与普通存储区。

关于performance - MOVSD的性能取决于参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57137074/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com