Я заметил неожиданное (для меня!) Поведение кода openmp, который я пишу. Структура кода заключается в следующем:Проблемы с производительностью #pragma omp atomic с OMP_NUM_THREADS = 1
#pragma omp parallel for
for(int i=0;i<N;i++){
// lots of calculations that produce 3 integers i1,i2,i3 and 3 doubles d1,d2,d3
#pragma omp atomic
J1[i1] += d1;
#pragma omp atomic
J2[i2] += d2;
#pragma omp atomic
J3[i3] += d3;
}
Я составил три различные версии этого кода:
1) с OpenMP (-fopenmp)
2) без OpenMP
3) с openmp, но без 3-х атомных операций (точно так же, как испытание, поскольку необходимы атомные операции)
Когда я запускаю версию 1) с переменной окружения O MP_NUM_THREADS = 1, я наблюдаю значительное замедление относительно версии 2); в то время как версия 3) работает так же быстро, как версия 2).
Я хотел бы знать причину такого поведения (почему атомные операции замедляют работу кода даже при однократном исключении ?!), и если можно скомпилировать/переписать код таким образом, что версия 1) работает так же быстро, как версия 2).
В конце вопроса я добавляю рабочий пример, который показывает вышеупомянутое поведение. Я составил 1) с:
g++ -fopenmp -o toy_code toy_code.cpp -std=c++11 -O3
2) с:
g++ -o toy_code_NO_OMP toy_code.cpp -std=c++11 -O3
и 3) с:
g++ -fopenmp -o toy_code_NO_ATOMIC toy_code_NO_ATOMIC.cpp -std=c++11 -O3
версия компилятора GCC версии 5.3.1 20160519 (Debian 5.3.1-20). Время выполнения 3-х версий:
1) 1 мин 24 сек
2) 51 сек
3) 51 сек
Заранее спасибо за любые советы!
// toy_code.cpp
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <cmath>
#include <omp.h>
#define Np 1000000
#define N 1000
int main(){
double* Xp, *Yp, *J,*Jb;
Xp = new double[Np];
Yp = new double[Np];
J = new double [N*N];
Jb = new double [N*N];
for(int i=0;i<N*N;i++){
J[i]=0.0;
Jb[i]=0.0;
}
for(int i=0;i<Np;i++){
Xp[i] = rand()*1.0/RAND_MAX - 0.5;
Yp[i] = rand()*1.0/RAND_MAX - 0.5;
}
for(int n=0; n<2000; n++){
#pragma omp parallel for
for(int p=0;p<Np;p++){
double rx = (Xp[p]+0.5)*(N-1);
double ry = (Yp[p]+0.5)*(N-1);
int xindex = (int)floor(rx+0.5);
int yindex = (int)floor(ry+0.5);
int k;
k=xindex*N+yindex;
#pragma omp atomic
J[k]+=1;
#pragma omp atomic
Jb[k]+=1;
}
}
delete[] Xp;
delete[] Yp;
delete[] J;
delete[] Jb;
return 0;
}
с включенными openmp, прагмы расширены до кода GOMP, что, вероятно, вызывает накладные расходы по сравнению с последовательным кодом –