Параллельная обработка изображений в OpenMP — разделение изображения

У меня есть функция, определенная Intel IPP для работы с изображением/регионом изображения.
Входными данными для изображения являются указатель на изображение, параметры для определения размера для обработки и параметры фильтра.
Функция IPP является однопоточной.

Теперь у меня есть изображение размером M x N.
Я хочу применить к нему фильтр параллельно.
Основная идея проста: разбить изображение на 4 части изображения, которые не зависят друг от друга.< br> Примените фильтр к каждому подизображению и запишите результат в подблок пустого изображения, где каждый поток записывает в отдельный набор пикселей.
Это действительно похоже на обработку 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?


person Royi    schedule 28.03.2015    source источник
comment
Ваша операция связана с пропускной способностью памяти, поэтому она не может масштабироваться с количеством физических ядер (если только ваш фильтр не намного больше). Тем не менее, я бы ожидал более чем 10-20% улучшения. Обычно я не распараллеливаю цикл на основе количества потоков. Я бы сделал это для количества пикселей или что-то в этом роде.   -  person Z boson    schedule 30.03.2015
comment
Как я могу доказать себе, что эта проблема ограничена памятью?   -  person Royi    schedule 30.03.2015
comment
Вероятно, вам следует использовать для этого какой-нибудь инструмент профилирования. Но это не то, чем я занимаюсь. Я определяю FLOPS операции и сравниваю его с пиковыми FLOPS процессора. Я также определяю, какая полоса пропускания используется операцией (это тоже можно рассчитать), и сравниваю ее с пиковой пропускной способностью процессора. Если операция намного меньше пикового FLOPS и ограничена пропускной способностью, то она ограничена пропускной способностью памяти.   -  person Z boson    schedule 10.04.2015


Ответы (2)


Как сравнивается производительность многопоточного IPP? При отсутствии условий гонки проблемы с производительностью при записи в разделяемые массивы чаще всего возникают в строках кэша, где часть строки записывается одним потоком, а другая часть читается другим. Скорее всего, потребуется область данных размером более 10 мегабайт или около того, прежде чем будет видно полное параллельное ускорение.
Вам потребуется более глубокий анализ, например. с помощью Intel VTune Amplifier, чтобы узнать, ограничивают ли пропускную способность памяти или перекрытие данных производительность.

person tim18    schedule 29.03.2015

Используя Intel IPP Filter, лучшим решением было использование:

 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 );
            }
        }

Спасибо.

person Royi    schedule 10.04.2015