我观察到我正在编写的 openmp 代码的意外(对我来说!)行为。代码结构如下:
#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个原子操作(只是作为测试,因为原子操作是必要的)
当我使用环境变量 OMP_NUM_THREADS=1 运行版本 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;
}