如何检查 AVX 内在 __m256 的 inf

Posted

技术标签:

【中文标题】如何检查 AVX 内在 __m256 的 inf【英文标题】:How to check inf for AVX intrinsic __m256 【发布时间】:2015-06-05 19:25:16 【问题描述】:

检查 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));

但是,这似乎有些冗长。有更快的方法吗?

【问题讨论】:

屏蔽符号位并直接与正无穷大进行比较。这是 2 条指令。 @Mysticial 3:我还得加载 +inf。 根据编译器的处理方式,可以将常量内联为内存访问。所以像这样:vandnps ymm1, ymm0, YMMPTR[MASK]; vcmpps ymm1, ymm1, YMMPTR[INIFINITY], 0; 如果它处于循环中,编译器可能会将两个常量都拉入寄存器。 @Mystical 你介意将你的评论“升级”为答案吗? 【参考方案1】:

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

#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;

【讨论】:

您的has_infinity 函数似乎是ptest 之前的旧做事方式。 vmovmskps 也需要testvptest 设置RFLAGS 寄存器并且不需要test 根据我的回答中的彼得评论,无论如何,ptestmovmsk 来说并不是一个很大的优势。 这个thread 来自这个answer 可能你会感兴趣。【参考方案2】:

我认为更好的解决方案是使用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 来设置零标志。

【讨论】:

movmsk vs. ptest 并不像你想象的那么明显。 test 可以与 jcc 融合,ptest 是 2 微秒。不过,我认为ptest 的延迟更低。 @PeterCordes,好点子!我想这就是为什么 Paul R 发现我使用 ptest 的解决方案只比 movmsk 快一点,而不是快很多。【参考方案3】:

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

使用 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)。它的输出是一个掩码寄存器,我还没有研究过那里的值的测试/分支。但您可以测试任何/所有 +/- 零、+/- inf、Q/S NaN、非正规、负数。

【讨论】:

我在这里做了类似的事情sse-testing-equality-between-two-m128i-variables。 我认为您可以使用vptest 来生成has_infinity 自己?我不这么认为,因为您所能做的就是测试是否存在任何表示inf 的位模式。您需要测试所有位是否已设置。我认为你需要像 pcmpeqcmpps 这样的东西来将 match-exact-bit-pattern 转换为 all-set 或 none-set,如果你希望你的条件在一个元素匹配你的模式时为真,但其他元素可以是任何东西。

以上是关于如何检查 AVX 内在 __m256 的 inf的主要内容,如果未能解决你的问题,请参考以下文章

AVX 内在 _mm256_cmp_ps 是不是应该在为真时返回 NaN?

加载指令与 AVX 中的 AVX2 __m256i const* mem_addr [关闭]

AVX2 上的 256 位 CRC 计算

我可以使用内在函数加速类型转换吗?

AVX2 1x mm256i 32bit 到 2x mm256i 64bit

在 C++ 中检查具有内在函数的 nan