为什么OpenMP并行for循环不能正常工作?

时间:2017-12-29 19:38:55

标签: c for-loop parallel-processing openmp

我想实现OpenMP来并行化我的代码。我从一个非常基本的例子开始,了解它是如何工作的,但我错过了一些东西......

所以,我的例子看起来像这样,没有并行化:

int main() {
  ...

  for (i = 0; i < n-1; i++) {
    u[i+1] = (1+h)*u[i]; // Euler
    v[i+1] = v[i]/(1-h); // implicit Euler
  }

  ...

  return 0;
}

我省略了“......”中的某些部分,因为它们不相关。它有效,如果我在文件上打印u[]v[]数组,我会得到预期的结果。

现在,如果我尝试通过添加以下内容来并行化它:

#include <omp.h>

int main() {
  ...

  omp_set_num_threads(2);

  #pragma omp parallel for
  for (i = 0; i < n-1; i++) {
    u[i+1] = (1+h)*u[i]; // Euler
    v[i+1] = v[i]/(1-h); // implicit Euler
  }

  ...

  return 0;
}

代码编译并运行程序,但 u[] v[] 数组中有一半是零。

如果我设置 omp_set_num_threads( 4 ) ,我会得到四分之三的零。 如果我设置 omp_set_num_threads( 1 ) ,我会得到预期的结果。

所以它看起来只有第一个线程正在执行,而不是其他线程......

我做错了什么?

4 个答案:

答案 0 :(得分:1)

OpenMP假定循环的每次迭代都独立于其他循环。当你这样写:

i

循环的迭代i+1正在修改迭代i+1。同时,迭代t+1可能同时发生。

除非你能使迭代独立,否则这不是并行的好用例。

而且,如果你考虑一下Euler的方法所做的事情,那么显然不可能以这种方式并行处理你正在处理的代码。欧拉的方法基于时间t处的信息在时间t+1计算系统的状态。由于您在不知道首先了解t的情况下无法知道Intent intent = new Intent(Activity1.this, Activity2.class); intent.putExtra("data1" , "data_value_1"); intent.putExtra("data2" , "data_value_2"); startActivity(intnent); 处的内容,因此无法在欧拉方法的迭代中进行并行化。

答案 1 :(得分:1)

欢迎来到并行(或&#34;只是&#34; - 并发)多个计算现实。

为什么?

处理循环的任何非顺序计划都会出现隐藏(未正确处理)数据泄露的问题 - { - access | -值}  及时完整。

纯粹的[SERIAL]处理流程没有这种危险,因为主要序列化的步骤间接地引入(正确的执行任务的严格顺序除外)一步一个接一个地作为序列)订单,其中没有机会&#34;触摸&#34;相同的内存位置同时两次或多次。

这&#34;安心&#34;一旦进程进入"just"-[CONCURRENT]true-[PARALLEL]处理,就会无意中丢失。

突然间有一个几乎随机的顺序(在&#34;只是&#34; - [CONCURRENT])或主要&#34;立即&#34;奇点(避免任何原始含义&#34;顺序&#34; - 在真正的[PARALLEL]代码执行模式的情况下 - 像机器人一样,具有6DoF,到达每个轨迹点真 - [PARALLEL]时尚,以纯粹的[SERIAL]方式并行驱动所有6个DoF轴,而不是一个接一个地驱动所有6个轴,而不是现在的某个 - 其他 - 以后 - 和 - 因为机器人手臂的三维轨迹将变得难以预测,并且经常在汽车装配上发生相互碰撞,所以它只能以一种方式进入...... [CONCURRENT]方式线......)。

解决方案:

使用防御工具,称为原子操作,或主要方法 - 设计(b)无锁算法,尽可能,或明确表示信号和坐标读取和写入(确保以超时和降低性能为代价),以保证如果保护步骤(确保所有&#34;旧的&#34; -writes安全地通过&#34;通过&#34;在任何&#34; next&#34; -reads继续抓住&#34;右&#34; -value)之前没有被编码(如如上所述。

尾声:

使用像OpenMP这样的工具来解决问题,它不会带来任何好处,这将导致花费时间和降低性能(因为需要处理所有与工具相关的开销,而并行性的净效果几乎为零)在这种情况下,算法不允许任何并行性的享受),所以最终会有一种方式比最终得到的更多。

了解OpenMP最佳实践的一个好点可能来自劳伦斯利弗莫尔国家实验室(确实非常称职)和类似的publications on using OpenMP.

答案 2 :(得分:1)

在并行化代码之前,您必须确定其并发性,即同时发生逻辑的任务集,然后找出一种方法来实现它们< em>实际上并行发生。

如上所述,由于其性质上没有并发性,因此这不是一个应用并行性的好例子。由于所谓的竞争条件,试图使用这样的并行性将导致错误的结果。

如果您只是想了解OpenMP的工作原理,请尝试提供一些示例,您可以清楚地识别出独立于概念的任务。我能想到的最简单的一个就是通过积分来计算曲线下面积。

答案 3 :(得分:1)

u[i+1] = (1+h)*u[i];                                                                                                                                              
v[i+1] = v[i]/(1-h);                                                                                                                                       

相当于

u[i] = pow((1+h), i)*u[0];
v[i] = v[0]*pow(1.0/(1-h), i);

因此您可以像这样将代码并行化

#pragma omp parallel for
for (int i = 0; i < n; i++) {
    u[i] = pow((1+h), i)*u[0];
    v[i] = v[0]*pow(1.0/(1-h), i);
}

如果你想减轻pow函数的成本,你可以每个线程执行一次,而不是像他的那样(从t << n开始)每次迭代执行一次。

#pragma omp parallel
{
    int nt = omp_get_num_threads();
    int t  = omp_get_thread_num();
    int s  = (t+0)*n/nt;
    int f  = (t+1)*n/nt;
    u[s]   = pow((1+h), s)*u[0];
    v[s]   = v[0]*pow(1.0/(1-h), s);
    for(int i=s; i<f-1; i++) {
        u[i+1] = (1+h)*u[i];
        v[i+1] = v[i]/(1-h);
    }
}

您还可以编写自己的pow(double, int)函数,针对整数幂进行优化。

请注意,我使用的关系实际上并非100%等效,因为浮点运算不是关联的。这通常不是问题,但是应该注意的事情。