Visual Studio 2017:_mm_load_ps 经常编译成 movups

Visual Studio 2017: _mm_load_ps often compiled to movups

我正在查看为我的代码生成的程序集(使用 Visual Studio 2017)并注意到 _mm_load_ps 经常(总是?)编译为 movups。

我在 _mm_load_ps 上使用的数据定义如下:

struct alignas(16) Vector {
    float v[4];
}

// often embedded in other structs like this
struct AABB {
    Vector min;
    Vector max;
    bool intersection(/* parameters */) const;
}

现在,当我使用这个结构时,会发生以下情况:

// this code
__mm128 bb_min = _mm_load_ps(min.v);

// generates this
movups  xmm4, XMMWORD PTR [r8]

由于 alignas(16),我期待 movaps。在这种情况下,我还需要其他东西来说服编译器使用 movaps 吗?

编辑:我的问题与 this question 不同,因为我没有遇到任何崩溃。该结构是专门对齐的,我也在使用对齐分配。相反,我很好奇为什么编译器将 _mm_load_ps(对齐内存的内在函数)切换为 movups。如果我知道结构是在一个对齐的地址分配的,并且我通过这个*调用它,那么使用 movaps 是安全的,对吗?

在最新版本的 Visual Studio 和英特尔编译器(最近为 post-2013?)中,编译器很少再生成对齐的 SIMD load/stores。

为 AVX 或更高版本编译时:

  • Microsoft 编译器(>VS2013?)不生成对齐加载。但它仍然会生成对齐的商店。
  • 英特尔编译器(> Parallel Studio 2012?)不再执行此操作。但您仍会在 ICC 编译的二进制文件中看到它们,它们位于手动优化的库中,例如 memset().
  • 从 GCC 6.1 开始,当您使用对齐的内部函数时,它仍然会生成对齐的 load/stores。

允许编译器执行此操作,因为如果代码编写正确,则不会丢失功能。当地址对齐时,从 Nehalem 开始的所有处理器都不会因未对齐 load/stores 而受到惩罚。

微软在这个问题上的立场是"helps the programmer by not crashing"。不幸的是,我无法再从 Microsoft 找到此声明的原始来源。在我看来,这恰恰相反,因为它隐藏了错位惩罚。从正确性的角度来看,它也隐藏了不正确的代码。

无论如何,无条件地使用未对齐 load/stores 确实可以稍微简化编译器。

新关联:

  • 从 Parallel Studio 2018 开始,英特尔编译器根本不再生成对齐的移动 - 即使对于 Nehalem 之前的目标也是如此。
  • 从 Visual Studio 2017 年开始,Microsoft 编译器也不再生成对齐的移动 - 即使针对 AVX 之前的硬件也是如此。

这两种情况都会导致旧处理器不可避免的性能下降。但似乎this is intentional因为英特尔和微软都不再关心旧处理器。


唯一不受此影响的 load/store 内在函数是非时间 load/stores。没有未对齐的等价物,因此编译器别无选择。

因此,如果您只想测试代码的正确性,可以用 load/store 内在函数代替非时态内在函数。但是要小心,不要让这样的东西进入生产代码,因为 NT load/stores(尤其是 NT 商店)是一把双刃剑,如果您不知道自己在做什么,它可能会伤到您。