正确使用嵌套的#pragma omp指令

时间:2011-11-24 16:00:54

标签: c++ openmp

以下代码在应用OpenMP并行化之前像魅力一样运行。实际上,以下代码处于无限循环状态!我确信这是因为我对OpenMP指令的错误使用造成的。你能告诉我正确的方法吗?非常感谢你。

          #pragma omp parallel for
          for (int nY = nYTop; nY <= nYBottom; nY++)
          {   
              for (int nX = nXLeft; nX <= nXRight; nX++)
              {   
                  // Use look-up table for performance
                  dLon = theApp.m_LonLatLUT.LonGrid()[nY][nX] + m_FavoriteSVISSRParams.m_dNadirLon;
                  dLat = theApp.m_LonLatLUT.LatGrid()[nY][nX];

                  // If you don't want to use longitude/latitude look-up table, uncomment the following line
                  //NOMGeoLocate.XYToGEO(dLon, dLat, nX, nY);

                  if (dLon > 180 || dLat > 180)
                  {  
                     continue;
                  }

                  if (Navigation.GeoToXY(dX, dY, dLon, dLat, 0) > 0) 
                  {  
                     continue;
                  }

                  // Skip void data scanline
                  dY = dY - nScanlineOffset;

                  // Compute coefficients as well as its four neighboring points' values
                  nX1 = int(dX);
                  nX2 = nX1 + 1;
                  nY1 = int(dY);
                  nY2 = nY1 + 1;

                  dCx = dX - nX1;
                  dCy = dY - nY1;

                  dP1 = pIRChannelData->operator [](nY1)[nX1];
                  dP2 = pIRChannelData->operator [](nY1)[nX2];
                  dP3 = pIRChannelData->operator [](nY2)[nX1];
                  dP4 = pIRChannelData->operator [](nY2)[nX2];

                  // Bilinear interpolation
                  usNomDataBlock[nY][nX] = (unsigned short)BilinearInterpolation(dCx, dCy, dP1, dP2, dP3, dP4);
              } 
          }

4 个答案:

答案 0 :(得分:4)

不要将它嵌得太深。通常,只需一个指令即可确定并行化的好点。

一些评论,可能是您问题的根源:

      #pragma omp parallel default(shared)  // Here you open several threads ...
      {   
          #pragma omp for
          for (int nY = nYTop; nY <= nYBottom; nY++)  
          {                                          

              #pragma omp parallel shared(nY, nYBottom) // Same here ...
              {   
                  #pragma omp for
                  for (int nX = nXLeft; nX <= nXRight; nX++)
                  { 

(从概念上讲)你打开了许多线程,在每个线程中你都会在for循环中再次打开多个线程。对于for循环中的每个线程,你再次打开多个线程,对于每个线程,你再次打开许多线程for for循环。

模式匹配单词中的(thread (thread)*)+;应该只有thread+

只做一个并行。不要在外循环上进行细粒度和并行化,每个线程应尽可能长时间运行:

#pragma omp parallel for
for (int nY = nYTop; nY <= nYBottom; nY++)
{      
    for (int nX = nXLeft; nX <= nXRight; nX++)
    {
    }
}

避免线程之间的数据和缓存共享(线程不应该在数据上太精细化的另一个原因)。

如果它运行稳定且速度很快,您可以根据OpenMP参考卡使用不同的调度算法对其进行微调。

并将您的变量声明放在您真正需要它们的位置。不要覆盖兄弟线程读取的内容。

答案 1 :(得分:3)

您还可以有效地折叠多个循环。循环条件有限制:它们必须是独立的。不仅如此,并非所有编译器都支持“崩溃”lexem。 (对于使用OpenMP的gcc,它可以工作。)

      int i,j,k;
      #pragma omp parallel for collapse(3)
      for(i=0; i<=N-1; i++)
          for(j=0; j<=N-1; j++)
              for(k=0; k<=N-1; k++)
              {
                  // something useful... 
              }

答案 2 :(得分:2)

在实践中,通常最有效的是并行化最外层循环。并行化所有内部循环可能会给你太多线程(虽然OpenMP坚持使用硬件执行单元的数量,否则没有说明)。更重要的是 - 并行化内部循环很可能会经常创建和销毁线程,这是一项昂贵的操作。您的CPU将执行线程API调用而不是您的工作负载。

不是真正的答案,但我想我会分享经验。

答案 3 :(得分:2)

内部循环中分配的所有变量都存在写安全性问题。每个线程都会尝试为相同的变量赋值,很可能你会得到垃圾。例如,两个线程可能同时更新dLon,导致线程1将线程2的值传递给Navigation.GeoToXY(dX, dY, dLon, dLat, 0)。由于您在循环中调用其他方法,因此在垃圾参数上调用的那些方法可能不会终止。

要解决此问题,请在应用omp parallel for后立即在外部循环中声明局部变量,或者使用private之类的firstprivate子句使OpenMP自动为每个变量创建局部变量线。在firstprivate的情况下,它将复制初始化的全局值。例如,

int dLon = 0;
#pragma omp parallel for firstprivate(dLon) // dLon = 0 for each thread
for (...)
{
    // each thread has it's own dLon variable so there's no clash in writing
    dLon = ...;
}

点击此处查看有关条款的详情:https://computing.llnl.gov/tutorials/openMP/