矢量化嵌套循环 - SIMD

时间:2012-05-09 00:44:01

标签: c++ parallel-processing x86 vectorization simd

有没有人知道使用SIMD对这样的东西进行矢量化:

for(size_t i = 0; i < refSeq.length() / 4; i++){

    for(size_t j = 0; j<otherSeq.length(); j++){
    if(refSeq[i] == otherSeq[j]){
        if(i == 0 || j == 0)
            L[i][j] = 1;
       else
        L[i][j] = L[i-1][j-1] + 1;
    }
       else
        L[i][j] = 0;
    }
}

2 个答案:

答案 0 :(得分:1)

这是一个动态编程问题,而且一个前沿实现有太多的数据依赖性,不适合SIMD计算。

但是,如果将算法从逐行迭代更改为对角线迭代,则可以并行计算整个对角线。见下图。

enter image description here

&#34;伪&#34;下面的代码使用一个带有1个额外行/列的矩阵,以简化&#34;内部&#34;计算。在每次对角线迭代之前初始化这个额外的行/列。

int i, j, k;
for (k = 1; ; k++) {
    int minI = k > refLen ? k - refLen : 1;
    int maxI = k > otherLen ? otherLen : k - 1;

    for (i = maxI; i >= minI; ) {
        j = k - i;

        // vectorized calculation 256 bit (AVX2)
        if (i >= 32 && otherLen - j >= 32) {
            // calculate 32 values of the diagonal with SIMD
            i -= 32;
            continue;
        }

        // vectorized calculation 128 bit (SSE)
        if (i >= 16 && otherLen - j >= 16) {
            // calculate 16 values of the diagonal with SIMD
            i -= 16;
            continue;
        }

        // scalar calculation
        if (refSeq[i - 1] == otherSeq[j - 1]) {
            L[i][j] = L[i - 1][j - 1] + 1;
        } else {
            L[i][j] = 0;
        }
        i--;
    }

    if (k == otherLen + refLen) {
        break;
    }

    // initialize next j-endpoint in diagonal
    if (k <= refLen) {
        L[0][k] = 0;
    }
    // initialize next i-endpoint in diagonal
    if (k <= otherLen) {
        L[k][0] = 0;
    }
}

不确定您是否需要用于计算的实际SIMD指令,或者只是知道如何并行化/向量化计算。

答案 1 :(得分:0)

让我试着提出一个解决方案。最初计算L [i] [0]和L [0] [j]的值。现在开始从i = 1和j = 1迭代。现在可以删除在循环的每次迭代中检查i == 0或j == 0。另外一个优点是,对于行中每次迭代中的每个L [i] [j],L [i-1] [j-1]的值是可用的。现在,假设向量寄存器可以容纳数组的4个元素。现在我们可以加载refSeq,otherSeq,L(前一行)和L(当前行)的4个元素。从理论上讲,我们现在得到了矢量化。我假设自动矢量化器不会识别这个。所以我们必须手动完成它。如果我错了,请纠正我。

for(size_t i=0;i<refSeq.length()/4;i++)
{
    if(refSeq[i]==otherSeq[0])
        L[i][0]=1;
    else
        L[i][0]=0;
}
for(size_t j=0; j<otherSeq.length();j++)
{
    if(refSeq[0]==otherSeq[j])
        L[0][j]=1;
    else
        L[0][j]=0;
}

for(size_t i=1;i<refSeq.length()/4;i++)
{
    for(size_t j=1; j<otherSeq.length();j++)
    {
        if(refSeq[i]==otherSeq[j])
            L[i][j] = L[i-1][j-1] + 1;
        else
            L[i][j]=0;
    }
}

一个缺点是,现在我们正在加载前一行,无论refSeq [i]是否等于otherSeq [j],或者与原始代码一样,只有当序列相等时才访问对角元素。 / p>