通常有两种类型的SIMD指令:
A。使用对齐的内存地址的地址,如果地址未在操作数大小边界上对齐,则会引发一般保护(#GP)异常:
movaps xmm0, xmmword ptr [rax]
vmovaps ymm0, ymmword ptr [rax]
vmovaps zmm0, zmmword ptr [rax]
B。而那些使用未对齐内存地址的地址,则不会引发此类异常:
movups xmm0, xmmword ptr [rax]
vmovups ymm0, ymmword ptr [rax]
vmovups zmm0, zmmword ptr [rax]
但是我很好奇,为什么我要朝自己的脚开枪并完全使用第一组中对齐的内存指令?
答案 0 :(得分:11)
movups/vmovups
。统一访问案例中讨论的相同处罚(见下)也适用于此。此外,跨越缓存行或虚拟页面边界的访问总是会在所有处理器上造成损失。movups/vmovups
在管道的前端和后端消耗更多的资源(最多两倍)。换句话说,就延迟和/或吞吐量而言,movups/vmovups
的速度可能是movaps/vmovaps
的两倍。因此,如果您不关心较旧的微体系结构,则两者在技术上是等效的。尽管如果您知道或期望数据会对齐,则应使用对齐的指令来确保数据确实对齐,而不必在代码中添加显式检查。
答案 1 :(得分:7)
我认为即使在“ Intel Nehalem和更高版本(包括Silvermont和更高版本)以及AMD Bulldozer和更高版本”上使用_mm_loadu_ps
和_mm_load_ps
之间也存在细微的差异,这可能会对性能产生影响。
除非使用启用了允许未对齐内存操作数的AVX进行编译,否则只能使用load
而不是loadu
内部函数来完成将负载和另一操作(例如乘法)折叠的操作。
考虑以下代码
#include <x86intrin.h>
__m128 foo(float *x, float *y) {
__m128 vx = _mm_loadu_ps(x);
__m128 vy = _mm_loadu_ps(y);
return vx*vy;
}
这得到converted to
movups xmm0, XMMWORD PTR [rdi]
movups xmm1, XMMWORD PTR [rsi]
mulps xmm0, xmm1
但是,如果使用对齐的载荷固有函数(_mm_load_ps
),则会将其编译为
movaps xmm0, XMMWORD PTR [rdi]
mulps xmm0, XMMWORD PTR [rsi]
保存一条指令。但是,如果编译器可以使用VEX编码的负载,则为only two instructions for unaligned as well。
vmovups xmm0, XMMWORD PTR [rsi]
vmulps xmm0, xmm0, XMMWORD PTR [rdi]
因此,尽管在Intel Nehalem和更高版本,Silvermont和更高版本,AMD Bulldozer和更高版本上使用movaps
和movups
指令时,性能没有差异,但仍可以进行对齐访问。
但是在未启用AVX的情况下进行编译时,在使用_mm_loadu_ps
和_mm_load_ps
intrinsics 时,在性能上可能会有所不同。权衡不是movaps
与movups
的权衡,而是在movups
或将负载折叠到ALU指令之间。 (当向量仅用作一件事的输入时,会发生这种情况,否则编译器将使用mov*
加载将结果存入寄存器以供重用。)