首页 > 解决方案 > OpenMP并行的两种方法之间的区别

问题描述

我只想通过 OpenMP 的汇总来评估函数的集成,方法是使用数组来保存每个步骤中计算的每个值 > 取所有值的总和;并在没有数组的情况下取和。

代码是:

double f(double x)
{
    return sin(x)*sin(x)/(x*x+1);
}

方法一

    long i = 0;
    const long NUM_STEP = 100000;
    double sum[NUM_STEP];
    double from = 0.0, to = 1.0;
    double step = (to - from)/NUM_STEP;
    double result = 0;

    #pragma omp parallel for shared(sum) num_threads(4)
    for(i=0; i<NUM_STEP; i++)
        sum[i] = step*f(from+i*step);
    for(i=0; i<NUM_STEP; i++)
        result += sum[i];
    printf("%lf", result);

方法二

    long i = 0;
    const long NUM_STEP = 100000;
    double from = 0.0, to = 1.0;
    double step = (to - from)/NUM_STEP;
    double result = 0;

    #pragma omp parallel for shared(result) num_threads(4)
    for(i=0; i<NUM_STEP; i++)
        result += step*f(from+i*step);
    printf("%lf", result);

但结果相差太大。方法 1 给出了一个稳定的值,但方法 2 给出了一个可变值。这是一个例子:

方法一:0.178446

方法二:0.158738

METHOD 1 的值为真(由另一个工具检查)。

标签: cmultithreadingparallel-processingopenmprace-condition

解决方案


TL;DR第一种方法没有竞争条件,而第二种方法有。

第一种方法没有竞争条件,而第二种方法有。即,在第一种方法中:

#pragma omp parallel for shared(sum) num_threads(4)
for(i=0; i<NUM_STEP; i++)
    sum[i] = step*f(from+i*step);
for(i=0; i<NUM_STEP; i++)
    result += sum[i];

每个线程将操作的结果保存在step*f(from+i*step);数组的不同位置sum[i]。之后,线程依次减少数组中保存的值sum,即:

for(i=0; i<NUM_STEP; i++)
    result += sum[i];

实际上,您可以在此版本上进行一些改进;sum而不是分配与数量相同大小的数组NUM_STEP,您可以分配与线程数相同大小的数组,并且每个线程将保存在等于其的位置ID,即:

int total_threads = 4;
double sum[total_threads];
#pragma omp parallel num_threads(total_threads)
{
  int thread_id = omp_get_thread_num();
  for(i=0; i<NUM_STEP; i++)
      sum[thread_id] += step*f(from+i*step);
  for(i=0; i< total_threads; i++)
      result += sum[i];
}

尽管如此,最好的方法是实际修复第二种方法。

在第二种方法中,变量更新存在竞争条件result

#pragma omp parallel for shared(result) num_threads(4)
for(i=0; i<NUM_STEP; i++)
    result += step*f(from+i*step);

因为该result变量正在由多个线程以非线程安全的方式同时更新。

要解决这种竞争条件,您需要使用reduce子句:

#pragma omp parallel for reduction(+:result) num_threads(4)
for(i=0; i<NUM_STEP; i++)
    result += step*f(from+i*step);

推荐阅读