在创建3D网格时嵌套for()循环的OpenMP

时间:2015-04-18 18:07:15

标签: c++ openmp

我正在尝试使用openmp在创建3D网格时并行化以下功能。已声明了Point,Triangle和Box类;结构化,但代码不粘贴在这里,因为它包含在问题中很长(约110行)。如果你需要它并允许我粘贴它,那么我可以编辑这个问题。

我尝试在最外面的for()循环上使用#pragma omp parallel for,程序在运行时崩溃了。然后,我尝试在下面实现以下#pragma代码,程序花了更长的时间来运行它(与串行编码相比)。我在实现它时提到了这个链接here。实际上,首先,是否可以使用openmp并行化下面的代码?如果是,那么我真的需要你的帮助。

int main()
{
    vector<Box> boxes;
    Triangle dummy1;

    float dx = 81.0, dy = 121.0, dz = 98.0, delta=1.0;
    long int nx = 5*ceil(dx/(5.0*delta));
    long int ny = 5*ceil(dy/(5.0*delta));
    long int nz = 5*ceil(dz/(5.0*delta));
    long int Box_ID=1, Pt_ID=1;
    float x_val=0.0, y_val=0.0, z_val=0.0;
    float x0=-42.0f, y0=-3.0f, z0=-52.0f;

    long int i, j, k;
    for(i=0; i<nz+2; i++)
    {
        z_val=i*delta + z0;
        for(j=0; j<ny+2; j++)
        {
            y_val=j*delta + y0;
            #pragma omp parallel
            {
                vector<Box> box_private;
                #pragma omp for nowait schedule(static)
                for(k=0; k<nx+2; k++)
                {
                    x_val=k*delta + x0;
                    Point x1(x_val, y_val, z_val, Pt_ID);
                    Point x2(x_val+delta, y_val, z_val, Pt_ID+1);
                    Point x3(x_val, y_val+delta, z_val, Pt_ID+2);
                    Point x4(x_val+delta, y_val+delta, z_val, Pt_ID+3);
                    Point x5(x_val, y_val, z_val+delta, Pt_ID+4);
                    Point x6(x_val+delta, y_val, z_val+delta, Pt_ID+5);
                    Point x7(x_val, y_val+delta, z_val+delta, Pt_ID+6);
                    Point x8(x_val+delta, y_val+delta, z_val+delta, Pt_ID+7);
                    box_private.push_back(Box(x1,x2,x3,x4,x5,x6,x7,x8,dummy1,Box_ID));
                    Box_ID++;
                    Pt_ID++;
                }
                #pragma omp for schedule(static) ordered
                for(int i=0; i<omp_get_num_threads(); i++)
                {
                    #pragma omp ordered
                    boxes.insert(boxes.end(), box_private.begin(), box_private.end());
                }
            }
        }
    }
}

当我实现下面的代码而不是上面的代码时程序崩溃。

    #pragma omp parallel for
    for(i=0; i<nz+2; i++)
    {
        z_val=i*delta + z0;
        for(j=0; j<ny+2; j++)
        {
            y_val=j*delta + y0;
            for(k=0; k<nx+2; k++)
            {
                x_val=k*delta + x0;
                /* Point x1 to x8...*/
                boxes.push_back(Box(x1,x2,x3,x4,x5,x6,x7,x8,dummy1,Box_ID));
                Box_ID++;
                Pt_ID++;
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

您的代码存在的问题是您不了解共享变量和私有变量之间的区别,因此您有多种竞争条件。 并行部分内定义的变量是私有的,外部定义的变量是共享的。由于您在外部定义了所有内容(box_private除外),因此所有内容都是共享的(除了box_privatek,OpenMP使其无论如何都是私有的。)

但是,即使将适当的变量设为私有,也无法解决问题,因为Box_ID++Pt_ID++。您可以使用atomic修复它们,但这是不必要且效率低下的。如果您定义Box_ID = ny*nx*i + nx*j + k;Pt_ID相同),那么您的代码应该有效。

for(long i=0; i<nz+2; i++) {
    float z_val=i*delta + z0;
    for(long j=0; j<ny+2; j++) {
        float y_val=j*delta + y0;
        #pragma omp parallel
        {
            vector<Box> box_private;
            #pragma omp for nowait schedule(static)
            for(long k=0; k<nx+2; k++) {
                float x_val=k*delta + x0;
                long Box_ID = ny*nx*i + nx*j + k;
                long Pt_ID  = ny*nx*i + nx*j + k;                    
                Point x1(x_val, y_val, z_val, Pt_ID);
                // Point x2 - x8
                box_private.push_back(Box(x1,x2,x3,x4,x5,x6,x7,x8,dummy1,Box_ID));

但我认为你应该退一步思考一下你想做什么。如果您提前知道要填充多少个框,则没有理由使用push_back或私有向量。实际上你应该可以做到

#pragma omp parallel for schedule(static)
for(long i=0; i<nz+2; i++) {
    float z_val=i*delta + z0;
    for(long j=0; j<ny+2; j++) {
        float y_val=j*delta + y0;
        for(long k=0; k<nx+2; k++) {
            float x_val=k*delta + x0;
            long Box_ID = ny*nx*i + nx*j + k;
            long Pt_ID  = ny*nx*i + nx*j + k;               
            Point x1(x_val, y_val, z_val, Pt_ID);
            // Point x2 - x8
            boxes[ny*nx*i + nx*j +k] = Box(x1,x2,x3,x4,x5,x6,x7,x8,dummy1,Box_ID);
        }
    }
}

其中box是C数组,或者如果它是std:vector,请确保调整大小。

最后,如果您移动z_valy_valk的{​​{1}}与x_val一起移动,然后执行#pragma omp parallel for schedule(static) collapse(3),则可以折叠三个循环。但你也可以这样做by hand

long x = nz + 2, y = ny + 2, z = nx + 2;
#pragma omp parallel for schedule(static)
for(long n=0; n<(x*y*z); n++) {
    long i = n/(y*z);
    long j = (n%(y*z))/z;
    long k = (n%(y*z))%z;
    z_val=i*delta + z0;
    y_val=j*delta + y0;
    x_val=k*delta + x0;
    Point x1(x_val, y_val, z_val, n+1);
    // Point x2 - x8
    boxes[n] = Box(x1,x2,x3,x4,x5,x6,x7,x8,dummy1,n+1);
}

您真的需要Box_IDPt_ID吗?

答案 1 :(得分:0)

在内部循环上具有并行性的第一个代码变体运行缓慢,因为管理线程和合并向量的开销与实际运行的代码相比花费了太多时间。减速的另一个原因是广泛使用shared变量。当OpenMP同步对它们的访问时,需要花费很多时间。通常,您应该尝试最小化共享变量的使用。我相信好的风格会为你的并行部分指定default(none),并明确指定你想要共享的变量。

为了提高实际代码和管理代码之间的比例,您应该使并行区域尽可能大,并尽可能减少线程之间的同步。因此,您应该平行最外层的循环以获得最佳效果。

在您的第二种方法中,您忘记了同步。您不能insert来自不同线程的相同boxes向量。您可以以完全相同的方式应用您在第一种方法中使用的同步机制。