【OpenMP学习笔记】3:循环并行化和parallel for指令

从parallel到parallel for

parallel for指令和parallel指令是不同的。

parallel

parallel指令只是指明后面的代码块被并行执行,对共享下标的访问次序无法控制和预知:

#include
#include
#include

int main(int argc,char *argv[])
{
    int i;
    int thrdCnt=strtol(argv[1],NULL,10);
#   pragma omp parallel num_threads(thrdCnt)
    for(i=0;i<6;i++)
        printf("我是%d\n",omp_get_thread_num());
    return 0;
}

多次输出

[lzh@hostlzh OpenMP]$ ./test2.o 2
我是0
我是0
我是0
我是0
我是0
我是0
我是1
[lzh@hostlzh OpenMP]$ ./test2.o 2
我是0
我是0
我是0
我是0
我是0
我是1
我是0
[lzh@hostlzh OpenMP]$ ./test2.o 2
我是0
我是0
我是0
我是0
我是0
我是0
我是1
我是1
我是1
我是1
我是1
我是1
[lzh@hostlzh OpenMP]$

parallel for

而parallel for指令能将合法的典型结构的for循环并行化,且在parallel for中循环变量的缺省作用域是私有的,线程之间不会相互影响:

#include
#include
#include

int main(int argc,char *argv[])
{
    int i;
    int thrdCnt=strtol(argv[1],NULL,10);
#   pragma omp parallel for num_threads(thrdCnt)
    for(i=0;i<6;i++)
        printf("我是%d\n",omp_get_thread_num());
    return 0;
}

输出

[lzh@hostlzh OpenMP]$ ./test2.o 2
我是0
我是0
我是0
我是1
我是1
我是1
[lzh@hostlzh OpenMP]$

可见对for循环进行了块划分。

注意parallel for和parallel是完全不同的指令,parallel for指令后面直接跟随需要并行化的for,而不能像parallel那样修饰大括号扩起来的代码块。

能够被parallel for正确并行化的for循环

书上把这样的for循环称为典型的for循环。

for循环头的允许形式

fori=a;i<bi>bi<=bi>=b;i++++iiii+=ki=ki=i+ki=k+ii=ik f o r ( i = a ; i < b i > b i <= b i >= b ; i + + + + i i − − − − i i + = k i − = k i = i + k i = k + i i = i − k )

对for循环体的限制

循环次数必须由前面的for循环头本身确定,不能在循环体中做可能改变for循环次数判定的事情,如break和改变循环变量的值。

书上的例子

这个例子使用parallel for并行化求 π π 值的一个for循环。

用reduction归约子句保护了加入总和的过程,当然这里使用critical子句也可以,但保护的机制截然不同。

用private子句为符号变量sign设定了私有作用域,以保证每个线程对这个变量都有自己的副本,防止在使用前又被其它线程赋值改变。

用if-else判断解除了使用sign=-sign改变符号变量时具有的循环依赖问题,即本次循环中使用了前面循环计算得到的结果。循环依赖在循环并行化中往往是有害的,因为并行化的程序往往不按照串行的时间线执行,依赖了其它迭代次的数据是错误的,或者有时候所依赖的数据还没有计算出来(如斐波那契数列)。

而对于循环变量i,因为在parallel for指令中,循环变量的缺省作用域是私有的,所以它已经受到保护。

#include
#include
#include

int main(int argc,char *argv[])
{
    double sum=0.0;//归约变量,被reduction子句保护
    int i;//循环变量,被parallel for保护为私有
    double sign;//表示符号的变量,被private子句保护
    int thrdCnt=strtol(argv[1],NULL,10);
#   pragma omp parallel for \
    num_threads(thrdCnt) \
    reduction(+:sum) \
    private(sign)
    for(i=0;i<10000;i++)
    {
        //利用if-else判断对sign=-sign解除循环依赖
        if(i%2==0)
            sign=1.0;
        else
            sign=-1.0;
        //本质上是加到reduction子句为每个线程建立的私有变量上
        sum+=sign/(2*i+1);
    }
    printf("pi=%f\n",4*sum);//这个级数*4近似为pi
    return 0;
}

输出

[lzh@hostlzh OpenMP]$ !gcc
gcc -fopenmp -o test2.o test2.c
[lzh@hostlzh OpenMP]$ ./test2.o 5
pi=3.141493
[lzh@hostlzh OpenMP]$

私有作用域的变量在parallel和parallel for块刚开始时和结束后都是未指定的。

你可能感兴趣的:(OpenMP)