OpenMP 中的并行图像处理 - 分割图像
Parallel Image Processing in OpenMP - Splitting Image
我有一个由英特尔 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%)。
我认为这可能与我无法 "Promise" 每个线程收到的区域是 "Read Only"。
而且要让它知道它写入的内存位置也是只属于自己的。
我读到有关将变量定义为私有和共享的内容,但我找不到处理数组和指针的指南。
在 OpenMP 中处理指针和子数组的正确方法是什么?
线程 IPP 的性能比较如何?
假设没有竞争条件,写入共享数组的性能问题最有可能发生在高速缓存行中,其中行的一部分由一个线程写入,另一部分由另一个线程读取。
在看到完全并行加速之前,可能需要大于 10 兆字节左右的数据区域。
您需要更深入的分析,例如通过英特尔 VTune 放大器,查看内存带宽或数据重叠是否限制了性能。
使用英特尔 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 );
}
}
谢谢。
我有一个由英特尔 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%)。
我认为这可能与我无法 "Promise" 每个线程收到的区域是 "Read Only"。
而且要让它知道它写入的内存位置也是只属于自己的。
我读到有关将变量定义为私有和共享的内容,但我找不到处理数组和指针的指南。
在 OpenMP 中处理指针和子数组的正确方法是什么?
线程 IPP 的性能比较如何?
假设没有竞争条件,写入共享数组的性能问题最有可能发生在高速缓存行中,其中行的一部分由一个线程写入,另一部分由另一个线程读取。
在看到完全并行加速之前,可能需要大于 10 兆字节左右的数据区域。
您需要更深入的分析,例如通过英特尔 VTune 放大器,查看内存带宽或数据重叠是否限制了性能。
使用英特尔 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 );
}
}
谢谢。