使用 AVX 与 NaN 的比较

Posted

技术标签:

【中文标题】使用 AVX 与 NaN 的比较【英文标题】:Comparison with NaN using AVX 【发布时间】:2015-08-04 20:31:17 【问题描述】:

我正在尝试使用 Intel 的 AVX 内部函数为 BPSK 创建一个快速解码器。我有一组表示为交错浮点数的复数,但由于BPSK 调制,只需要实部(或偶数索引浮点数)。每个浮点数x 映射到0,当x < 01,如果x >= 0。这是使用以下例程完成的:

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 的整数映射,这当然会导致我的程序由于无效索引而崩溃。

是否有任何解决方法可以避免此问题,或者至少使用AVXNaN 设置为0

【问题讨论】:

(float *)&amp;in[i]complex * 转换为float *。邀请未定义的行为。 不,complex_t 指向一个内存区域,其中交错的浮点数表示复数。 仍然是一个别名问题 imo。而 complex_t 的布局是实现定义的。 @Olaf OP 已经致力于编译器内在函数,所以我怀疑实现定义对他来说是个问题。 C 自 1999 年以来就具有复杂类型,并且保证它们具有与相应实类型的二元素数组相同的布局。所以应该没有填充或对齐问题。 【参考方案1】:

你可以用简单的方法开始,比较和屏蔽:(未测试)

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 特性)。

没有 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);

【讨论】:

嗯,我认为你是对的。我会检查AVX 版本,我会回来查看结果;) @Manos 哦等等,我刚刚注意到,你想要 integer 0 和 1?我会改代码 你可以多作弊,使用与 (int)1 具有相同位模式的浮点数,然后将其写入内存,无需 cvt【参考方案2】:

Harold 针对您真正提出的问题发布了一个很好的解决方案,但我想明确指出,在钳位时消除 NaN 值是完全简单的。如果任一参数是 NaN,则 MINPS 和 MAXPS 只返回第二个参数。因此,您需要做的就是交换参数顺序,NaN 也会被限制。例如,以下代码会将 NaN 限制为 _min_mask:

res = _mm256_max_ps(_mm256_min_ps(_max_mask, res), _min_mask);

【讨论】:

以上是关于使用 AVX 与 NaN 的比较的主要内容,如果未能解决你的问题,请参考以下文章

为啥 gcc -O3 处理 avx256 的内在比较与 gcc -O0 和 clang 不同?

如何检查 AVX 内在 __m256 的 inf

avx512中比较内在指令的不同语义?

Python\Numpy:将数组与 NAN 进行比较 [重复]

AVX2 64位无符号整数比较

JavaScript 随笔