嵌套循环的OpenMP偶数/奇数分解

时间:2015-06-16 14:36:07

标签: c++ c++11 parallel-processing openmp

我参与了可以并行完成的代码,所以我开始阅读有关openMP的内容并做了这些介绍示例。现在我试图将它应用于以下问题,这里示意性地显示:

Grid.h

class Grid
{
public:
    // has a grid member variable
    std::vector<std::vector<int>> 2Dgrid;
    // modifies the components of the 2Dgrid, no push_back() etc. used what could possibly disturbe the use of openMP
    update_grid(int,int,int,in);

};

Test.h

class Test
{
public:
    Grid grid1;
    Grid grid2;
    update();
    repeat_update();
};

Test.cc

.
.
.
Test::repeat_update() {
    for(int i=0;i<100000;i++)
        update();

}



Test::update() {
    int colIndex = 0;
    int rowIndex = 0;
    int rowIndexPlusOne = rowIndex + 1;
    int colIndexPlusOne = colIndex + 1;

// DIRECTION_X (grid[0].size()), DIRECTION_Y (grid.size) are the size of the grid

    for (int i = 0; i < DIRECTION_Y; i++) {
        // periodic boundry conditions
        if (rowIndexPlusOne > DIRECTION_Y - 1)
            rowIndexPlusOne = 0;

        // The following could be done parallel!!!
        for (int j = 0; j < DIRECTION_X - 1; j++) {
             grid1.update_grid(rowIndex,colIndex,rowIndexPlusOne,colIndexPlusOne);
             grid2.update_grid(rowIndex,colIndex,rowIndexPlusOne,colIndexPlusOne);


            colIndexPlusOne++;
            colIndex++;
        }
        colIndex = 0;
        colIndexPlusOne = 1;
        rowIndex++;
        rowIndexPlusOne++;
    }


}
.
.
.

问题是,Test::update(...)中完成的更新可以以并行方式完成,因为Grid::update(...)仅取决于网格的最近邻居。因此,例如在内部循环中,多个线程可以独立地执行colIndex = 0,2,4,...的工作,这将是均匀分解。之后,可以更新奇数索引colIndex=1,3,5,...。然后外循环向前迭代一个,并且方向x上的更新可以再次并行完成。我处理了16个内核,并行进行并行化可能是一个很好的时间。但是我完全没有透视去看看如何做到这一点,主要是因为我不知道如何跟踪colIndexrowIndex等,因为#pragma omp parallel for应用于i,j指数。如果有人能告诉我走出黑暗的道路,我将不胜感激。

1 个答案:

答案 0 :(得分:0)

如果不确切知道update_grid(int,int,int,int)做了什么,给出一个明确的答案是有点棘手的。您显示了一对

类型的嵌入式循环
for(int i = 0; i < Y; i++)
{
    for(int j = 0; j < X; j++)
    {
        //...
    }
}

并断言j循环可以并行完成。这将是细粒度parallelism的一个例子。您可以选择并行化i循环,这将是一个更粗粒度的并行化。如果每个单独线程的工作量大致相等,粗粒度方法的优点是开销较小(假设两个循环的并行化是等效的)。

在并行化循环时,您必须注意一些事项。对于初学者,您可以在内部循环中增加colIndexPlusOnecolIndex。如果colIndexPlusOnecolIndex有多个线程和单个变量,则每个线程将递增变量和/或race conditions。您可以通过多种方式绕过它,为每个线程提供变量的副本,或者增加atomiccritical,或者通过完全删除变量的依赖关系并计算它应该是什么动态循环的每一步。

我首先要将整个update函数并行化:

Test::update()
{
    #pragma omp parallel
    {
        int colIndex = 0;
        int colIndexPlusOne = colIndex + 1;

         // DIRECTION_X (grid[0].size()), DIRECTION_Y (grid.size) are the size of the grid
        #pragma omp for
        for (int i = 0; i < DIRECTION_Y; i++)
        {
            int rowIndex = i;
            int rowIndexPlusOne = rowIndex + 1;
            // periodic boundary conditions
            if (rowIndexPlusOne > DIRECTION_Y - 1)
                rowIndexPlusOne = 0;

            // The following could be done parallel!!!
            for (int j = 0; j < DIRECTION_X - 1; j++)
            {
                 grid1.update_grid(rowIndex,colIndex,rowIndexPlusOne,colIndexPlusOne);
                 grid2.update_grid(rowIndex,colIndex,rowIndexPlusOne,colIndexPlusOne);

                // The following two can be replaced by j and j+1...
                colIndexPlusOne++;
                colIndex++;
            }
            colIndex = 0;
            colIndexPlusOne = 1;
            // No longer needed:
            // rowIndex++;
            // rowIndexPlusOne++;
        }
    }

}

通过将#pragma omp parallel放在开头,所有变量都是每个线程的本地变量。此外,在i循环的开头,我分配rowIndex = i,至少在显示的代码中,就是这种情况。对于j循环和colIndex也可以这样做。