在无法应用omp原子/还原的情况下,使用ompcritical可以提高效率吗?

时间:2019-01-08 13:59:24

标签: c++ openmp

我在c ++中遇到了一个问题。

假设我们有vector<SomeClass> v,并且想使用多线程添加v的所有元素。 (v的大小为N。) +=中有SomeClass个运算符的重载,但这不是原子兼容的。

SomeClass sum; // init by "zero" but not exactly a single (int)
#pragma omp parallel for
for (int i=0; i<N; ++i) {
   #pragma omp critical
   {
      sum += v[i];
   }
}

我的实验表明,此代码的运行时间类似于单线程的运行时间。 我认为这是由于critcial部分所致,该部分一次只允许一个线程评估加法。 (我无法利用多线程)。 可以选择使用atomic或(reduction),但是它们不可用,因为+=不适用于原子变量。

在这种情况下,我们该怎么办? 我应该在这里退出使用多线程吗? 非常感谢。

2 个答案:

答案 0 :(得分:2)

您需要减少。如果openMP的标准reduction(op:var)指令不支持该类,则可以手动使用它。

每个线程将向量值求和到一个专用累加器中。然后,将专用累加器汇总为一个全局累加器(对于整个循环,每个线程只需要执行一次,而不是在每次迭代时都这样做)

SomeClass sum; // init by "zero" but not exactly a single (int)
#pragma omp parallel
{
    SomeClass mySum=0; //local accumulator, init with 0 or whatever the "zero" is for that class
    #pragma omp for
    for (int i=0; i<N; ++i) mySum += v[i];

    #pragma omp critical
    sum += mySum; //The critical section is entered only once
}

答案 1 :(得分:2)

@Brice的答案是正确的,因为您想要减少,但对于+=是您想要的运算符的东西,您应该可以本地使用OpenMP减少。即使不是,规范也提供用户定义的归约,以允许对具有任意表达式的任意类型进行归约,例如:

// For a version that uses a `.combine()` method
// #pragma omp declare reduction(somered:SomeClass: omp_out.combine(omp_in))
#pragma omp declare reduction(somered:SomeClass: omp_out += omp_in)
// this assumes you can initialize with SomeClass tmp = 0;
// if not, add initializer() clause with a correct initialization for the tmp
SomeClass sum; // init by "zero" but not exactly a single (int)
#pragma omp parallel for reduction(somered: sum)
for (int i=0; i<N; ++i) {
      sum += v[i];
}