我正在寻求解释我在循环中看到的结果,该循环使用 x86 movdqu 指令从某个源内存位置移动 64 字节到某个目标内存位置(movdqu 指令支持将 16 字节数据从/移到 xmm-寄存器到/从未对齐的内存位置)。这是实现类似于 memcpy()/java.lang.System.arraycopy() 的函数的代码的一部分
我尝试使用两种不同的模式来实现副本:
模式1
0x30013f74: prefetchnta BYTE PTR [rsi]
0x30013f77: prefetchnta BYTE PTR [rdi]
0x30013f7a: movdqu xmm3, XMMWORD PTR [rsi+0x30]
0x30013f7f: movdqu xmm2, XMMWORD PTR [rsi+0x20]
0x30013f84: movdqu XMMWORD PTR [rdi+0x30],xmm3
0x30013f89: movdqu XMMWORD PTR [rdi+0x20],xmm2
0x30013f8e: movdqu xmm1, XMMWORD PTR [rsi+0x10]
0x30013f93: movdqu xmm0, XMMWORD PTR [rsi]
0x30013f97: movdqu XMMWORD PTR [rdi+0x10], xmm1
0x30013f9c: movdqu XMMWORD PTR [rdi], xmm0
在此模式中,rsi 保存源(src)地址,rdi 保存目标(dst)地址,xmm 寄存器用作临时寄存器。此代码的迭代次数与 copylen_in_bytes/64 一样多。如您所见,这里遵循 ld-ld-st-st-ld-ld-st-st 加载存储模式。
模式2
0x30013f74: prefetchnta BYTE PTR [rsi]
0x30013f77: prefetchnta BYTE PTR [rdi]
0x30013f7a: movdqu xmm3, XMMWORD PTR [rsi+0x30]
0x30013f7f: movdqu XMMWORD PTR [rdi+0x30], xmm3
0x30013f84: movdqu xmm2, XMMWORD PTR [rsi+0x20]
0x30013f89: movdqu XMMWORD PTR [rdi+0x20], xmm2
0x30013f8e: movdqu xmm1, XMMWORD PTR [rsi+0x10]
0x30013f93: movdqu XMMWORD PTR [rdi+0x10], xmm1
0x30013f98: movdqu xmm0, XMMWORD PTR [rsi]
0x30013f9c: movdqu XMMWORD PTR [rdi], xmm0
在模式 2 中,遵循 ld-st-ld-st-ld-st-ld-st 模式。
观察
在运行此代码几百次时,其中 src 和 dst 在不同的 8 字节边界处对齐,我观察到以下内容:
在 Westmere (至强 X5690)
Pattern1 表现出非常高的运行间差异。
Pattern2 几乎没有变化。
Pattern2 上的最小时间(最快观察时间)比 Pattern1 上的最小时间高(约 8%)。
在 Ivybridge (Xean E5-2697 v2)
Pattern1 表现出非常高的运行间差异。
Pattern2 几乎没有变化。
Pattern2 的最小时间比 Pattern1 的最小时间高(~20%)。
哈斯韦尔(酷睿 i7-4770)
Pattern1 没有表现出非常高的运行间差异。
Pattern2 几乎没有变化。
Pattern2 的最小时间比 Pattern1 的最小时间高(~20%)。
奇怪的是,在 Westmere 和 Ivybridge 上,src/dest 的对齐与不良结果(导致高方差)之间似乎没有相关性。我看到相同 src/dest 对齐的好坏数字。
问题
我知道跨越缓存行的 movdqu 会比不跨越缓存行的 movdqu 执行得更差,但我不明白以下内容:
1) 为什么 Pattern1 在 Westmere 和 Ivybridge 上表现出高方差?加载存储的顺序有何不同?
2) 为什么 Pattern2 上的最小时间比 Pattern1 慢,跨不同的架构?
感谢您花时间阅读这篇长文。
卡尔提克