当我在快速ADD循环(Speed up x64 assembler ADD loop)上工作时,我正在使用SSE和AVX指令测试内存访问。要添加,我必须读取两个输入并产生一个输出。所以我编写了一个虚拟例程,它将两个x64值读入寄存器,然后将其写回存储器而不进行任何操作。这当然没用,我只做了基准测试。
我使用一个展开的循环,每个循环处理64个字节。它由8个这样的块组成:
mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
mov QWORD PTR [rcx+r11*8-64], rax
然后我将其升级到SSE2。现在我使用4个这样的块:
movdqa xmm0, XMMWORD PTR [rdx+r11*8-64]
movdqa xmm1, XMMWORD PTR [r8+r11*8-64]
movdqa XMMWORD PTR [rcx+r11*8-64], xmm0
后来我使用了AVX(每个寄存器256位)。我有2个这样的块:
vmovdqa ymm0, YMMWORD PTR [rdx+r11*8-64]
vmovdqa ymm1, YMMWORD PTR [r8+r11*8-64]
vmovdqa YMMWORD PTR [rcx+r11*8-64], ymm0
到目前为止,还不那么引人注目。有趣的是基准测试结果:当我在1k + 1k = 1k 64位字(即两次8kb输入和一次8kb输出)上运行三种不同方法时,我得到奇怪的结果。以下每个时序用于处理两次64字节输入到64字节输出。
我的问题是:为什么AVX方法比SSE2方法慢?(虽然不是很多)?我预计它至少会与之相提并论。使用YMM寄存器会花费多少额外的时间吗?内存已对齐(否则会获得GPF)。
有没有人对此有解释?
答案 0 :(得分:12)
在Sandybridge / Ivybridge上,256b AVX加载和存储分为两个128b操作[正如Peter Cordes所说,这些不是很好的μops,但它需要两个周期才能在加载/存储中清除端口]执行单元,因此没有理由期望使用这些指令的版本更快。
为什么慢?我想到了两种可能性:
对于基本+索引+偏移量寻址,128b负载的延迟为6个周期,而256b负载的延迟为7个周期(英特尔优化手册中的表2-8)。虽然您的基准测试应受 throughput 而非延迟的约束,但延迟时间越长意味着处理器需要更长的时间才能从任何打嗝中恢复(管道气泡或预测未命中或中断服务或......),确实有一些影响。
表示,对于256b负载,高速缓存行和页面交叉的惩罚可能比128b负载更大。如果您的加载不是全部32字节对齐,这也可以解释您在使用256b加载/存储操作时看到的速度减慢:
示例11-12显示了SAXPY的两个未对齐实现 地址。备选1使用32字节加载,备选2使用16 字节加载。这些代码示例使用两个源缓冲区执行, src1,src2,与32字节对齐的4字节偏移,和a 目标缓冲区,DST,即32字节对齐。使用两个16字节 代替32字节内存访问的内存操作执行得更快。