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); // 
        }
      }
    }