AVX2 float 比较并得到 0.0 或 1.0 而不是全 0 或全 1 位
AVX2 float compare and get 0.0 or 1.0 instead of all-0 or all-one bits
基本上,在生成的向量中,我想为所有 > 1 的输入浮点值保存 1.0,而为所有 <= 1 的输入浮点值保存 0.0。这是我的代码,
float f[8] = {1.2, 0.5, 1.7, 1.9, 0.34, 22.9, 18.6, 0.7};
float r[8]; // Must be {1, 0, 1, 1, 0, 1, 1, 0}
__m256i tmp1 = _mm256_cvttps_epi32(_mm256_loadu_ps(f));
__m256i tmp2 = _mm256_cmpgt_epi32(tmp1, _mm256_set1_epi32(1));
_mm256_store_ps(r, _mm256_cvtepi32_ps(tmp2));
for(int i = 0; i < 8; i++)
std::cout << f[i] << " : " << r[i] << std::endl;
但我没有得到正确的结果。这就是我得到的。为什么 AVX2 关系操作对我来说不能正常工作?
1.2 : 0
0.5 : 0
1.7 : 0
1.9 : 0
0.34 : 0
22.9 : -1
18.6 : -1
0.7 : 0
当使用 float 转换为 int 时
_mm256_cvttps_epi32 那么返回的整数是截断的(向零舍入)值。即1.2、1.7、1.9转换为1,不大于1。
_mm256_cmpgt_epi32 的输出不是 1,而是 "all ones",来自 docs:
... if the s1 data element is greater than the corresponding element in s2, then the corresponding element in the destination vector is set to all 1s.
"All ones" 是在使用二进制补码整数时,如您的结果所示,减一。
题外话:
- 为什么要使用未对齐的加载和对齐的存储?
- 你应该看看
_mm256_cmp_ps
我认为您的问题最好使用 _mm256_cmp_ps
。为此,我实施了以下程序。这比你想要的更多。如果你想保存一个,你应该将所有 mask
元素设置为 1
,但如果你想保存另一个数字,你可以将掩码值更改为任何你想要的。
//gcc 6.2, Linux-mint, Skylake
#include <stdio.h>
#include <x86intrin.h>
float __attribute__(( aligned(32))) f[8] = {1.2, 0.5, 1.7, 1.9, 0.34, 22.9, 18.6, 1.0};
// float __attribute__(( aligned(32))) r[8]; // Must be {1, 0, 1, 1, 0, 1, 1, 0}
// in C++11, use alignas(32). Or C11 _Alignas(32), instead of GNU C __attribute__.
void printVecps(__m256 vec)
{
float tempps[8];
_mm256_store_ps(&tempps[0], vec);
printf(" [0]=%3.2f, [1]=%3.2f, [2]=%3.2f, [3]=%3.2f, [4]=%3.2f, [5]=%3.2f, [6]=%3.2f, [7]=%3.2f \n",
tempps[0],tempps[1],tempps[2],tempps[3],tempps[4],tempps[5],tempps[6],tempps[7]) ;
}
int main()
{
__m256 mask = _mm256_set1_ps(1.0), vec1, vec2, vec3;
vec1 = _mm256_load_ps(&f[0]); printf("vec1 : ");printVecps(vec1); // load vector values from f[0]-f[7]
vec2 = _mm256_cmp_ps ( mask, vec1, _CMP_LT_OS /*0x1*/);
printf("vec2 : ");printVecps(vec2); // compare them to mask (less)
vec3 = _mm256_min_ps (vec2 , mask); printf("vec3 : ");printVecps(vec3); // select minimum from mask and compared results
return 0;
}
mask = {1,1,1,1,1,1,1,1}
的输出是:
vec1 : [0]=1.20, [1]=0.50, [2]=1.70, [3]=1.90, [4]=0.34, [5]=22.90, [6]=18.60, [7]=1.00
vec2 : [0]=-nan, [1]=0.00, [2]=-nan, [3]=-nan, [4]=0.00, [5]=-nan, [6]=-nan, [7]=0.00
vec3 : [0]=1.00, [1]=0.00, [2]=1.00, [3]=1.00, [4]=0.00, [5]=1.00, [6]=1.00, [7]=0.00
mask = {2,2,2,2,2,2,2,2}
是:
vec1 : [0]=1.20, [1]=0.50, [2]=1.70, [3]=1.90, [4]=0.34, [5]=22.90, [6]=18.60, [7]=1.00
vec2 : [0]=0.00, [1]=0.00, [2]=0.00, [3]=0.00, [4]=0.00, [5]=-nan, [6]=-nan, [7]=0.00
vec3 : [0]=0.00, [1]=0.00, [2]=0.00, [3]=0.00, [4]=0.00, [5]=2.00, [6]=2.00, [7]=0.00
这取决于 _mm256_min_ps
与 NaN 的非交换行为,以用 1.0 替换 NaN 元素。 NaN > 1.0 : NaN : 1.0
= 1.0
,因为 NaN > anything
总是假的。
注意 (即使它知道 minps
指令不是)。使用最新的 gcc,或确保 gcc 选择按照此算法所需的顺序使用操作数编译您的代码。 (或使用 clang)。 gcc 可能永远不会与 AVX 交换操作数,只会与 SSE 交换(以避免额外的 movapd
指令),但最安全的是使用 gcc7 或更高版本。
基本上,在生成的向量中,我想为所有 > 1 的输入浮点值保存 1.0,而为所有 <= 1 的输入浮点值保存 0.0。这是我的代码,
float f[8] = {1.2, 0.5, 1.7, 1.9, 0.34, 22.9, 18.6, 0.7};
float r[8]; // Must be {1, 0, 1, 1, 0, 1, 1, 0}
__m256i tmp1 = _mm256_cvttps_epi32(_mm256_loadu_ps(f));
__m256i tmp2 = _mm256_cmpgt_epi32(tmp1, _mm256_set1_epi32(1));
_mm256_store_ps(r, _mm256_cvtepi32_ps(tmp2));
for(int i = 0; i < 8; i++)
std::cout << f[i] << " : " << r[i] << std::endl;
但我没有得到正确的结果。这就是我得到的。为什么 AVX2 关系操作对我来说不能正常工作?
1.2 : 0
0.5 : 0
1.7 : 0
1.9 : 0
0.34 : 0
22.9 : -1
18.6 : -1
0.7 : 0
当使用 float 转换为 int 时 _mm256_cvttps_epi32 那么返回的整数是截断的(向零舍入)值。即1.2、1.7、1.9转换为1,不大于1。
_mm256_cmpgt_epi32 的输出不是 1,而是 "all ones",来自 docs:
... if the s1 data element is greater than the corresponding element in s2, then the corresponding element in the destination vector is set to all 1s.
"All ones" 是在使用二进制补码整数时,如您的结果所示,减一。
题外话:
- 为什么要使用未对齐的加载和对齐的存储?
- 你应该看看
_mm256_cmp_ps
我认为您的问题最好使用 _mm256_cmp_ps
。为此,我实施了以下程序。这比你想要的更多。如果你想保存一个,你应该将所有 mask
元素设置为 1
,但如果你想保存另一个数字,你可以将掩码值更改为任何你想要的。
//gcc 6.2, Linux-mint, Skylake
#include <stdio.h>
#include <x86intrin.h>
float __attribute__(( aligned(32))) f[8] = {1.2, 0.5, 1.7, 1.9, 0.34, 22.9, 18.6, 1.0};
// float __attribute__(( aligned(32))) r[8]; // Must be {1, 0, 1, 1, 0, 1, 1, 0}
// in C++11, use alignas(32). Or C11 _Alignas(32), instead of GNU C __attribute__.
void printVecps(__m256 vec)
{
float tempps[8];
_mm256_store_ps(&tempps[0], vec);
printf(" [0]=%3.2f, [1]=%3.2f, [2]=%3.2f, [3]=%3.2f, [4]=%3.2f, [5]=%3.2f, [6]=%3.2f, [7]=%3.2f \n",
tempps[0],tempps[1],tempps[2],tempps[3],tempps[4],tempps[5],tempps[6],tempps[7]) ;
}
int main()
{
__m256 mask = _mm256_set1_ps(1.0), vec1, vec2, vec3;
vec1 = _mm256_load_ps(&f[0]); printf("vec1 : ");printVecps(vec1); // load vector values from f[0]-f[7]
vec2 = _mm256_cmp_ps ( mask, vec1, _CMP_LT_OS /*0x1*/);
printf("vec2 : ");printVecps(vec2); // compare them to mask (less)
vec3 = _mm256_min_ps (vec2 , mask); printf("vec3 : ");printVecps(vec3); // select minimum from mask and compared results
return 0;
}
mask = {1,1,1,1,1,1,1,1}
的输出是:
vec1 : [0]=1.20, [1]=0.50, [2]=1.70, [3]=1.90, [4]=0.34, [5]=22.90, [6]=18.60, [7]=1.00
vec2 : [0]=-nan, [1]=0.00, [2]=-nan, [3]=-nan, [4]=0.00, [5]=-nan, [6]=-nan, [7]=0.00
vec3 : [0]=1.00, [1]=0.00, [2]=1.00, [3]=1.00, [4]=0.00, [5]=1.00, [6]=1.00, [7]=0.00
mask = {2,2,2,2,2,2,2,2}
是:
vec1 : [0]=1.20, [1]=0.50, [2]=1.70, [3]=1.90, [4]=0.34, [5]=22.90, [6]=18.60, [7]=1.00
vec2 : [0]=0.00, [1]=0.00, [2]=0.00, [3]=0.00, [4]=0.00, [5]=-nan, [6]=-nan, [7]=0.00
vec3 : [0]=0.00, [1]=0.00, [2]=0.00, [3]=0.00, [4]=0.00, [5]=2.00, [6]=2.00, [7]=0.00
这取决于 _mm256_min_ps
与 NaN 的非交换行为,以用 1.0 替换 NaN 元素。 NaN > 1.0 : NaN : 1.0
= 1.0
,因为 NaN > anything
总是假的。
注意 minps
指令不是)。使用最新的 gcc,或确保 gcc 选择按照此算法所需的顺序使用操作数编译您的代码。 (或使用 clang)。 gcc 可能永远不会与 AVX 交换操作数,只会与 SSE 交换(以避免额外的 movapd
指令),但最安全的是使用 gcc7 或更高版本。