可怕的性能 - 一个简单的开销问题,还是存在程序缺陷?

5 parallel-processing overhead openmp race-condition

我在这里了解到的是一个相对简单的OpenMP构造.问题是,与2个线程相比,程序运行速度比1个线程快100-300倍.该计划的87%用于gomp_send_wait(),另外9.5%用于gomp_send_post.

该计划为正确的结果,但我不知道是否有在代码中的缺陷,是造成一些资源冲突,或者如果它仅仅是创建线程的开销大大不值得的块大小4的AA环路 p范围从17到1000,取决于我们正在模拟的分子的大小.

我的数字是最坏的情况,当p是17并且块大小为4.无论我使用静态,动态还是引导式调度,性能都是相同的.使用p=150和块大小75,程序仍然比串行慢75x-100x.

...
    double e_t_sum=0.0;
    double e_in_sum=0.0;

    int nthreads,tid;

    #pragma omp parallel for schedule(static, 4) reduction(+ : e_t_sum, e_in_sum) shared(ee_t) private(tid, i, d_x, d_y, d_z, rr,) firstprivate( V_in, t_x, t_y, t_z) lastprivate(nthreads)
    for (i = 0; i < p; i++){
        if (i != c){
            nthreads = omp_get_num_threads();               
            tid = omp_get_thread_num();

            d_x = V_in[i].x - t_x; 
            d_y = V_in[i].y - t_y;
            d_z = V_in[i].z - t_z;


            rr = d_x * d_x + d_y * d_y + d_z * d_z;

            if (i < c){

                ee_t[i][c] = energy(rr, V_in[i].q, V_in[c].q, V_in[i].s, V_in[c].s);
                e_t_sum += ee_t[i][c]; 
                e_in_sum += ee_in[i][c];    
            }
            else{

                ee_t[c][i] = energy(rr, V_in[i].q, V_in[c].q, V_in[i].s, V_in[c].s);
                e_t_sum += ee_t[c][i]; 
                e_in_sum += ee_in[c][i];    
            }

            // if(pid==0){printf("e_t_sum[%d]: %f\n", tid, e_t_sum[tid]);}

        }
    }//end parallel for 


        e_t += e_t_sum;
        e_t -= e_in_sum;            

...
Run Code Online (Sandbox Code Playgroud)

Aat*_*man 6

首先,我不认为在这种情况下优化您的序列代码将有助于回答您的OpenMP困境.别担心.

IMO对减速有三种可能的解释:

  1. 这个可以很容易地解释减速.数组ee_t的元素导致缓存行中的错误共享.虚假共享是当核心最终写入同一缓存行时,不是因为它们实际上正在共享数据,而是当核心正在写入时恰好位于同一缓存行中(这就是为什么它被称为虚假共享).如果你在谷歌上找不到虚假分享,我可以解释更多.使ee_t元素高速缓存行对齐可能会有很大帮助.

  2. 产卵工作的开销高于并行效益.你尝试过少于8个核心吗?2核的性能如何?

  3. 迭代总数很小,比如我们以17为例.如果将它拆分为8个核心,它将遇到负载不平衡问题(特别是因为你的一些迭代实际上没有做任何工作(当i == c时).至少有一个核心必须做3次迭代,而所有其他核心都要做2.这并不能解释为什么加速不如你预期的那么慢的原因.由于你的迭代长度各不相同,我会使用块大小为1的动态调度或使用openmp guide.试验块大小,块太小也会导致减速.

让我知道事情的后续.


Met*_*tiu 1

我相信您应该尝试移出循环内的所有这些分支(即 if),并在两个单独的循环中执行此操作,一个用于 i < c,一个用于 i > c。即使单线程代码这也会大大受益,但它应该为您提供更多并行性,即使正如您所说,线程创建开销可能大于小 n 的好处。