C ++优化矢量化嵌套循环

时间:2014-05-29 08:47:47

标签: c++ vectorization nested-loops

我有一个处理多个嵌套循环的程序,在3D域上运行:

unsigned int sX(m_sizeZ*m_sizeY);
unsigned int b(sX+m_sizeZ);
for(unsigned int i(1);i<m_sizeX-1;i++){
    for(unsigned int j(1);j<m_sizeY-1;j++){     
        for(unsigned int k(1);k<m_sizeZ-1;k++){
            m_r[b+k]=m_func[b+k]-m_cX*(m_data[b+k-sX]+m_data[b+k+sX]-2.0*m_data[b+k])
                        -m_cY*(m_data[b+k-m_sizeZ]+m_data[b+k+m_sizeZ]-2.0*m_data[b+k])
                        -m_cZ*(m_data[b+k-1]+m_data[b+k+1]-2.0*m_data[b+k]);
        }
        b+=m_sizeZ;
    }
    b+=2*m_sizeZ;
}

我的数组大小为m_sizeX * m_sizeY * m_sizeZ。

的两倍

我以这种方式迭代,因为我不想触及域的边界。

使用(g ++)-msse2 -ftree-vectorizer-verbose = 2进行编译时,我当然会得到多个嵌套循环注释。

有没有办法在没有(或多或少)复杂的检查操作的情况下使用单个循环?

谢谢!

4 个答案:

答案 0 :(得分:2)

如果您的目标是良好的矢量化,最好将相同的计算应用于边缘点和内部点,只有在计算完所有点后重置它们。像这样:

unsigned int sX(m_sizeZ*m_sizeY);
unsigned int start = (1*m_sizeY + 1)*m_sizeZ + 1;
unsigned int end = ((m_sizeX - 1)*m_sizeY - 1)*m_sizeZ - 1;
//Do calculation for everything, including the edges.
for(unsigned int i = start; i < end; i++) {
    m_r[i]=m_func[i]-m_cX*(m_data[i-sX]+m_data[i+sX]-2.0*m_data[i])
                -m_cY*(m_data[i-m_sizeZ]+m_data[i+m_sizeZ]-2.0*m_data[i])
                -m_cZ*(m_data[i-1]+m_data[i+1]-2.0*m_data[i]);
}
//Reset the edges.
for(unsigned x = 0; x < m_sizeX; x++) {
    for(unsigned y = 0; y < m_sizeY; y++) {
        m_r[x*sX + y*m_sizeZ] = m_data[x*sX + y*m_sizeZ];
        m_r[x*sX + y*m_sizeZ + m_sizeZ-1] = m_data[x*sX + y*m_sizeZ + m_sizeZ-1];
    }
}
for(unsigned x = 0; x < m_sizeX; x++) {
    for(unsigned z = 0; z < m_sizeZ; z++) {
        m_r[x*sX + z] = m_data[x*sX + z];
        m_r[x*sX + (m_sizeY-1)*m_sizeZ + z] = m_data[x*sX + (m_sizeY-1)*m_sizeZ + z];
    }
}

这是可以进行的额外计算,但它有两个积极的影响:

  1. 现在编译器很容易对第一个循环进行矢量化(大部分时间都是这样)。

  2. 这种方法几乎消除了由固定向量大小引起的边缘问题:由于向量单元在一个处理多个对齐(!)循环迭代,计算中的每个边缘都会导致需要进行两次特殊迭代完成。一个在运行开始时将矢量循环对齐,另一个在末尾处理矢量循环的剩余部分。

答案 1 :(得分:1)

您可以在一个循环中从1m_sizeX*m_sizeY*m_sizeZ(使用计数器C)进行迭代并计算ij,{{1 as:

k

此方法的约束条件是您必须处理i = C / (m_sizeY*m_sizeZ) j = (C % (m_sizeY*m_sizeZ)) / m_sizeZ k = (C % (m_sizeY*m_sizeZ)) % m_sizeZ 范围内的m_sizeX*m_sizeY*m_sizeZ而不会溢出。

修改

要在不使用C子句的情况下控制边界,您可以创建一个函数

if-else

并在循环中使用它:

size_t nextToCalculate(size_t previous)
{
    return previous+1+!condition;
}

甚至可以将其实施纳入其中:

for(int C = 0; C < m_sizeX*m_sizeY*m_sizeZ; C = nextToCalculate(C))
{
  int z = (C % (m_sizeY*m_sizeZ)) % m_sizeZ;
  int y = (C % (m_sizeY*m_sizeZ)) / m_sizeZ;
  int x = C / (m_sizeY*m_sizeZ);
  ...
  ...
  ...
}

答案 2 :(得分:0)

将代码作为可编译的整个函数。初看:

  • 使用const int m_sizeX_1 = m_sizeX-1并将其用于for循环
  • 将临时变量用于b + k
  • 甚至为m_data + b + k
  • 使用临时变量
  • 对矢量化的一般建议 - 使用提取变量简化代码,然后您将更容易接下来做什么

答案 3 :(得分:0)

你可以试试这个(单圈,如你所要求的):

unsigned int sX(m_sizeZ*m_sizeY);
unsigned int b(sX+m_sizeZ);
unsigned int i, j, k;
for (i = 1, j = 1, k = 1;
     i < m_sizeX-1 && j < m_sizeY - 1 && k < m_sizeZ - 1;
     k++) {
    m_r[b+k]=m_func[b+k]-m_cX*(m_data[b+k-sX]+m_data[b+k+sX]-2.0*m_data[b+k])
             -m_cY*(m_data[b+k-m_sizeZ]+m_data[b+k+m_sizeZ]-2.0*m_data[b+k])
             -m_cZ*(m_data[b+k-1]+m_data[b+k+1]-2.0*m_data[b+k]);
    if (k == (m_sizeZ - 2)) {
      if (j == (m_sizeY - 2)) {
         b+=2*m_sizeZ;
         j = 0;
         i++;
     }
     k = 0;
     b+=m_sizeZ;
     j++;
    }
}