C++ 使用内部函数查找数组中的下一个非零

C++ 使用内部函数查找数组中的下一个非零,c++,performance,vectorization,sse,avx,C++,Performance,Vectorization,Sse,Avx,我有一个int数组[10000],我想从某个位置迭代以找到下一个非零索引。目前我使用一个基本的while循环: while(array[i] == 0){ pos++; } 等 我知道使用Intrinsic我可以一次测试4个整数的零,但是有没有方法返回指示“第一个”非零的向量索引的内容?这样做相当简单,但吞吐量的提高可能不是很大,因为您可能会受到内存带宽的限制(除非您的数组已经缓存): 如果您有AVX2(Haswell和更高版本),那么您可以一次处理8个整数,而不是4个。这样做相当简单

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

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


我知道使用Intrinsic我可以一次测试4个整数的零,但是有没有方法返回指示“第一个”非零的向量索引的内容?

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


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

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


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

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


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

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




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

您是否尝试过简单的解决方案:?除非你想搜索数百万条记录,否则把事情复杂化是没有用的。@JoachimPileborg我在寻找低延迟,所以它是值得的。如果我不需要它,我就不会费心问了。感谢您的建议-但我问的是本质,因为我需要速度。我认为您正在寻找下一个非零项,而不是索引…?好的,您需要索引,无论如何,它是一个必须非零的项。:)如果您的阵列不是非常稀疏,那么您不应该担心顺序测试。但是如果是的话,也许你应该考虑应用一些特殊的数据结构来有效地处理稀疏表。对于宽指令/比较的优化(如HasWaveAX2)只有当你确信数据在高速缓存中,虚拟页在TLB中,并且数组足够稀疏时才有意义。如果最终导致TLB未命中或缓存未命中(需要解决数百个周期),那么使用哪条指令加载和比较并不重要。你有没有试过简单的解决办法?除非你想搜索数百万条记录,否则把事情复杂化是没有用的。@JoachimPileborg我在寻找低延迟,所以它是值得的。如果我不需要它,我就不会费心问了。感谢您的建议-但我问的是本质,因为我需要速度。我认为您正在寻找下一个非零项,而不是索引…?好的,您需要索引,无论如何,它是一个必须非零的项。:)如果您的阵列不是非常稀疏,那么您不应该担心顺序测试。但是如果是的话,也许你应该考虑应用一些特殊的数据结构来有效地处理稀疏表。对于宽指令/比较的优化(如HasWaveAX2)只有当你确信数据在高速缓存中,虚拟页在TLB中,并且数组足够稀疏时才有意义。如果最终导致TLB未命中或缓存未命中(需要解决数百个周期),那么使用哪条指令加载和比较并不重要。你有没有试过简单的解决办法?除非你想搜索数百万条记录,否则把事情复杂化是没有用的。@JoachimPileborg我在寻找低延迟,所以它是值得的。如果我不需要它,我就不会费心问了。感谢您的建议-但我问的是本质,因为我需要速度。我认为您正在寻找下一个非零项,而不是索引…?好的,您需要索引,无论如何,它是一个必须非零的项。:)如果您的阵列不是非常稀疏,那么您不应该担心顺序测试。但是如果是的话,也许你应该考虑应用一些特殊的数据结构来有效地处理稀疏表。对于宽指令/比较的优化(如HasWaveAX2)只有当你确信数据在高速缓存中,虚拟页在TLB中,并且数组足够稀疏时才有意义。如果最终导致TLB未命中或缓存未命中(需要解决数百个周期),那么使用哪条指令加载和比较并不重要。你有没有试过简单的解决办法?除非你想搜索数百万条记录,否则把事情复杂化是没有用的。@JoachimPileborg我在寻找低延迟,所以它是值得的。如果我不需要它,我就不会费心问了。感谢您的建议-但我问的是本质,因为我需要速度。我认为您正在寻找下一个非零项,而不是索引…?好的,您需要索引,无论如何,它是一个必须非零的项。:)如果您的阵列不是非常稀疏,那么您不应该担心顺序测试。但是如果是的话,也许你应该考虑应用一些特殊的数据结构来有效地处理稀疏表。对于宽指令/比较的优化(如HasWaveAX2)只有当你确信数据在高速缓存中,虚拟页在TLB中,并且数组足够稀疏时才有意义。如果最终导致TLB未命中或缓存未命中(需要解决数百个周期),那么使用哪条指令加载和比较并不重要。See可以使用while循环和掩码作为继续条件吗?我认为这不会有多大区别,但请继续尝试。@PaulR,回答得好,但我不认为展开会有什么帮助。没有依赖链(如累加器)。它取决于CPU和编译器(以及编译器开关),但根据我的经验,如果智能地进行2x展开,小循环通常会从中受益。请注意,这也允许您分摊掩码测试和分支的成本。如果不清楚的话,我会发布一个展开的例子,你可以试试
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;
        }
    }
}
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;
        }
    }
}