openacc嵌套带有动态数组的循环

时间:2017-02-13 20:59:15

标签: c++ multidimensional-array openacc

我正在尝试应用openacc来开发多核和gpu加速二进制文件。我已经阅读了Farber的书,并从那里成功地运行了测试程序,并通过NVIDIA提供的一些在线课程。然后,我尝试并行化我们的遗留代码。

我正在编写的子程序依赖于在三个嵌套循环中使用的三维数组。对于nsteps的轨迹,这是典型的N(N + 1)/ 2对距离问题。对于我们的科学问题,nsteps通常是1E5到1E7,nparticles是1E4到5E5。

for(i=0 ; i < nsteps ; i++){
    pair = 0 ;
    for(j=0 ; j < nparticles-1 ; j++){
        x1 = position[i][j][0] ;
        y1 = position[i][j][1] ;
        z1 = position[i][j][2] ;
        for(k=j+1 ; k < nparticles ; k++){
            x2 = position[i][k][0] ;
            y2 = position[i][k][1] ;
            z2 = position[i][k][2] ;
            dx2 = (x1 - x2) * (x1 - x2) ;
            dy2 = (y1 - y2) * (y1 - y2) ;
            dz2 = (z1 - z2) * (z1 - z2) ;
            sdist = sqrt(dx2 + dy2 + dz2) ;
            dist[pair] += sdist ;
            pair++ ;
        }
    }
}

在编译时我无法控制输入位置数组(nsteps,nparticles),因为代码通过python运行到C ++扩展,将python numpy数组转换为C数组数据类型。 openacc代码将被编译为源对象库。 C ++扩展调用openacc代码。这是需要这种安排的遗留代码。在Python中定义系统,调用C ++扩展并访问openacc * .so文件的步骤可以正常工作。

问题是这个问题在本书或课程练习中并不明显,因此对问题的解决方案和/或评论可能对其他人有所帮助。

我尝试了并行,内核和循环&amp;使用Stack Overflow和我发现的其他资源上的一些示例显示的代码片段上的数据指令。据我所知,Faber书和其他来源中使用的示例并未涉及此问题中提出的用例。充其量我可以获得并行化的前两个循环,但是最内部的循环不并行化(循环未向量化:数据依赖性)。当我正在寻找一般指导时,我不会发布失败的位,以便提供更多教学讨论/提示。

好的,现在回答我的问题。

  1. 如何使用pragma指令处理输入位置数组的未知维度?
  2. 如何管理dist []数组的累积(由于粒子对的数量,它本身的长度在编译时是未知的)?
  3. 通常建议针对此问题使用哪些pragma指令?
  4. 如何处理“k-loop”对“j-loop”的依赖?
  5. 是否应该解决问题以帮助定义要使用的指令?
  6. THX,

    SB

    更新:

    根据@jefflarkin的建议提供结果我改变了代码以考虑dist数组的索引并添加了建议的编译指示。子程序编译良好并且并行运行。我现在将开始分析以了解如何优化例程以最大化资源利用率。以下是工作代码的副本:

    #pragma acc data copyin(position[nsteps][nparticles][3]) copy(dist[npairs])
    for(i=0 ; i < nsteps ; i++){
        #pragma acc parallel loop
        for(j=0 ; j < nparticles-1 ; j++){
            x1 = position[i][j][0] ;
            y1 = position[i][j][1] ;
            z1 = position[i][j][2] ;
            #pragma acc loop
            for(k=j+1 ; k < nparticles ; k++){
                x2 = position[i][k][0] ;
                y2 = position[i][k][1] ;
                z2 = position[i][k][2] ;
                dx2 = (x1 - x2) * (x1 - x2) ;
                dy2 = (y1 - y2) * (y1 - y2) ;
                dz2 = (z1 - z2) * (z1 - z2) ;
                sdist = sqrt(dx2 + dy2 + dz2) ;
                local_count = ((j*nparticles)-((j*(j+1))/2))+k-(j+1) ;
                dist[local_count] += sdist ;
            }
        }
    }
    

    我得到了这个编译器结果(pgc ++(16.10):CFLAGS = -fPIC -c -fast -acc -Minfo = accel -ta = multicore -O3 -shared)

     38, Generating Multicore code
     39, #pragma acc loop gang
     45, Loop is parallelizable
    

    和GPU(CFLAGS = -v -fPIC -c -fast -acc -Minfo = accel -ta = tesla:cuda8,fastmath -O3 -shared)

     35, Generating copyin(coor[:nframes][:nparticles][:3])
         Generating copy(dist[:npairs])
     38, Accelerator kernel generated
         Generating Tesla code
         39, #pragma acc loop gang /* blockIdx.x */
         45, #pragma acc loop vector(128) /* threadIdx.x */
     45, Loop is parallelizable
    

    其中,第35行是i循环(nsteps),第38行是j循环,第45行是k循环。

1 个答案:

答案 0 :(得分:0)

你说数组的大小是未知的,但你可以根据nparticles为你的数据子句计算它们。位置数组的大小为[nparticles] [nparticles] [3]。 dist数组有点棘手,因为k会缩小每个j,但它是(nparticles - 1)+(nparticles - 2)+ ... + 1的总和,我相信(nparticles *( nparticles - 1))/ 2。尽管在运行之前可能无法确定粒子的数量,但编译器必须知道有关数组的内容,否则[]将无法工作。

我建议将索引更改为dist数组,以便您可以根据j和k计算该数组的索引,否则您需要一个原子操作来保护对的增量和这并不能保证每一步都会使同一对点位于距离数组中的相同位置。

如果修复了这个问题,我认为步骤循环acc data,j循环上的acc parallel loop和k循环上的简单acc loop(以帮助编译出来依赖分析)应该让你运行起来。