使用 AVX 与 NaN 的比较
Comparison with NaN using AVX
我正在尝试使用英特尔的 AVX
内在函数为 BPSK
创建一个快速解码器。我有一组表示为交错浮点数的复数,但由于 BPSK
调制,只需要实数部分(或偶数索引浮点数)。每个浮点数 x
在 x < 0
时映射到 0
,在 x >= 0
时映射到 1
。这是使用以下例程完成的:
static inline void
normalize_bpsk_constellation_points(int32_t *out, const complex_t *in, size_t num)
{
static const __m256 _min_mask = _mm256_set1_ps(-1.0);
static const __m256 _max_mask = _mm256_set1_ps(1.0);
static const __m256 _mul_mask = _mm256_set1_ps(0.5);
__m256 res;
__m256i int_res;
size_t i;
gr_complex temp;
float real;
for(i = 0; i < num; i += COMPLEX_PER_AVX_REG){
res = _mm256_load_ps((float *)&in[i]);
/* clamp them to avoid segmentation faults due to indexing */
res = _mm256_max_ps(_min_mask, _mm256_min_ps(_max_mask, res));
/* Scale accordingly for proper indexing -1->0, 1->1 */
res = _mm256_add_ps(res, _max_mask);
res = _mm256_mul_ps(res, _mul_mask);
/* And then round to the nearest integer */
res = _mm256_round_ps(res, _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC);
int_res = _mm256_cvtps_epi32(res);
_mm256_store_si256((__m256i *) &out[2*i], int_res);
}
}
首先,我将所有接收到的浮点数限制在 [-1, 1]
范围内。然后经过一些适当的缩放后,结果四舍五入到最接近的整数。这会将 0.5
以上的所有浮点数映射到 1
,并将 0.5
以下的所有浮点数映射到 0
。
如果输入的浮点数是正常数字,则程序运行良好。但是,由于前面阶段的一些情况,有可能一些输入的浮点数是NaN
或者-NaN
。在这种情况下,'NaN' 数字通过 _mm256_max_ps()
、_mm256_min_ps()
和所有其他 AVX
函数传播,导致 -2147483648
的整数映射,这当然会导致我程序因索引无效而崩溃。
是否有任何解决方法可以避免此问题,或者至少使用 AVX
将 NaN
设置为 0
?
您可以通过简单的方式开始、比较和屏蔽:(未测试)
res = _mm256_cmp_ps(res, _mm256_setzero_ps(), _CMP_NLT_US);
ires = _mm256_srl_epi32(_mm256_castps_si256(res), 31);
或移位和异或:(也未测试)
ires = _mm256_srl_epi32(_mm256_castps_si256(res), 31);
ires = _mm256_xor_epi32(ires, _mm256_set1_epi32(1));
此版本还将关心 NaN 的符号(并忽略 NaN-ness)。
无 AVX2 的替代方案(未测试)
res = _mm256_cmp_ps(res, _mm256_setzero_ps(), _CMP_NLT_US);
res = _mm256_and_ps(res, _mm256_set1_ps(1.0f));
ires = _mm256_cvtps_epi32(res);
Harold 针对您真正提出的问题发布了一个很好的解决方案,但我想明确指出,在限制时消除 NaN 值是完全简单的。如果任一参数是 NaN,MINPS 和 MAXPS 只需 return 第二个参数。所以你需要做的就是交换参数顺序,NaN 也会被限制。例如,以下内容会将 NaN 限制为 _min_mask:
res = _mm256_max_ps(_mm256_min_ps(_max_mask, res), _min_mask);
我正在尝试使用英特尔的 AVX
内在函数为 BPSK
创建一个快速解码器。我有一组表示为交错浮点数的复数,但由于 BPSK
调制,只需要实数部分(或偶数索引浮点数)。每个浮点数 x
在 x < 0
时映射到 0
,在 x >= 0
时映射到 1
。这是使用以下例程完成的:
static inline void
normalize_bpsk_constellation_points(int32_t *out, const complex_t *in, size_t num)
{
static const __m256 _min_mask = _mm256_set1_ps(-1.0);
static const __m256 _max_mask = _mm256_set1_ps(1.0);
static const __m256 _mul_mask = _mm256_set1_ps(0.5);
__m256 res;
__m256i int_res;
size_t i;
gr_complex temp;
float real;
for(i = 0; i < num; i += COMPLEX_PER_AVX_REG){
res = _mm256_load_ps((float *)&in[i]);
/* clamp them to avoid segmentation faults due to indexing */
res = _mm256_max_ps(_min_mask, _mm256_min_ps(_max_mask, res));
/* Scale accordingly for proper indexing -1->0, 1->1 */
res = _mm256_add_ps(res, _max_mask);
res = _mm256_mul_ps(res, _mul_mask);
/* And then round to the nearest integer */
res = _mm256_round_ps(res, _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC);
int_res = _mm256_cvtps_epi32(res);
_mm256_store_si256((__m256i *) &out[2*i], int_res);
}
}
首先,我将所有接收到的浮点数限制在 [-1, 1]
范围内。然后经过一些适当的缩放后,结果四舍五入到最接近的整数。这会将 0.5
以上的所有浮点数映射到 1
,并将 0.5
以下的所有浮点数映射到 0
。
如果输入的浮点数是正常数字,则程序运行良好。但是,由于前面阶段的一些情况,有可能一些输入的浮点数是NaN
或者-NaN
。在这种情况下,'NaN' 数字通过 _mm256_max_ps()
、_mm256_min_ps()
和所有其他 AVX
函数传播,导致 -2147483648
的整数映射,这当然会导致我程序因索引无效而崩溃。
是否有任何解决方法可以避免此问题,或者至少使用 AVX
将 NaN
设置为 0
?
您可以通过简单的方式开始、比较和屏蔽:(未测试)
res = _mm256_cmp_ps(res, _mm256_setzero_ps(), _CMP_NLT_US);
ires = _mm256_srl_epi32(_mm256_castps_si256(res), 31);
或移位和异或:(也未测试)
ires = _mm256_srl_epi32(_mm256_castps_si256(res), 31);
ires = _mm256_xor_epi32(ires, _mm256_set1_epi32(1));
此版本还将关心 NaN 的符号(并忽略 NaN-ness)。
无 AVX2 的替代方案(未测试)
res = _mm256_cmp_ps(res, _mm256_setzero_ps(), _CMP_NLT_US);
res = _mm256_and_ps(res, _mm256_set1_ps(1.0f));
ires = _mm256_cvtps_epi32(res);
Harold 针对您真正提出的问题发布了一个很好的解决方案,但我想明确指出,在限制时消除 NaN 值是完全简单的。如果任一参数是 NaN,MINPS 和 MAXPS 只需 return 第二个参数。所以你需要做的就是交换参数顺序,NaN 也会被限制。例如,以下内容会将 NaN 限制为 _min_mask:
res = _mm256_max_ps(_mm256_min_ps(_max_mask, res), _min_mask);