- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我只是注意到复制内存时,我的代码片段表现出不同的性能。测试表明,如果目标缓冲区的地址大于源缓冲区的地址,则内存复制性能会下降。听起来很荒谬,但是以下代码显示了区别(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
最佳答案
通常,快速字符串或ERMSB微码可使rep movsb/w/d/q
和rep 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 > dst
或src < 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的缓冲区。该循环执行cld
或std
/ 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/
我是一名优秀的程序员,十分优秀!