【问题标题】:Parallel Image Processing in OpenMP - Splitting ImageOpenMP 中的并行图像处理 - 分割图像
【发布时间】:2023-03-30 16:36:01
【问题描述】:

我有一个由英特尔 IPP 定义的函数,用于对图像/图像区域进行操作。
图像的输入是指向图像的指针、定义要处理的大小的参数和过滤器的参数。
IPP 函数是单线程的。

现在,我有一张 M x N 大小的图片。
我想并行应用过滤器。
主要思想很简单,将图像分解成4个相互独立的子图像。
将过滤器应用于每个子图像并将结果写入空图像的子块,其中每个线程写入一组不同的像素。
这真的就像在自己的核心上处理 4 个图像。

这是我正在使用的程序:

void OpenMpTest()
{
    const int width  = 1920;
    const int height = 1080;

    Ipp32f input_image[width * height];
    Ipp32f output_image[width * height];

    IppiSize size = { width, height };

    int step = width * sizeof(Ipp32f);

    /* Splitting the image */
    IppiSize section_size = { width / 2, height / 2};

    Ipp32f* input_upper_left  = input_image;
    Ipp32f* input_upper_right = input_image + width / 2;
    Ipp32f* input_lower_left  = input_image + (height / 2) * width;
    Ipp32f* input_lower_right = input_image + (height / 2) * width + width / 2;

    Ipp32f* output_upper_left  = input_image;
    Ipp32f* output_upper_right = input_image + width / 2;
    Ipp32f* output_lower_left  = input_image + (height / 2) * width;
    Ipp32f* output_lower_right = input_image + (height / 2) * width + width / 2;

    Ipp32f* input_sections[4] = { input_upper_left, input_upper_right, input_lower_left, input_lower_right };
    Ipp32f* output_sections[4] = { output_upper_left, output_upper_right, output_lower_left, output_lower_right };

    /* Filter Params */
    Ipp32f pKernel[7] = { 1, 2, 3, 4, 3, 2, 1 };

    omp_set_num_threads(4);
    #pragma omp parallel for
    for (int i = 0; i < 4; i++)
        ippiFilterRow_32f_C1R(
                              input_sections[i], step,
                              output_sections[i], step,
                              section_size, pKernel, 7, 3);
}

现在的问题是,与在所有图像上工作的单线程模式相比,我没有看到任何收益。
我尝试更改图像大小或滤镜大小,但不会改变图片。
我能得到的最多没什么意义(10-20%)。

我认为这可能与我无法“承诺”每个线程接收到的区域为“只读”有关。
而且要让它知道它写入的内存位置也只属于他自己。
我阅读了有关将变量定义为私有和共享的内容,但我找不到处理数组和指针的指南。

在 OpenMP 中处理指针和子数组的正确方法是什么?

【问题讨论】:

  • 您的操作受内存带宽限制,因此无法随物理内核的数量而扩展(除非您的过滤器更大)。但是,我预计会有超过 10-20% 的改进。通常,我不会根据线程数并行化循环。我会为像素数之类的。
  • 我如何向自己证明这个问题是内存受限的?
  • 您可能应该为此使用一些分析工具。但这不是我要做的。我确定操作的 FLOPS 并将其与处理器的峰值 FLOPS 进行比较。我还确定了操作使用了多少带宽(您也可以计算它)并将其与处理器的峰值带宽进行比较。如果操作远小于峰值 FLOPS 并受带宽限制,则它是内存带宽限制。

标签: image-processing parallel-processing openmp icc intel-ipp


【解决方案1】:

线程 IPP 的性能比较如何? 假设没有竞争条件,写入共享数组的性能问题最有可能发生在高速缓存行中,其中部分行由一个线程写入,另一部分由另一个线程读取。 在看到完全并行加速之前,可能需要一个大于 10 兆字节左右的数据区域。
您需要更深入的分析,例如通过 Intel VTune Amplifier,查看内存带宽或数据重叠是否会限制性能。

【讨论】:

    【解决方案2】:

    使用英特尔 IPP 过滤器,最佳解决方案是:

     int height  = dstRoiSize.height;
            int width   = dstRoiSize.width;
           Ipp32f *pSrc1, *pDst1;
            int nThreads, cH, cT;
    
    #pragma omp parallel  shared( pSrc, pDst, nThreads, width, height, kernelSize,\
                                 xAnchor, cH, cT ) private( pSrc1, pDst1 )
            {
        #pragma omp master
                {
                    nThreads = omp_get_num_threads();
                    cH = height / nThreads;
                    cT = height % nThreads;
                }
        #pragma omp barrier
                {
                    int curH;
                    int id = omp_get_thread_num();
    
                    pSrc1 = (Ipp32f*)( (Ipp8u*)pSrc + id * cH * srcStep );
                    pDst1 = (Ipp32f*)( (Ipp8u*)pDst + id * cH * dstStep );
                    if( id != ( nThreads - 1 )) curH = cH;
                    else curH = cH + cT;
                    ippiFilterRow_32f_C1R( pSrc1, srcStep, pDst1, dstStep,
                                width, curH, pKernel, kernelSize, xAnchor );
                }
            }
    

    谢谢。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-12
      • 2022-10-17
      • 2019-12-30
      • 2016-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多