检查向量是否包含大于零的任何元素

MRO*_*ROF 5 c vector avx

如果有人可以帮助编写一个接收AVX向量的函数并检查它是否包含任何大于零的元素,我将感激不尽.

我编写了以下代码,但它不是最佳的,因为它存储元素然后操纵它.矢量应该作为一个整体进行检查.

int check(__m256 vector)
{
  float * temp;
  posix_memalign ((void **) &temp, 32, 8 * sizeof(float));    
  _mm256_store_ps( temp, vector );

  int flag=0;
  for(int k=0; k<8; k++)
  {
    flag= ( (temp[k]>0) ? 1 : 0 );
    if (flag==1) return 1;
  }

  free( temp);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 4

如果您要对结果进行分支,则使用“传统”比较/移动掩码/整数测试通常会减少微指令,就像使用 SSE1 一样。

__m256 vcmp = _mm256_cmp_ps(_mm256_setzero_ps(), x, _CMP_LT_OQ);
int cmp = _mm256_movemask_ps(vcmp);
if (cmp)
    return 1;
Run Code Online (Sandbox Code Playgroud)

这通常会编译成类似的东西

vcmplt_oqps  ymm2, ymm0, ymm1
vpmovmskb    eax, ymm2

test         eax,eax
jnz      .true_branch
Run Code Online (Sandbox Code Playgroud)

这些都是单 uop 指令,以及支持 AVX 的 Intel 和 AMD CPU 上的 test/jnz 宏熔断,因此总共只有 3 个 uop(在 Intel 上)。

请参阅Agner Fog 的指令表 + 微架构指南以及https://stackoverflow.com/tags/x86/info链接的其他指南。


您也可以使用 PTEST,但在这种情况下效率较低。请参阅_mm_testc_ps 和 _mm_testc_pd 与 _mm_testc_si128

没有 AVX,ptest可以方便地检查寄存器是否全零,而不需要额外的指令来复制它(因为它直接设置整数标志)。但由于它是 2 uop,并且不能与jcc分支指令进行宏融合,所以它实际上比上面的更糟糕:

// don't use, sub-optimal
__m256 vcmp = _mm256_cmp_ps(_mm256_setzero_ps(), x, _CMP_LT_OQ);
if (!_mm256_testz_si256(vcmp, vcmp)) {
    return 1;
}
Run Code Online (Sandbox Code Playgroud)

内在testzPTEST. 它直接根据其参数的 AND 和 AND NOT 的结果设置 ZF 和 CF 标志。vcmp当有任何非零位时, testz 内在函数为 true 。(只有当把一些东西放在那里时才会这样vcmpps。)

VPTESTwith ymm regs 仅适用于 AVX。尽管 AVX2 看起来像向量整数指令,但它并不是必需的。

这将编译成类似的东西

vcmplt_oqps  ymm2, ymm0, ymm1
vptest       ymm2, ymm2
jnz      .true_branch
Run Code Online (Sandbox Code Playgroud)

代码大小可能比上面的要小,但这实际上是 4 uop,而不是 3。如果您使用setnzor cmovnz,则宏融合不会成为一个因素,因此ptest将实现收支平衡。正如我上面提到的,主要用例ptest是当您可以在没有比较指令且没有 AVX 的情况下使用它时。

检查向量是否全零 ( pcmpeqb xmm0,xmm1/ pmovmskb eax, xmm1/ test eax,eax) 的另一种方法必须在没有 AVX 的情况下销毁其中一个输入向量,因此movdqa如果在测试后仍然需要这两个向量,则需要额外的指令来复制。


ptest浮点位黑客

我认为对于这个特定的测试,可能可以跳过比较指令并vptest直接使用来查看是否有任何float元素的符号位未设置,但其他地方有一些非零位。

实际上不,这个想法行不通,因为它不尊重元素边界。它无法区分具有正元素的向量与具有元素+0.0(符号位清除)和另一个负元素(其他位设置)的向量之间的区别。

vptest设置CF=bool(~src1 & src2)和 ZF=(src1 & src2)。我认为 src1=set1(0x7FFFFFFF)可以告诉我们一些关于符号位和非符号位的有用信息,我们可以使用检查 CF 和 ZF 的条件来测试这些信息。例如ja:CF=0 且 ZF=0。不过,实际上并不存在仅在 CF=1ZF=0 时才成立的 x86 条件,因此这是另一个问题。

也是NaN > 0false,但 NaN 有一些设置位。(指数全 1,尾数非零,符号位 = 不关心,因此可以有 +NaN 和 -NaN)。如果这是唯一的问题,那么在不需要 NaN 处理的情况下这仍然很有用。