C 关键区域的开销超过for循环并行化的加速比
我正在尝试在函数中并行化以下串行代码C 关键区域的开销超过for循环并行化的加速比,c,openmp,C,Openmp,我正在尝试在函数中并行化以下串行代码 float arr[100]; for ( int i = 0; i < 100; ++i) arr[i]=0; int i,j; for ( i = 0; i < 100; ++i) { float* pi = arr+i; int index[10]; f(i, index); // f fills in the array index for (j = 0;
float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index); // f fills in the array index
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
float tmp = g(i,j); // some function g
*pi += tmp;
*pj += tmp;
}
}
}
float-arr[100];
对于(inti=0;i<100;++i)arr[i]=0;
int i,j;
对于(i=0;i<100;++i){
浮点数*pi=arr+i;
整数指数[10];
f(i,index);//f填充数组索引
对于(j=0;j<10;++j){
float*pj=arr+指数[j];
如果(pj>pi){
float tmp=g(i,j);//某些函数g
*pi+=tmp;
*pj+=tmp;
}
}
}
在另一个函数中会出现一个类似的函数
float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index); // f fills in the array index
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
h(pi,i); // function h updates the memory pointed by pi,
// by adding to to *pi something, which only depends on i but not on the values pointed by pi.
h(pj,j); //
}
}
}
float-arr[100];
对于(inti=0;i<100;++i)arr[i]=0;
int i,j;
对于(i=0;i<100;++i){
浮点数*pi=arr+i;
整数指数[10];
f(i,index);//f填充数组索引
对于(j=0;j<10;++j){
float*pj=arr+指数[j];
如果(pj>pi){
h(pi,i);//函数h更新pi指向的内存,
//通过向*pi添加一些东西,它只依赖于i,而不依赖于pi所指的值。
h(pj,j);//
}
}
}
我的方式是:
float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
#pragma omp parallel for shared(arr) private(i,j) schedule(auto)
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index);
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
float tmp = g(i,j);
*pi += tmp;
#pragma omp atomic update
*pj += tmp;
}
}
}
float-arr[100];
对于(inti=0;i<100;++i)arr[i]=0;
int i,j;
#共享(arr)专用(i,j)计划(自动)的pragma omp并行
对于(i=0;i<100;++i){
浮点数*pi=arr+i;
整数指数[10];
f(i,指数);
对于(j=0;j<10;++j){
float*pj=arr+指数[j];
如果(pj>pi){
浮点数tmp=g(i,j);
*pi+=tmp;
#pragma omp原子更新
*pj+=tmp;
}
}
}
及
float-arr[100];
对于(inti=0;i<100;++i)arr[i]=0;
int i,j;
#共享(arr)专用(i,j)计划(自动)的pragma omp并行
对于(i=0;i<100;++i){
浮点数*pi=arr+i;
整数指数[10];
f(i,指数);
对于(j=0;j<10;++j){
float*pj=arr+指数[j];
如果(pj>pi){
h(pi,i);
#pragma omp临界值(pj)
h(pj,j);//
}
}
}
我使用原子指令和临界指令,因为多个线程可以同时写入pj指向的内存。(如果我是正确的,线程不会同时写入pi指向的内存。)
但是,添加原子指令和关键指令会增加运行时间,使其与串行代码的运行时间大致相同
所以我想知道我该怎么办?将此代码并行化的整个想法是有缺陷的。根本没有足够的工作来证明启动和同步线程的开销是合理的 使用原子更新时,同步本身的开销并不太大,但关键部分会破坏所有性能:一次只能有一个线程进入,并且同步的典型开销时间大约为微秒(取决于等待的实现方式)。这比仅仅用一个核心做工作要糟糕得多
如果您完全准备对调用
h()
的循环进行并行化(这里我不打算使用“优化”一词),您应该确保h()
以线程安全的方式运行(可能使用原子操作),忘记#pragma omp critical
尝试在该算法之外进行并行化。这就是整个程序吗?我想你是在写东西。也许为每个线程创建一个求和器,然后求和就结束了。@SteveCox:这个被并行化的for循环已经是最外层的循环了。@Jim:谢谢!您能否更具体地说明如何为每个线程创建一个求和器,然后求和到最后?请注意函数h
。仅出于我个人的好奇心,f
究竟用什么填充索引?
float arr[100];
for ( int i = 0; i < 100; ++i) arr[i]=0;
int i,j;
#pragma omp parallel for shared(arr) private(i,j) schedule(auto)
for ( i = 0; i < 100; ++i) {
float* pi = arr+i;
int index[10];
f(i, index);
for (j = 0; j < 10; ++j) {
float* pj = arr+index[j];
if (pj > pi){
h(pi,i);
#pragma omp critical (pj)
h(pj,j); //
}
}
}