并行计算工具OpenMP 二

如果你想要变量是每个线程拥有自己的单独的变量,那么就在并行块里面定义变量,对于在进入并行块前已经定义了的变量,是在多个线程之间共享的,需要小心处理,否则程序不会按照我们想要的方式执行。

并行块前定义变量

在上面这个程序中,试图用sum来累加变量和。#pragma omp parallel for告诉编译器要把这个for语句拆开来并行执行。注意 sum 定义在并行块外面,所以在并行块中会共享这个变量。也就是说在四个核中用的是同一个变量,对于sum + = i 语句,是先把sum的值取出来和 i 相加后再把结果赋给sum变量。既然四个线程用的是同一个sum,那么如果sum在线程0中取出来了,另一个线程中也同时取出来了同样值的sum,加完后值在两个线程中都会被送就回到sum,那么就会造成有一次操作被重写了(数据碰撞),也就是说最后的结果很可能小于正确的结果。(实际上我在visual studio2019上运行上面的代码并没有出错,但是不排除是因为vs进行了更正操作)结果如下:

并行结果

为了避免上面中出现的情况,可以进行一些其他操作:比如可以在 sum += i; 前面加上#pragma omp critical 语句,这条语句是告诉编译器下面的代码块很关键,任何时刻都只能有一个线程执行该代码块。不过这样的话代码会在多个核中的运行会相互影响,对性能有一定影响,最好的解决方法是用规约reduction,代码如下图:

规约reduction代码

reduction (+:sum) 告诉编译器,这并行块要用规约,+表示求和操作,sum是目标变量名。这样的话,编译器就会给每个线程一个sum的拷贝并正确初始化为0,然后每个线程执行完之后再合并。规约只适用于固定的操作符。

规约后代码的结果

OpenMP支持的规约操作符如下图:

规约操作符

如果我们不想要已经定义了的变量在多个线程中共享,想要让每个线程有自己的拷贝怎么办呢?下面就简单介绍一个private从句,以及firstprivate和lastprivate。通过使用#pragma omp parallel private(variable list),告诉编译器在下面的并行块中每个线程都对variable list列出的变量进行拷贝。不过值得注意的时,每个线程中拷贝的变量的初始值是不确定的,执行完并行块后该变量的值也不确定。所以就有了firstprivate和lastprivate,firstprivate 是说变量的初始值就是按照该变量进入并行块之前的值初始化;lastprivate 是说退出并行块后,按照串行情况最后的一个值给到主线程中的变量。如果同时使用firstprivate和lastprivate,那么该变量就会有和串行是一样的初始值和结束值。

你可能感兴趣的:(并行计算工具OpenMP 二)