Почему версия 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.