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

Using intrinsics to find next non-zero in an array

本文关键字:查找 下一个 数组 内部函数      更新时间:2023-10-16

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

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

etc

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

这很简单,但吞吐量的提高可能不会很大,因为您可能会受到内存带宽的限制(除非您的阵列已经缓存):

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的倍数,int是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个int,而不是4个。