我在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
),但是它们不可用,因为+=
不适用于原子变量。
在这种情况下,我们该怎么办? 我应该在这里退出使用多线程吗? 非常感谢。
答案 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];
}