Как я могу оптимизировать прагмы omp для запуска кода между параллельными регионами? - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть этот C-код, который мне нужно оптимизировать с помощью OpenMP, я не могу написать оригинальный код, но вот суррогат:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#ifdef _OPENMP
#include <omp.h>
#endif

void Funct(double *vec, int len)
{
    int i;
    double tmp; 
    //Section 1
    #pragma omp parallel for
    for ( i = 0; i < len; i++ )    //Code that initialize vec, it simulates an initialization in the original code
        vec [ i ] = i; 

    //Section 2
    //This code must be run sequentially
    tmp = vec [ 0 ];
    vec [0 ] = vec [ len - 1 ];
    vec [ len - 1 ] = tmp;

    tmp = vec [ 0 ];
    vec [0 ] = vec [ len - 1 ];
    vec [ len - 1 ] = tmp; 
    //End of the sequential code 

    //Section 3
    #pragma omp parallel for
    for ( i = 0; i < len; i++ )    //Code to simulate loadwork on vec
    {
        vec [ i ] = pow(vec[i], 2 ); 
        vec [ i ] = sqrt ( vec [ i ] );
        vec [ i ] += 1;
        vec [ i ] = pow(vec[i], 2 ); 
        vec [ i ] = sqrt ( vec [ i ] );
        vec [ i ] -= 1;
    }

}

int main ()
{
    double *vec;
    int i;
    vec = (double *) malloc ( sizeof ( double ) * 5104 );  //Length of the vector in the original code

    for ( i = 0; i < 1000000; i++ )    //Iteration in the original code 
        Funct(vec, 5104 );

    for ( i = 0; i < 5; i++ )      // Access the array to avoid -O2 cancellations
    printf ("%.2f ", vec [ i * 1000 ] );
    return 0;
}

В Funct, разделы 1, 2 и 3 должныисполняться последовательно;Раздел 2 строго последовательный.

В исходном коде я вынужден использовать распараллеливание внутри функции Funct (...), поэтому, к сожалению, стоимость создания потоков умножается на числоитераций, но это не проблема, так как это все еще позволяет некоторую оптимизацию времени, когда возникает внутренняя длина for main или vec (если у вас есть предложения, я очень открыт для прослушивания).Проблема в «Разделе 2», фактически он заставляет OMP создать барьер или ожидание, но это замедляет выполнение;Если я удаляю этот раздел, я получаю довольно приемлемую оптимизацию по отношению к последовательному коду;к сожалению, я не могу.Я попробовал omp single, omp критический и т. Д., Чтобы увидеть, назначил ли бы он код некоторым потокам в предыдущем пуле, но нет, есть ли способ сделать его более производительным?(Как резко изменить прагмы, не проблема)

(Скомпилировано с gcc file.c -o file.out -lm -O2 -fopenmp, протестировано в Linux Lubuntu с использованием time ./file.out)

Редактировать 1: Я хотел бы отметить, что

tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp;

tmp = vec [ 0 ];
vec [0 ] = vec [ len - 1 ];
vec [ len - 1 ] = tmp; 

Это просто случайный код, который я поместил в метод, чтобы прояснить, что он должен выполняться последовательно (он выполняет одну и ту же операцию два раза, меняет местами vec [0] и vec [len - 1], поэтому в конце выполнения ничего не происходит);Вместо этого я мог бы написать любую другую функцию или код;

Например, я мог бы поставить

Foo1();
Foo2();
Foo3();

Ответы [ 2 ]

0 голосов
/ 02 января 2019

В конце параллельного участка есть неявный барьер.Способ улучшить код состоит в том, чтобы заключить всю функцию в директиву #pragma omp parallel, чтобы потоки создавались только один раз в начале, а не дважды в разделах 1 и 3.

Неявный барьер будет по-прежнемубыть в конце цикла omp for, но это все равно меньше накладных расходов, чем создание новых потоков.Затем раздел 2 должен быть заключен в блок omp single (это вполне может быть то, что вы сделали, когда отметили, что omp single не сработал лучше, но это не на 100% ясно).

void Funct(double *vec, int len)
{
    // Create threads
    #pragma omp parallel
    {
        //Section 1
        #pragma omp for
        for (int i = 0; i < len; i++ ){
            //Code that initialize vec, it simulates an initialization in the original code
            vec [ i ] = i; 
        } // Implicit barrier here (end of omp for loop) 

        //Section 2
        //This code must be run sequentially
        // It will start only once the section 1 has been completed
        #pragma omp single
        {
            double tmp;

            tmp = vec [ 0 ];
            vec [0 ] = vec [ len - 1 ];
            vec [ len - 1 ] = tmp;

            tmp = vec [ 0 ];
            vec [0 ] = vec [ len - 1 ];
            vec [ len - 1 ] = tmp;
        } // Implicit barrier here (end of omp single block) 
        //End of the sequential code 

        //Section 3
        #pragma omp for
        for ( i = 0; i < len; i++ )    //Code to simulate loadwork on vec
        {
            vec [ i ] = pow(vec[i], 2 ); 
            vec [ i ] = sqrt ( vec [ i ] );
            vec [ i ] += 1;
            vec [ i ] = pow(vec[i], 2 ); 
            vec [ i ] = sqrt ( vec [ i ] );
            vec [ i ] -= 1;
        } // Implicit barrier here end of for
    } // Implicit barrier here end of parallel + destroy threads
}

Лучше всего было бы переместить директиву omp parallel в функцию main, чтобы потоки создавались только один раз.

0 голосов
/ 27 декабря 2018

Установите ваши индексы цикла равными

for ( i = 1; i < len-1; i++ )

и рассматривайте первый и последний элементы как особые случаи.Они могут быть выполнены за пределами регионов OpenMP.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...