正确使用 _mm256_maskload_ps 将少于 8 个浮点数加载到 __m256
Posted
技术标签:
【中文标题】正确使用 _mm256_maskload_ps 将少于 8 个浮点数加载到 __m256【英文标题】:Proper use of _mm256_maskload_ps for loading less than 8 floats into __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 上,%
是文件名
【问题讨论】:
【参考方案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 内部函数时,很多类型和函数名称都以_
开头.如果你有一个数组a
,va
是从它加载的 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 了解说明。
【讨论】:
以上是关于正确使用 _mm256_maskload_ps 将少于 8 个浮点数加载到 __m256的主要内容,如果未能解决你的问题,请参考以下文章