OpenMP中的Reduction操作 #pragma omp xxx reduction()

参考

OpenMP: For & Reduction

Reduction

Reduction操作类似于我们将很多东西组合组合在一起,比如MapReduce中先Map操作将数据分散,再通过Reduce将相同键的值合并起来。而OpenMP中的Reduction操作就用在这方面。

简单的for循环

int sum = 0;
for (int i = 0; i <= 10; ++i)
{
	sum += i;
}
cout<<sum<<endl;

例如上述的一个简单的for循环,如果我们使用parallel for并行,因为sum是被所有的线程共享的值,所以很可能出现很多线程同时读取sum并将其加上一些值,这就带来数据冲突,产生错误。

#include <omp.h>
#include <iostream>
using namespace std;

int main()
{
    int sum = 0;
    omp_set_num_threads(5);
    #pragma omp parallel for
    for (int i = 0; i <= 10; ++i)
    {
        sum += i;
    }
    cout<<sum<<endl;
    return 0;
}

上述代码结果应为55,多次执行其结果并不一致。
这里我们就可以用到reduction

使用Reduction

会造成冲突的数据是sum,我们要对sum执行的操作是+,所以在使用Reduction时,我们声明类似为:reduction(+: sum)。代码如下:

#include <omp.h>
#include <iostream>
using namespace std;

int main()
{
    int sum = 0;
    omp_set_num_threads(5);
    #pragma omp parallel for reduction(+: sum)
    for (int i = 0; i <= 10; ++i)
    {
        sum += i;
    }
    cout<<sum<<endl;
    return 0;
}

这样,执行的结果就正确了。

原理

我们输出线程号看看都是哪个线程操作了哪个`i``。

omp_set_num_threads(5);
#pragma omp parallel for reduction(+: sum)
for (int i = 0; i <= 10; ++i)
{
	int id = omp_get_thread_num();
    sum += i;
    printf("thread %d is operating loop %d, sum is %d\n", id, i, sum);
}

结果为:

thread 0 is operating loop 0, sum is 0
thread 0 is operating loop 1, sum is 1
thread 0 is operating loop 2, sum is 3
thread 1 is operating loop 3, sum is 3
thread 1 is operating loop 4, sum is 7
thread 4 is operating loop 9, sum is 9
thread 4 is operating loop 10, sum is 19
thread 3 is operating loop 7, sum is 7
thread 3 is operating loop 8, sum is 15
thread 2 is operating loop 5, sum is 5
thread 2 is operating loop 6, sum is 11

可以看到,线程0 1 2操作了0 1 2,并且最后sum = 3。但是当线程1操作i=3后,sum为3,也就是没有把之前的0 1 2加进来。其他线程也是如此。也就是说,10次循环分给了5个线程,在每个线程中,都创建了一个私有的sum,把该线程操作的数累加,之后5个线程再汇集其私有的sum,得到最终的sum,这样每个线程的私有sum互不干涉,就防止数据冲突。

总结

sum += i, sum *= i, sum -= i等可以使用Reduction,这些都有一个共同点,就是把他们分给几个线程分别做线程私有变量上的操作,最后得到的线程结果再一起做一次操作,结果一致。但是类似于sum = i - sum就不可以,因为这相当于sum = i / 2sum的值完全由最后一个i决定,不符合上述的共同点,就算分给多线程,最终线程结果合并时,也不知道到底该用哪个线程的结果。

猜你喜欢

转载自blog.csdn.net/qq_43219379/article/details/123911644