如何检查AVX内部__m256的信息
How to check inf for AVX intrinsic __m256
检查AVX内禀__m256
(8 float
的向量)是否包含任何inf
的最佳方法是什么?I tried
__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;
}
我认为一个更好的解决方案是使用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
只是为了让编译器高兴,"这个内在的只用于编译,不生成任何指令,因此它具有零延迟。"
vptest
优于vmovmskps
,因为它设置零标志,而vmovmskps
不设置。对于vmovmskps
,编译器必须生成test
来设置零标志。
这个简短的测试向量中的浮点数是否损坏(NAN或INFINITY):
int is_corrupted( const __m256 & float_v8 ) {
__m256 self_sub_v8 = _mm256_sub_ps( float_v8, float_v8 );
return _mm256_movemask_epi8( _mm256_castps_si256( self_sub_v8 ) );
}
这是2个AVX2指令,没有额外的常量,并使用一个技巧——任何正常的"自我"减法应该以零结束,所以movemask_epi8
以后应该提取一些比特,表明它是零还是NAN/INFINITY。我还没有在不同的平台上测试过。
编辑:参见Peter关于舍入为负数的重要评论
如果您不介意也检测NaN ,即检查非有限的数字,查看@gox的答案建议从自身中减去(在有限输入的默认四舍五入模式下产生+0.0,否则NaN),然后使用_mm256_movemask_epi8
从每个字节中取一位,包括来自指数的一位,该指数对于NaN来说是非零的,或者对于0.0来说是零。测试movemask & 0x77777777
将允许您忽略符号位,因此即使使用FP四舍五入模式= roundTowardNegative, x-x
给出-0.0
如果你需要特别检测无穷大,也不要NaN
AVX-512F+VL具有_mm256_fpclass_ps_mask
+ _kortestz_mask16_u8
。但是没有AVX-512, 在位模式上使用AVX2整数可能是最有效的。
表示无穷大的IEEE binary32位模式是一个全1的指数域和一个全0的尾数。符号位表示它是正无穷还是负无穷。(NaN是相同的指数,但尾数非零)所以我们想要检测2个位模式,它们只在高位上不同。
我们可以使用AVX2整数移位+ cmpeq操作来实现这一点,只有一个矢量常数,即使考虑到输入来自FP数学指令的旁路延迟,延迟也比vcmpps
低。并且潜在的吞吐量优势,因为vpslld
和/或vpcmpeqd
可以在不同的端口上运行,而不是在某些cpu上运行FP数学/比较指令。(使用按位AND、ANDN或or来强制符号位为已知状态,清除或设置,可以进一步帮助绕过某些cpu上的延迟,并且可以更好地提高吞吐量,能够在更多cpu上的更广泛的后端执行单元上执行。)
(https://uops.info//https://agner.org/optimize/)
你可以用整数操作来做到这一点,比如左移1来移除符号位,然后_mm256_cmpeq_epi32
对set1_epi32(0xff000000)
(无限的位模式,左移1)。所有位都在指数中设置,所有位都在尾数中清除,否则就是NaN)。然后你只需要一个常量,整数比较的较低延迟应该弥补可能的旁路延迟。
int has_infinity_avx2(__m256 v)
{
__m256i bits = _mm256_castps_si256(v);
bits = _mm256_slli_epi32(bits, 1); // shift out sign bits. Requires AVX2
bits = _mm256_cmpeq_epi32(bits, _mm256_set1_epi32(0xff000000)); // infinity << 1
return _mm256_movemask_epi8(bits);
// or cast for _mm256_movemask_ps if you want to std::countr_zero to find out where in terms of elements instead of byte offsets
}
我有一个更早的想法,但它最终只帮助,如果你想测试所有元素是无限的。哎呀。
使用AVX2,可以使用PTEST
测试所有元素是否为无穷大。我从EOF对这个问题的评论中得到了使用xor来比较相等性的想法,我在那里的答案中使用了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
)。(参见英特尔的指南)。它的输出是一个掩码寄存器,你可以直接打开它的分支。您可以测试任何/所有+/-零,+/- inf, Q/S NaN,异常,负
- 将函数类成员映射到类本身内部
- Boost Spirit,获取迭代器内部语义动作
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 内联函数中具有内部链接的全局变量
- 在函数内部的声明中初始化数组,并在外部使用它
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 卷曲bracers内部结构的声明
- 从函数角度看ID到文件路径的内部与外部映射
- spdlog标头仅与外部fmt一起使用.spdlog错误:'内部':不是'fmt'
- 如何在pugixml中获取节点的内部XML
- 使用C链接在函数内部创建C++模板
- 用于AVX的ln(x)的实现,m256
- 指针没有更新它在void函数内部指向的值
- 方法内部但循环仍得到预期的不合格id错误C++
- C++:具有内部链接的正向声明常量
- SDL_PollEvent() 无法捕获类函数内部SDL_QUIT?
- libcurl 和 DNS ttl 中的内部连接管理
- 如何修改 lambda 内部字符串的向量
- 如果我将嵌套映射作为多重映射的值,如何将值插入内部映射?
- 来自 Android 应用程序内部的 boost 类型的 boost::wrapexcept<boost::system::system_error> 的未捕获异常