我可以正确比较 avx 中的零寄存器吗?
Posted
技术标签:
【中文标题】我可以正确比较 avx 中的零寄存器吗?【英文标题】:Could I compare to zero register in avx correctly? 【发布时间】:2015-08-18 12:15:38 【问题描述】:我遇到了 AVX 内部指令 _mm256_testc_pd() 的一个非常奇怪的行为。 这里可以看到这个函数的描述https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=AVX,AVX2&text=test&expand=5432
我在我的代码中使用了 2 次,预计会看到与零寄存器进行比较的类似结果。
#include <immintrin.h>
#include <stdio.h>
int main(void)
unsigned char arr[32] __attribute__ ((aligned (32)));
__m256d a, zero;
int res1, res2;
memset(arr, 0 , 32);
arr[0] = 0xff;
arr[4] = 0xff;
arr[8] = 0xff;
arr[12] = 0xff;
arr[16] = 0xff;
arr[20] = 0xfd;
arr[24] = 0xff;
arr[28] = 0xff;
zero = _mm256_setzero_pd();
a = _mm256_load_pd((double *)arr);
res1 = _mm256_testc_pd(zero, a);
printf("res1 = %d\n" , res1);
memset(arr, 0xff, 32);
a = _mm256_load_pd((double *)arr);
res2 = _mm256_testc_pd(zero, a);
printf("res2 = %d\n" , res2);
return 0;
结果我得到了
res1 = 1
res2 = 0
有人知道为什么会这样吗?我认为在这两种情况下a
都不等于零。
更新 在 cmets 讨论后,我的问题得到了解决,但我对函数 _mm256_testc_si256 和 _mm256_testz_si256 有一点误解
例如:
unsigned char arr[32] __attribute__ ((aligned (32)));
__m256d a, zero;
int res1, res2;
memset(arr, 0 , 32);
arr[0] = 0x80;
zero = _mm256_setzero_pd();
a = _mm256_load_pd((double *)arr);
res1 = _mm256_testc_si256(_mm256_castpd_si256(zero),_mm256_castpd_si256(a));
res2 = _mm256_testz_si256(_mm256_castpd_si256(zero),_mm256_castpd_si256(a));
printf("res1 = %d\n" , res1);
printf("res2 = %d\n" , res2);
输出是
res1 = 0
res2 = 1
而且我认为只有第一个是正确的。那么为什么这个函数会产生不同的输出呢?
【问题讨论】:
arr[20] = 0xfd;
是故意的吗?
没关系,可能是0xff
,同样的行为。
这条指令真的能如你所愿吗?我宁愿说你想使用 _mm256_cmp_pd() 来比较值。
我认为你是对的,但你知道 _mm256_cmp_pd () 的第三个参数应该是什么吗?
@AlekseyM:你可以通过_CMP_EQ_OQ
(== 0
)。
【参考方案1】:
_mm256_testc_pd
仅对每个双精度元素的 符号位 进行操作,因此观察到的行为是正确的。如果您想在每个元素中测试双精度 values,则首先使用合适的比较指令(例如 _mm256_cmp_pd
和适当的 _CMP_xxx
参数),然后使用 _mm256_testc_pd
或 _mm256_testz_pd
,具体取决于根据您的具体要求。
【讨论】:
@AlekseyM:您可以使用整数PTEST
测试 FP 零,方法是测试除符号位之外的所有掩码。例如ones=_mm256_cmpeq_epi64(same, same); testmask=_mm256_srli_epi64(ones, 1); return _mm256_testz_si256(a, ones);
测试除符号位位置以外的任何地方的非零位。不过,我不建议将这种特殊情况优化用于实际使用!
@AlekseyM:如果要测试整个寄存器中的任何设置位,请使用 _mm256_testz_si256(a, a)
,就像在整数寄存器上使用 test eax, eax / jz
一样。确保您确实希望在函数中将 -0
与 0
区别对待。如果您确定,那么这是检查 FP 值的二进制表示中是否存在任何非零位的最直接方法。
@AlekseyM:您正在针对zero
进行测试,而不是针对自身。 PTEST
(testz_si256
) 在其 args 之间进行 AND,如果所有位都为零,则设置 z
标志。它还执行src1 AND (NOT src2)
并设置c
标志,如果其中的所有位都为零。无论a
如何,您的testc
总是为零。编辑:我认为您在我完成输入之前删除了您的评论。 :P
@AlekseyM:您最多可以编辑 cmets 5 分钟,而不是删除和搞乱排序。
@AlekseyM:您不需要针对 0 进行显式测试,只需使用 Peter 之前给出的示例,即 _mm256_testz_si256(a, a)
- 它更高效(不需要零向量)且更直观/可读。【参考方案2】:
感谢 Peter Cordes 和所有其他人,我的问题的正确(也是最漂亮)的解决方案是
res = _mm256_testz_si256(_mm256_castpd_si256(a), _mm256_castpd_si256(a))
【讨论】:
以上是关于我可以正确比较 avx 中的零寄存器吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 AVX512 寄存器 zmm26 中的 QuadWord 写入 rax 寄存器?
AVX2:AVX 寄存器中 8 位元素的 CountTrailingZeros