【问题标题】:Proper use of _mm256_maskload_ps for loading less than 8 floats into __m256正确使用 _mm256_maskload_ps 将少于 8 个浮点数加载到 __m256
【发布时间】:2021-08-29 01:55:47
【问题描述】:

我无法确定需要使用_mm256_maskload_ps 设置哪些位进行屏蔽。

documentation 声明掩码是“根据掩码寄存器的每个双字的最高有效位计算的整数值”

解析出来,我认为有 4 个 64 位整数。我想屏蔽 8 个值,因此我可以将其视为 8 个 32 位整数(这是我的理解变得不稳定的地方),每个整数都有一个为符号保留的 MSB,1 为负数,0 为正数。所以我可以为 8 个 32 位整数设置 -1 为“请加载这个”和 0 为“不要加载这个”,我的掩码应该是正确的。但是,我们实际上有 4 个 64 位整数,所以也许我必须打包它们?

基本上我正在寻找一种方法来描述一个掩码,以便在我这样做时设置第一个元素中的 1,2,3...8 个_mm256_maskload_ps

注意: 有趣的是,当我的掩码为{-1, 0, 0, 0} 时,前两个元素被设置。当我的掩码是{0xFFFFFFFF, 0, 0, 0} 时,只有第一个元素被设置。

#include <iostream>
#include <immintrin.h>
#include <string>

using namespace std;

int main()
{
  float a[3] {1,2,3};
  float b[3] {11, 22, 33};

  auto disp = [](float *arr) {
    cout << "[";
    string sep;
    for (size_t i = 0; i < 3; i++)
    {
      cout << sep << arr[i];
      sep = ", ";
    }
    cout << "]";
    cout << endl;
  };
  disp(a);
  disp(b);

  __m256 _a, _b;
  __m256i _load_mask = {-1, 0, 0, 0};


  _a = _mm256_maskload_ps(a, _load_mask);
  _b = _mm256_maskload_ps(b, _load_mask);
  _a = _mm256_add_ps(_a, _b);


  float c[8];
  _mm256_storeu_ps(c, _a);
  disp(c);

  return 0;
}

显示

[1, 2, 3]
[11, 22, 33]
[12, 24, 0]

编译时使用

!clang++ -mavx -Wall -Wextra -std=c++17 -stdlib=libc++ -ggdb % -o $(basename -s .cpp %

在我的 Mac 上,% 是文件名

【问题讨论】:

    标签: c++ simd avx


    【解决方案1】:

    双字是 32 位,而不是 64。字 = 16,双字 = 32,四字 = 64。前两个元素被选中,因为 -1 在所有 64 位中都是 1,因此当 maskload 将其视为两个将设置两个元素的最高位,而不是一个 64 位值。 0xFFFFFFFF,OTOH,是设置的最低 32 位和未设置的最高 32 位。由于 x86 是 little-endian,因此最低有效位在前,这就是为什么您最终选择了他的第一个元素而不是第二个元素。

    documentation in the intrinsics guide 在这里要好得多。

    请注意,在 GCC/clang 上,__m256i 是使用 vector extensions 实现的。但是,MSVC 不支持向量扩展,因此您的代码将无法在那里工作。此外,GCC 和 clang 都使用 64 位值的向量,即使 all 整数向量使用相同的 __m256i 类型,所以您可能希望使用 _mm256_set_epi32、@987654327 @ 或 _mm256_load_si256 无论如何都要创建你的 _load_mask

    哦,在 C 和 C++ 中都以下划线 are reserved 开头的名称。不要那样做。如果您确实需要传达它是一个内部变量或其他东西,您可以使用尾随下划线,但在您上面发布的代码中,我真的没有看到这样做的理由。

    【讨论】:

    • “哦,以下划线开头的名称是保留的” - 错误,请再次阅读该链接。当其他规则都不适用时,“以下划线开头”仅保留在全局命名空间中,但 OP 引入的唯一符号在本地范围内。
    • @o11c:出于风格原因,我仍然建议不要在 var 名称中使用前导下划线,尤其是,在使用 Intel 内部函数时,很多类型和函数名称都以 _ 开头.如果你有一个数组ava 是从它加载的 SIMD 向量的一个不错的名称,如果没有更语义上有意义的短名称,你可以想出。
    【解决方案2】:

    __m256i 类型中存储的整数是 64 位整数。当您使用 -1 时,会将所有 64 位设置为 1(即 _load_mask 中的前两个 32 位整数)。使用0xFFFFFFFF 只会设置 32 位,导致第一个整数设置 MSB,而第二个(以及其他六个)不会。

    您不应该以这种方式初始化 YMM 寄存器之一。 (这是不可移植的,因为其他编译器对 __m256i 和其他 SSE/AVX 类型使用联合,并且聚合初始化将初始化联合的第一个成员,可能是 8 字节整数。)

    在这种情况下,您应该使用适当的内在函数:

    static const int32_t mask_bits[8] = { -1, -1, 0, 0, 0, 0, 0, 0};
    _mm256_loadu_si256((const __m256i*)mask_bits);
    

    如果你有 AVX512 支持,你可以使用_mm256_loadu_epi32 来避免强制转换。

    请参阅this answer 了解说明。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-29
      • 1970-01-01
      • 2015-09-10
      • 2019-03-01
      相关资源
      最近更新 更多