正确使用 _mm256_maskload_ps 将少于 8 个浮点数加载到 __m256
Proper use of _mm256_maskload_ps for loading less than 8 floats into __m256
我无法确定需要使用 _mm256_maskload_ps
.
为屏蔽设置哪些位
documentation 声明掩码是“根据掩码寄存器的每个双字的最高有效位计算的整数值”
解析出来,我认为有 4 个 64 位整数。我想屏蔽 8 个值,所以我可以将其视为 8 个 32 位整数(这是我的理解变得不稳定的地方),每个整数都有一个为符号保留的 MSB,1 为负,0 为正。所以我可以为 8 个 32 位整数设置 -1 表示“请加载这个”,0 表示“不要加载这个”,我的掩码应该是正确的。但是,我们实际上有 4 个 64 位整数,所以也许我必须打包它们?
本质上,我正在寻找一种方法来描述掩码,以便在我执行 _mm256_maskload_ps
时设置第一个元素的 1,2,3...8
注意:
有趣的是,当我的掩码是 {-1, 0, 0, 0}
时,前 2 个元素被设置。当我的掩码是 {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 上,其中 %
是文件名
一个双字是 32 位,而不是 64 位。字 = 16,双字 = 32,四字 = 64。选择前两个元素是因为 -1 是所有 64 位的全一,所以当掩码加载处理它时作为两个 32 位值而不是一个 64 位值,两个元素的最高位将被设置。 0xFFFFFFFF,OTOH,是设置的最低 32 位和未设置的最高 32 位。由于 x86 是小尾数法,最低有效位在前,这就是为什么您最终选择了第一个元素而不是第二个元素。
这里的documentation in the intrinsics guide好多了。
请注意,在 GCC/clang 上,__m256i
是使用 vector extensions 实现的。但是,MSVC 不支持矢量扩展,因此您的代码无法在其中运行。此外,即使相同的 __m256i
类型用于 all 整数向量,GCC 和 clang 都使用 64 位值的向量,因此您可能想要使用 _mm256_set_epi32
、_mm256_setr_epi32
或 _mm256_load_si256
来创建您的 _load_mask
。
哦,C 和 C++ 中的名称都以下划线 are reserved 开头。不要那样做。如果你真的需要传达它是一个内部变量或其他东西,你可以使用尾随下划线,但我真的没有看到在你上面发布的代码中这样做的理由。
您的 __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
来避免转换。
有关解释,请参阅 。
我无法确定需要使用 _mm256_maskload_ps
.
documentation 声明掩码是“根据掩码寄存器的每个双字的最高有效位计算的整数值”
解析出来,我认为有 4 个 64 位整数。我想屏蔽 8 个值,所以我可以将其视为 8 个 32 位整数(这是我的理解变得不稳定的地方),每个整数都有一个为符号保留的 MSB,1 为负,0 为正。所以我可以为 8 个 32 位整数设置 -1 表示“请加载这个”,0 表示“不要加载这个”,我的掩码应该是正确的。但是,我们实际上有 4 个 64 位整数,所以也许我必须打包它们?
本质上,我正在寻找一种方法来描述掩码,以便在我执行 _mm256_maskload_ps
注意:
有趣的是,当我的掩码是 {-1, 0, 0, 0}
时,前 2 个元素被设置。当我的掩码是 {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 上,其中 %
是文件名
一个双字是 32 位,而不是 64 位。字 = 16,双字 = 32,四字 = 64。选择前两个元素是因为 -1 是所有 64 位的全一,所以当掩码加载处理它时作为两个 32 位值而不是一个 64 位值,两个元素的最高位将被设置。 0xFFFFFFFF,OTOH,是设置的最低 32 位和未设置的最高 32 位。由于 x86 是小尾数法,最低有效位在前,这就是为什么您最终选择了第一个元素而不是第二个元素。
这里的documentation in the intrinsics guide好多了。
请注意,在 GCC/clang 上,__m256i
是使用 vector extensions 实现的。但是,MSVC 不支持矢量扩展,因此您的代码无法在其中运行。此外,即使相同的 __m256i
类型用于 all 整数向量,GCC 和 clang 都使用 64 位值的向量,因此您可能想要使用 _mm256_set_epi32
、_mm256_setr_epi32
或 _mm256_load_si256
来创建您的 _load_mask
。
哦,C 和 C++ 中的名称都以下划线 are reserved 开头。不要那样做。如果你真的需要传达它是一个内部变量或其他东西,你可以使用尾随下划线,但我真的没有看到在你上面发布的代码中这样做的理由。
您的 __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
来避免转换。
有关解释,请参阅