【问题标题】:Increment shared loop counter in OpenMP for progress reporting在 OpenMP 中增加共享循环计数器以进行进度报告
【发布时间】:2015-06-11 11:24:33
【问题描述】:

我想跟踪由长时间运行的光线追踪过程处理的总像素和光线。如果我每次迭代都更新共享变量,则该过程会因为同步而显着减慢。我想跟踪进度,最后仍能得到准确的计数结果。有没有办法使用 OpenMP for 循环来做到这一点?

这里有一些循环的代码:

void Raytracer::trace(RenderTarget& renderTarget, const Scene& scene, std::atomic<int>& sharedPixelCount, std::atomic<int>& sharedRayCount)
{
    int width = renderTarget.getWidth();
    int height = renderTarget.getHeight();
    int totalPixelCount = width * height;

    #pragma omp parallel for schedule(dynamic, 4096)
    for (int i = 0; i < totalPixelCount; ++i)
    {
        int x = i % width;
        int y = i / width;

        Ray rayToScene = scene.camera.getRay(x, y);
        shootRay(rayToScene, scene, sharedRayCount); // will increment sharedRayCount
        renderTarget.setPixel(x, y, rayToScene.color.clamped());

        ++sharedPixelCount;
    }
}

【问题讨论】:

    标签: c++ multithreading openmp raytracing


    【解决方案1】:

    既然动态调度的并行 for 循环的块大小为 4096,为什么不将其用作分摊计数器更新的粒度?

    例如,以下内容可能会起作用。我没有测试此代码,您可能需要为totalPixelCount%4096!=0 添加一些簿记。

    与上一个答案不同,除了循环本身隐含的分支之外,这不会向循环添加分支,许多处理器已为此优化指令。它也不需要任何额外的变量或算术。

    void Raytracer::trace(RenderTarget& renderTarget, const Scene& scene, std::atomic<int>& sharedPixelCount, std::atomic<int>& sharedRayCount)
    {
        int width = renderTarget.getWidth();
        int height = renderTarget.getHeight();
        int totalPixelCount = width * height;
    
        #pragma omp parallel for schedule(dynamic, 1)
        for (int j = 0; j < totalPixelCount; j+=4096)
        {
          for (int i = j; i < (i+4096); ++i)
          {
            int x = i % width;
            int y = i / width;
    
            Ray rayToScene = scene.camera.getRay(x, y);
            shootRay(rayToScene, scene, sharedRayCount);
            renderTarget.setPixel(x, y, rayToScene.color.clamped());
          }
          sharedPixelCount += 4096;
        }
    }
    

    不清楚为什么sharedPixelCount 需要在这个循环内更新,因为它没有在循环体中被引用。如果这是正确的,我建议改为以下。

    void Raytracer::trace(RenderTarget& renderTarget, const Scene& scene, std::atomic<int>& sharedPixelCount, std::atomic<int>& sharedRayCount)
    {
        int width = renderTarget.getWidth();
        int height = renderTarget.getHeight();
        int totalPixelCount = width * height;
    
        int reducePixelCount = 0;
        #pragma omp parallel for schedule(dynamic, 4096) \
                             reduction(+:reducePixelCount) \
                             shared(reducePixelCount)
        for (int i = 0; i < totalPixelCount; ++i)
        {
            int x = i % width;
            int y = i / width;
    
            Ray rayToScene = scene.camera.getRay(x, y);
            shootRay(rayToScene, scene, sharedRayCount);
            renderTarget.setPixel(x, y, rayToScene.color.clamped());
    
            ++reducePixelCount; /* thread-local operation, not atomic */
        }
    
        /* The interoperability of C++11 atomics and OpenMP is not defined yet,
         * so this should just be avoided until OpenMP 5 at the earliest. 
         * It is sufficient to reduce over a non-atomic type and 
         * do the assignment here. */
        sharedPixelCount = reducePixelCount;
    }
    

    【讨论】:

      【解决方案2】:

      下面是一个例子:

      void Raytracer::trace(RenderTarget& renderTarget, const Scene& scene, std::atomic<int>& sharedPixelCount, std::atomic<int>& sharedRayCount)
      {
          int width = renderTarget.getWidth();
          int height = renderTarget.getHeight();
          int totalPixelCount = width * height;
          int rayCount = 0;
          int previousRayCount = 0;
      
          #pragma omp parallel for schedule(dynamic, 1000) reduction(+:rayCount) firstprivate(previousRayCount)
          for (int i = 0; i < totalPixelCount; ++i)
          {
              int x = i % width;
              int y = i / width;
      
              Ray rayToScene = scene.camera.getRay(x, y);
              shootRay(rayToScene, scene, rayCount);
              renderTarget.setPixel(x, y, rayToScene.color.clamped());
      
              if ((i + 1) % 100 == 0)
              {
                  sharedPixelCount += 100;
                  sharedRayCount += (rayCount - previousRayCount);
                  previousRayCount = rayCount;
              }
          }
      
          sharedPixelCount = totalPixelCount;
          sharedRayCount = rayCount;
      }
      

      循环运行时,它不会 100% 准确,但错误可以忽略不计。最后将报告确切的值。

      【讨论】:

        猜你喜欢
        • 2015-03-18
        • 1970-01-01
        • 1970-01-01
        • 2022-10-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-08
        相关资源
        最近更新 更多