Почему версия OpenMP работает медленнее?

Я экспериментирую с OpenMP. Я написал код для проверки его производительности. На 4-ядерном процессоре Intel с процессором Kubuntu 11.04 следующая программа, скомпилированная с OpenMP, примерно в 20 раз медленнее, чем программа, скомпилированная без OpenMP. Почему?

Я скомпилировал его с помощью g++ -g -O2 -funroll-loops -fomit-frame-pointer -march = native -fopenmp

#include <math.h>
#include <iostream>

using namespace std;

int main ()
{
  long double i=0;
  long double k=0.7;

  #pragma omp parallel for reduction(+:i)
  for(int t=1; t<300000000; t++){       
    for(int n=1; n<16; n++){
      i=i+pow(k,n);
    }
  }

  cout << i<<"\t";
  return 0;
}

Ответ 1

Проблема заключается в том, что переменная k считается общей переменной, поэтому ее необходимо синхронизировать между потоками. Возможное решение, чтобы избежать этого:

#include <math.h>
#include <iostream>

using namespace std;

int main ()
{
  long double i=0;

#pragma omp parallel for reduction(+:i)
  for(int t=1; t<30000000; t++){       
    long double k=0.7;
    for(int n=1; n<16; n++){
      i=i+pow(k,n);
    }
  }

  cout << i<<"\t";
  return 0;
}

Следуя намеку Мартина Беккета в комментарии ниже, вместо объявления k внутри цикла, вы также можете объявить k const и вне цикла.

В противном случае ejd верен - проблема здесь не кажется плохим распараллеливанием, но плохая оптимизация при распараллеливании кода. Помните, что реализация OpenMP в gcc довольно молода и далека от оптимальной.

Ответ 2

Самый быстрый код:

for (int i = 0; i < 100000000; i ++) {;}

Слегка медленный код:

#pragma omp parallel for num_threads(1)
for (int i = 0; i < 100000000; i ++) {;}

2-3 раза более медленный код:

#pragma omp parallel for
for (int i = 0; i < 100000000; i ++) {;}

независимо от того, что находится между {и}. Просто; или более сложные вычисления, те же результаты. Я скомпилирован под 64-разрядным Ubuntu 13.10, используя как gcc, так и g++, пробую разные параметры -ansi -pedantic-errors -Wall -Wextra -O3 и работает на четырехъядерных процессорах Intel с частотой 3,5 ГГц.

Я думаю, что накладные расходы на управление потоками виноваты? Для OMP не кажется разумным создавать поток каждый раз, когда вам это нужно, и уничтожать его после. Я думал, что будет четыре (или восемь) нитей, которые будут выполняться в случае необходимости или сна.

Ответ 3

Я наблюдаю подобное поведение на GCC. Однако мне интересно, если в моем случае это как-то связано с шаблоном или встроенной функцией. Является ли ваш код также внутри шаблона или встроенной функции? Посмотрите здесь.

Однако для очень коротких циклов вы можете наблюдать некоторые небольшие накладные расходы, связанные с переключением потоков, как в вашем случае:

#pragma omp parallel for
for (int i = 0; i < 100000000; i ++) {;}

Если ваш цикл выполняется в течение очень долгого времени в несколько миллисекунд или даже секунд, вы должны наблюдать повышение производительности при использовании OpenMP. Но только тогда, когда у вас более одного процессора. Чем больше ядер у вас есть, тем выше производительность, которую вы достигаете с помощью OpenMP.