使用内在函数在数组中查找下一个非零值

时间:2014-04-24 16:41:56

标签: c++ performance vectorization sse avx

我有一个int数组[10000],我想从某个位置迭代以找到下一个非零索引。目前我使用基本的while循环:

while(array[i] == 0){
    pos++;
}

我知道使用内在函数我可以一次测试4个整数为零,但有没有办法返回指示“第一个”非零的向量索引的东西?

1 个答案:

答案 0 :(得分:3)

执行此操作相当简单,但吞吐量提升可能不会很大,因为您可能会受到内存带宽的限制(除非您的阵列已经缓存):

int index = -1;
for (i = 0; i < n; i += 4)
{
    __m128i v = _mm_load_si128(&A[i]);
    __m128i vcmp = _mm_cmpeq_epi32(v, _mm_setzero_si128());
    int mask = _mm_movemask_epi8(vcmp);
    if (mask != 0xffff)
    {
        break;
    }
}
if (i < n)
{
    for (j = i; j < i + 4; ++j)
    {
        if (A[j] != 0)
        {
             index = j;
             break;
        }
    }
}

这假设数组A是16字节对齐的,其大小n是4的倍数,并且整数是32位。

循环展开2倍可能会有所帮助,特别是如果您的输入数据很大和/或稀疏,例如。

int index = -1;
for (i = 0; i < n; i += 8)
{
    __m128i v0 = _mm_load_si128(&A[i]);
    __m128i v1 = _mm_load_si128(&A[i + 4]);
    __m128i vcmp0 = _mm_cmpeq_epi32(v0, _mm_setzero_si128());
    __m128i vcmp1 = _mm_cmpeq_epi32(v1, _mm_setzero_si128());
    int mask0 = _mm_movemask_epi8(vcmp0);
    int mask1 = _mm_movemask_epi8(vcmp1);
    if ((mask0 | mask1) != 0xffff)
    {
        break;
    }
}
if (i < n)
{
    for (j = i; j < i + 8; ++j)
    {
        if (A[j] != 0)
        {
             index = j;
             break;
        }
    }
}

如果您有AVX2(Haswell及更高版本),那么您可以一次处理8个整数而不是4个整数。