Параллельный цикл в openmp

18

Я пытаюсь распараллелить очень простой for-loop, но это моя первая попытка использовать openMP за долгое время. Меня поражает время работы. Вот мой код:

#include <vector>
#include <algorithm>

using namespace std;

int main () 
{
    int n=400000,  m=1000;  
    double x=0,y=0;
    double s=0;
    vector< double > shifts(n,0);


    #pragma omp parallel for 
    for (int j=0; j<n; j++) {

        double r=0.0;
        for (int i=0; i < m; i++){

            double rand_g1 = cos(i/double(m));
            double rand_g2 = sin(i/double(m));     

            x += rand_g1;
            y += rand_g2;
            r += sqrt(rand_g1*rand_g1 + rand_g2*rand_g2);
        }
        shifts[j] = r / m;
    }

    cout << *std::max_element( shifts.begin(), shifts.end() ) << endl;
}

Я скомпилирую его с помощью

g++ -O3 testMP.cc -o testMP  -I /opt/boost_1_48_0/include

, то есть «-fopenmp», и я получаю эти тайминги:

real    0m18.417s
user    0m18.357s
sys     0m0.004s

, когда я использую «-fopenmp»,

g++ -O3 -fopenmp testMP.cc -o testMP  -I /opt/boost_1_48_0/include

Я получаю эти числа за время:

real    0m6.853s
user    0m52.007s
sys     0m0.008s

, что для меня не имеет смысла. Как использование восьми ядер может привести только к 3-кратным  повышение производительности? Я правильно кодирую цикл?

    
задан dsign 02.08.2012 в 09:46
источник
  • Ваши обращения к памяти очень локальные. Вероятно, вы делаете ужасные вещи в кеш процессора. Есть некоторые накладные расходы на отрасль / объединение материала, и вы можете ограничить пропускную способность памяти. –  Flexo♦ 02.08.2012 в 09:47
  • Должны ли x, y и r быть закрытыми () с помощью OpenMP? Как вы можете получить неправильные результаты. –  SinisterMJ 02.08.2012 в 10:01
  • @Anton Еще лучше, они должны быть объявлены внутри цикла. Предварительные объявления переменных в начале функции - это запах кода в C ++. –  Konrad Rudolph 02.08.2012 в 10:54
  • @KonradRudolph, это не «запах кода», если вы хотите узнать, где заканчивается эта случайная прогулка. –  Hristo Iliev 02.08.2012 в 11:18
  • @HristoIliev. Тогда они не могут быть частными. Это запах кода. Всегда. Объявляйте переменные при их инициализации, а не раньше. –  Konrad Rudolph 02.08.2012 в 11:28

3 ответа

22

Вы должны использовать предложение OpenMP reduction для x и y :

#pragma omp parallel for reduction(+:x,y)
for (int j=0; j<n; j++) {

    double r=0.0;
    for (int i=0; i < m; i++){

        double rand_g1 = cos(i/double(m));
        double rand_g2 = sin(i/double(m));     

        x += rand_g1;
        y += rand_g2;
        r += sqrt(rand_g1*rand_g1 + rand_g2*rand_g2);
    }
    shifts[j] = r / m;
}

С reduction каждый поток накапливает свою собственную частичную сумму в x и y , а в конце все частичные значения суммируются вместе, чтобы получить окончательные значения.

Serial version:
25.05s user 0.01s system 99% cpu 25.059 total
OpenMP version w/ OMP_NUM_THREADS=16:
24.76s user 0.02s system 1590% cpu 1.559 total

См. - сверхлинейное ускорение:)

    
ответ дан Hristo Iliev 02.08.2012 в 10:30
  • +1, я думаю, это то, чего хочет OP. На первый взгляд это не выглядело как сокращение, так как казалось, что x и y используются для чего-то другого. Позже я понял, что это фактически само сокращение. –  Mysticial 02.08.2012 в 10:40
  • @Mysticial Да, потому что на самом деле это не мой код, я немного путался в использовании переменных. Получение правильного расположения данных действительно помогло. –  dsign 02.08.2012 в 11:04
  • что, если x, y - двойной массив? Тогда сокращение сделало бы копию для каждого потока, который бы потребил n раз память, а затем серийную версию, правильно? –  avocado 02.04.2018 в 09:53
  • @avocado, это правильно. Это часто случается с параллельными алгоритмами - вы используете пространство для использования на скорости. –  Hristo Iliev 03.04.2018 в 08:23
7

давайте попробуем понять, как распараллеливать простой цикл, используя OpenMP

#pragma omp parallel
#pragma omp for
    for(i = 1; i < 13; i++)
    {
       c[i] = a[i] + b[i];
    }

предположим, что у нас есть 3 доступных потоков, вот что произойдет

во-первых

  • В потоках назначается независимый набор итераций

и, наконец,

  • Потоки должны подождать в конце конструкции совместного использования.
ответ дан Basheer AL-MOMANI 02.05.2016 в 18:13
  • Помог мне, большое спасибо. –  GntS 29.08.2017 в 13:52
0

То, что вы можете достичь максимум (!), - это линейное ускорение. Теперь я не помню, какой из них имеет время от linux, но я бы предложил вам использовать time.h или (в c ++ 11) «chrono» и измерить время выполнения непосредственно из программы. Лучше всего поместить весь код в цикл, запустите его 10 раз и усредним, чтобы получить прогр. Время выполнения.

Кроме того, у вас есть проблема с x, y - которые не придерживаются парадигмы локальности данных в параллельном программировании.

    
ответ дан Nox 02.08.2012 в 09:51
  • «То, что вы можете достичь максимум (!), - это линейное ускорение». - Неправильно! При наличии правильных шаблонов доступа к данным часто наблюдаются сверхлинейные ускорения для неловко параллельных проблем, подобных этому, потому что больше данных подходит для комбинированного кэша ЦП. –  Hristo Iliev 02.08.2012 в 10:57