AVX2整数比较,用于小于等于的情况。

3
什么是比较两个4x64位整数AVX向量的最有效方法,使它们满足<=条件。
根据英特尔指令集手册,我们有以下内容:
_mm256_cmpgt_epi64(__m256i a, __m256i b) = a > b
_mm256_cmpeq_epi64(__m256i a, __m256i b) = a == b
用于比较操作。
以及以下内容:
_mm256_and_si256(__m256i a, __m256i b) = a & b
_mm256_andnot_si256(__m256i a, __m256i b) = ~a & b
_mm256_or_si256(__m256i a, __m256i b) = a | b
_mm256_xor_si256(__m256i a, __m256i b) = a ^ b
用于逻辑操作。
我的方法是:
// check = ( a <= b ) = ~(a > b) & 0xF..F
__m256i a = ...
__m256i b = ...
__m256i tmp = _mm256_cmpgt_epi64(a, b)
__m256i check = _mm256_andnot_si256(tmp, _mm256_set1_epi64x(-1))

1
我不确定是否有更聪明(更有效)的方法来做这件事,因为不必要的 0xF..F 让我感到困扰。 - user2399267......seems good
2
你查过优秀编译器能为此做什么了吗?GCC通过将一个临时寄存器与自身(总是为真)进行比较生成“非and”位掩码,因此您不需要将其保留为常量,并且根据Agner Fog的分析,该指令被认为是独立于寄存器的先前值,因此可以快速生成掩码而不必浪费寄存器。 - EOF
1个回答

4

您说得对,没有直接获取所需掩码的方法,只有一个反向掩码:A gt B = A nle B

由于不存在矢量NOT指令,因此您需要一个全1的矢量以及一个额外的指令来反转矢量。(或者一个全0的矢量和_mm256_cmpeq_epi8,但是它不能在像_mm256_xor_si256与全1矢量一样多的执行端口上运行。) 请参见标签wiki以获取性能信息,特别是Agner Fog的指南。

另一个按位布尔选项_mm256_andn_si256与xor一样好。 它不是可交换的,并且稍微复杂一些,需要进行精神验证以确保正确。 用全1异或是翻转所有位的好习惯。


与其花费一条指令来反转掩码,在大多数代码中,可以直接将其用于相反的方式。

例如,如果它是blendv的输入,则将操作数的顺序颠倒。 代替
_mm256_blendv_epi8(a, b, A_le_B_mask),使用
_mm256_blendv_epi8(b, a, A_nle_B_mask)

如果您要将某些内容与掩码_mm_and,请改用_mm_andn

如果您要_mm_movemask并测试是否为全零,则可以测试是否为全1。 它将编译为cmp eax, -1指令,而不是test eax,eax,这样效率就一样高。 如果您要对第一个1进行位扫描,则必须反转它。 整数not指令(从使用movemask结果上的~)比在向量上执行它要便宜。


如果你要执行OR或XOR操作,那么你只有一个问题,因为这些指令没有否定其输入之一的变体。 (我不知道英特尔是否只是不想添加一个"PORN"助记符,但很可能"PAND"和"PANDN"更常用,尤其是在可变混合指令之前。)

1
@PaulR:显然,我的困意大脑知道正确的答案涉及操作数的某种反转,但在注意到我完全错了之前就失去了动力。 >.< - Peter Cordes
你对blendv内嵌函数是正确的,但实际上我需要它之后再加一个OR操作,而没有NOR函数可以使用 - 不过还是谢谢你 - user2399267......seems good
@user2399267......seemsgood: NOR 将会是 ~(a|b)。你需要的操作,(~a | b) 将被称为 ORN,就像 ANDN 一样。如果我没记错的话,AVX512 向量整数比较将采用谓词参数(编码在一个立即字节中),因此 AVX512 最终将为我们提供广泛的比较运算符选择,就像 cmpps 目前所做的那样。 - Peter Cordes
1
@PeterCordes:嗯,我想这是我第一次在你的帖子中发现错误,所以你的得分率比我的高得多。;-)既然你已经更新了答案,我会删除我的评论以减少噪音。 - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接