如何检查 AVX 内在的 inf __m256

How to check inf for AVX intrinsic __m256

检查 AVX 内在 __m256(8 float 的向量)是否包含任何 inf 的最佳方法是什么?我试过了

__m256 X=_mm256_set1_ps(1.0f/0.0f);
_mm256_cmp_ps(X,X,_CMP_EQ_OQ);

但这与 true 相比。请注意,此方法将找到 nan(与 false 相比)。所以一种方法是检查 X!=nan && 0*X==nan:

__m256 Y=_mm256_mul_ps(X,_mm256_setzero_ps());   // 0*X=nan if X=inf
_mm256_andnot_ps(_mm256_cmp_ps(Y,Y,_CMP_EQ_OQ),
                 _mm256_cmp_ps(X,X,_CMP_EQ_OQ));

但是,这显得有些冗长。有没有更快的方法?

如果你想检查一个向量是否有任何个无穷大:

#include <limits>

bool has_infinity(__m256 x){
    const __m256 SIGN_MASK = _mm256_set1_ps(-0.0);
    const __m256 INF = _mm256_set1_ps(std::numeric_limits<float>::infinity());

    x = _mm256_andnot_ps(SIGN_MASK, x);
    x = _mm256_cmp_ps(x, INF, _CMP_EQ_OQ);
    return _mm256_movemask_ps(x) != 0;
}

如果您想要无穷大值的矢量掩码:

#include <limits>

__m256 is_infinity(__m256 x){
    const __m256 SIGN_MASK = _mm256_set1_ps(-0.0);
    const __m256 INF = _mm256_set1_ps(std::numeric_limits<float>::infinity());

    x = _mm256_andnot_ps(SIGN_MASK, x);
    x = _mm256_cmp_ps(x, INF, _CMP_EQ_OQ);
    return x;
}

我有一个想法,但它最终只有在你想测试所有元素是否为无限时才有帮助。哎呀

使用 AVX2,您可以使用 PTEST 测试所有元素是否为无穷大。我从 EOF 对 this question 的评论中得到了使用 xor 比较相等性的想法,我将其用于我的回答。我以为我将能够制作一个较短版本的 test-for-any-inf,但当然 pxor 只能作为所有 256b 都相等的测试。

#include <limits>

bool all_infinity(__m256 x){
    const __m256i SIGN_MASK = _mm256_set1_epi32(0x7FFFFFFF);  // -0.0f inverted
    const __m256 INF = _mm256_set1_ps(std::numeric_limits<float>::infinity());

    x = _mm256_xor_si256(x, INF);  // other than sign bit, x will be all-zero only if all the bits match.
    return _mm256_testz_si256(x, SIGN_MASK); // flags are ready to branch on directly
}

对于 AVX512,有一个 __mmask8 _mm512_fpclass_pd_mask (__m512d a, int imm8)。 (vfpclasspd)。 (参见 Intel's guide)。它的输出是一个掩码寄存器,我还没有研究 testing/branching 那里的值。但是您可以测试 any/all of +/- zero, +/- inf, Q/S NaN, Denormal, Negative.

我认为更好的解决方案是使用 vptest 而不是 vmovmskps

bool has_infinity(const __m256 &x) {
    __m256 s   = _mm256_andnot_ps(_mm256_set1_ps(-0.0), x);
    __m256 cmp = _mm256_cmp_ps(s,_mm256_set1_ps(1.0f/0.0f),0);
    __m256i cmpi = _mm256_castps_si256(cmp);
    return !_mm256_testz_si256(cmpi,cmpi);
}

内在的_mm256_castps_si256只是为了让编译器开心"This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency."

vptest 优于 vmovmskps 因为它设置了零标志而 vmovmskps 没有。使用 vmovmskps 编译器必须生成 test 来设置零标志。