C++ 如何对具有多种条件的循环进行矢量化?

C++ 如何对具有多种条件的循环进行矢量化?,c++,vectorization,sse,simd,C++,Vectorization,Sse,Simd,我有下面的循环。目标是在数组tmp的所有元素之间执行操作,并将其存储在标量b中。该操作相当于一个加法,因此没有特定的执行顺序。例如,如果我们有a+b+c+d,我们可以按任意顺序计算,这意味着(a+b)+(c+d)也是可能的。这同样适用于该操作。然而,也存在一些特殊的条件,这些条件以不同的方式导致了结果 tmp.e和b.e是long,而tmp.x和b.x是双倍 是否有任何形式可以比较所有tmp.e,例如,SSE的2对,并相应地执行b.x的正确计算。在所有情况下,它都可以被视为一个addMul,在第

我有下面的循环。目标是在数组
tmp
的所有元素之间执行操作,并将其存储在标量
b
中。该操作相当于一个加法,因此没有特定的执行顺序。例如,如果我们有a+b+c+d,我们可以按任意顺序计算,这意味着(a+b)+(c+d)也是可能的。这同样适用于该操作。然而,也存在一些特殊的条件,这些条件以不同的方式导致了结果

tmp.e
b.e
long
,而
tmp.x
b.x
双倍

是否有任何形式可以比较所有
tmp.e
,例如,SSE的2对,并相应地执行
b.x
的正确计算。在所有情况下,它都可以被视为一个addMul,在第一种情况下,它只是乘以1,在其他情况下乘以0或绑定。有可能将其矢量化吗?如果是,怎么做

谢谢

void op(vec& tmp, scalar& b)
{
    for (i = 1; i < n; ++i)
    {
        if (b.e == tmp.e[i])
        {
            b.x += tmp.x[i];
            b.normalize();
            continue;
        }
        else if (b.e > tmp.e[i])
        {
            if (b.e > tmp.e[i]+1)
            {
                continue;
            }
            b.x += tmp.x[i] * BOUND;
            b.normalize();
        }
        else
        {
            if (tmp.e[i] > b.e+1)
            {
                b.x = tmp.x[i]; 
                b.e = tmp.e[i];
                b.normalize();
                continue;
            }
            b.x = b.x * BOUND + tmp.x[i];
            b.e = tmp.e[i];
            b.normalize();
        }
    }
}
void op(vec&tmp,scalar&b)
{
对于(i=1;itmp.e[i])
{
如果(b.e>tmp.e[i]+1)
{
持续
}
b、 x+=tmp.x[i]*界;
b、 规范化();
}
其他的
{
如果(tmp.e[i]>b.e+1)
{
b、 x=tmp.x[i];
b、 e=tmp.e[i];
b、 规范化();
持续
}
b、 x=b.x*界+tmp.x[i];
b、 e=tmp.e[i];
b、 规范化();
}
}
}

SIMD代码中的每元素条件通常通过使用压缩比较指令生成所有零元素和所有一元素的掩码来处理。你可以用这个来表示和或向量。因此,例如,您可以通过使用和来生成一个向量,该向量在应递增的元素中为1,在不应递增的元素中为0,因为0是加法的标识值,因此只能递增通过测试的元素。(x+0=x)

您还可以根据蒙版计算两个结果,然后将它们混合在一起。(使用AND和OR,或使用矢量混合指令。)

这种处理SIMD条件的方法类似于cmov:您必须计算分支的两侧,即使您在向量中处理的所有元素都位于分支的同一侧


看起来您的数据已经是数组结构格式。因此,您可以通过对
e
值的向量的操作生成掩码,用于
x
值的向量。如果
long
是32位,您可以对4个元素进行比较,然后将低值和高值解包,得到两个64位元素的掩码,以匹配您的双精度。如果数组很小(因此它们甚至可以放入缓存
.e[]
占用的空间与
.x[]
一样多),那么将长字符与双字符相同意味着更少的解包


不管怎样,这看起来不太有希望。太多的条件,我不知道整个事情真正想要完成的是什么,以及对输入数据可能有什么限制。如果我对这个问题有更多的了解,也许我可以想出一种矢量化的方法来解决一些问题



哦,我认为另一个致命的缺陷是每次迭代都依赖于上一次迭代,因为它可能会修改
b
。因此,除非您能够根据最后一个向量元素制定出更新
b
的规则,否则您不能并行地进行多个迭代。

b.normalize()具体做什么,您有SIMD实现吗?在您的循环中,您要做两件事:搜索
tmp.e
的一个特殊范围,并在这个范围内求和
tmp.x[i]
。尝试在求和之前先搜索范围。使用数组结构,您可以仅在
tmp.e[i]
上进行紧凑搜索,然后使用下面Peter描述的掩蔽对
tmp.x[i]
进行矢量化。在执行迭代i-1之前,您似乎不知道迭代i中采用的if级联的哪个分支。所以,不。现在还不清楚:1)你说一些操作是可交换和关联的,但不清楚它与你发布的代码有什么关系;2) 什么是规范化?如果你写下你想要得到的结果,而不是让其他人猜测你的代码实际上做了什么,那就太好了。这段代码似乎确实受到连续元素之间的数据依赖性的影响。此外,在循环体中还有五种不同的情况需要区分。除非迭代过程中的案例模式具有特殊的特征(例如相同案例的长时间重复),否则这种矢量化是没有希望的。事实上,我使用的是数组结构而不是数组结构,通过执行我在文章中解释的操作可以避免依赖性。但是,由于所有这些条件,似乎不可能将其矢量化。@fc67:Oops,我想我在编写代码时没有回滚到重新检查代码。我想我是从书面文本(不检查代码)中假设数组与标量具有相同的结构格式,因为代码省略了任何类型声明。或者我只是变得悲观了,认为这部分问题是最糟糕的,就像其他问题一样P@PeterCordes你们中有人有类似的智慧吗?谢谢。@Royi:我没有看代码,但我注意到在自述文件中,您发现带有
/fp:fast
的MSVC比gcc快。但是您遗漏了gcc中的
-ffast math
,所以您应该尝试添加它。还可以使用
-march=native
让它使用AVX和生成机器支持的任何其他东西。