openMP 函数总结(并行程序设计导论)

2017-11-24  本文已影响0人  Gavinjou大笨象

本篇文章只是记录api的用法和回顾,方便记忆

openMP

openMP提供“基于指令”的共享内存API。这就意味着在c和c++中,有一些特殊的预处理指令pragma。在系统中加入预处理指令一般时用来允许不是基本C语言的规范的行为。
不支持pragma的编译器会忽略pragma指令提示的那些语句,这样就允许使用pragma的程序在不支持它的平台上运行

简单例子
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

void Hello(void);
int main(int argc,char* argv[])
{       
         /*
             long strtol( 
                        const char* number_p *in*, 第一个参数是字符串
                        const char** end_p  *out*,终止的非法字符串 
                        int     base  *in* 进制(2-36)
                        )
            
            例:
            char buffer[20]="10379cend$3";
            char *stop;
            printf("%d\n",strtol(buffer, &stop, 2));
            printf("%s\n", stop);
            输出结果:
            2
            379cend$3
        */
        int thread_count = strtol(argv[1],NULL,10);
#pragma omp parallel num_threads(thread_count)
        Hello();
        
        return 0;
}

void Hello(void)
{
    int my_rank = omp_get_thread_num();
    int thread_count = omp_get_num_threads();

    printf("hello from thread %d of %d \n",my_rank,thread_count);
}


#编译
gcc -g -Wall -fopenmp -o main main.c

#-g :产生供gdb调试用的可执行文件
# http://www.jianshu.com/p/30ffc01380a0
#-Wall:编译后显示所有警告
#-fopenmp 使用mpi支持
#-o:输出到指定文件


#pragma omp pallel
- num_threads 子句
  1. 允许程序员指定执行后代码块的线程数
  2. 程序可以启动的线程数可能会受系统定义的限制。OpenMP标准并不保证实际能够启动thread_count个线程
#pragma omp parallel num_threads(thread_count)
-func omp_get_thread_num | omp_get_num_threads
#获得当前线程的编号
int omp_get_thread_num(void)
#获得线程数量
int omp_get_num_threads(void)
错误检查

可以通过预处理宏_OPENMP是否定义。

#ifdef _OPENMP
#include<omp.h>
#endif

#ifdef _OPENMP
    int my_rank=omp_get_thread_num();
    int thread_count=omp_get_num_threads();
#else
    int my_rank=0;
    int thread_count=1;
#endif
#pragma omp critical
语法
#pragma omp critical [(name)]
用法
#pragma omp critical
global_result += my_result;
变量的作用域
- reduction 规约子句
语法
reduction(<operator>:<variable list>)
# operator : +,*,-,&,|,^,&&,||
用法
global_result=0.0;
#pragma omp parallel num_threads(thread_count) reduction(+:global_result)
global_result += Local_trap(double a,double b,int n);

####等同

global_result=0.0;
#pragma omp parallel num_threads(thread_count) 
{
    double my_result =0.0;/*私有变量*/
    my_result += Local_trap(double a,double b,int n);
#pragma omp critial
    global_result += Local_trap(double a,double b,int n);
}

#pragma omp parallel for
合法方式
h=(b-a)/n;
approx =(f(a)+f(b))/2.0;
# pragma omp parallel for num_threads(thread_count) reduction(+:=approx)
approx += f(a+i*h);
approx = h* approx;
线程重用
#pragma omp parallel num_threads(thread_count) default(none) \ 
  shared(a,n) private(i,tmp,phase)
for(phase = 0;phase<n;phase++)
{
    if(phase%2 == 0)
      #pargma omp for
      for(i=1;i<n;i++)
        ...
    else
      #pargma omp for
      for(i=1;i<n;i++)
        ...
}  
数据依赖性
数据依赖
#y依赖于x
for(i=0;i<n;i++)
{
    x[i]=a+i*h;
    y[i]=exp(x[i]);
}
循环依赖

一个值在循环中计算,其结果在之后迭代中使用。

#并行化后某一个边界值将是另一个并行化线程中的使用。
fibo[0]=fibo[1]=1;
for(i=2;i<n;i++)
    fibo[i]=fibo[i-1]+fibo[i-2];
- private 子句
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

void Hello(void);
int main(int argc,char* argv[])
{

    int x=5;

    #pragma omp parallel private(x)
    {
        int my_rank =omp_get_thread_num();
        printf("Thread %d > before initialization,x=%d \n",my_rank,x);
        x=2*my_rank+2;
        printf("Thread %d > after initialization,x=%d \n",my_rank,x);
    }

    printf("after parallel,x=%d \n",x);
        return 0;
}
- default(none) 子句
double sum = 0.0;
/*
sum是一个规约变量(同时拥有私有和共享作用域的属性)。
*/
#pragma omp parallel for num_threads(thread_count) \
  default(none) redcution(+:sum) private(k,factor) \
  shared(n)
  for(k=0;k<n;k++)
    if(k%2 ==0)
        factor = 1.0;
    else
        factor = -1.0;
    sum += factor/(2*K+1);
- schedule子句

对线程进行调度。

语法
schedule(<type> [,<chunsize>]

type可以时一下的任意一个。

(static,1)
Thread0:0,3,6,9
Thread1:1,4,7,10
Thread2:2,5,8,11

(static,2)
Thread0:0,1,6,7
Thread1:2,3,8,9
Thread2:4,5,10,11

缺省调度(static,total_iterations/thread_count)
dynamic调度中,迭代也被分成chunksize个连续迭代的块。
每个线程执行一块,并且当一个线程完成一块时,
他将从运行时系统请求另一块,直到所有的迭代完成。
chunksize可以被忽略。当它被忽略时,chunksize为1。
在guided调度中,每个线程也执行一块,并且当一个线程完成一块,将请求另一块。
然而,在guided调度中,当块完成后,新块的大小变小。
例如:
n=10 000并且thread_count=2时,迭代将如表那样分配。块的大小近似等于的迭代数除以线程数。第一块的大小为9999/2 ~=5000,因为9999个未被分配的迭代。第二块的大小为4999/2~=2500。以此类推。

| 线程 |    块    | 快的大小 | 剩下的迭代代数 |
| 0   | 1~5000   | 5000 | 4999 |
| 1   | 5001-7500| 2500 | 2499 |
| 1   | 7501-8750| 1250 | 1249 |
...
设置环境变量
$export OMP_SCHEDULE="static,1"
#pragma omp barrier
#pragma omp atomic

语句必须是以下形式:

#op:+,*,-,/,&,^,|,<<,or >> .
#expreesion不能引用x。
x<op>=<expreesion>;
x++;
++x;
x--;
y--;

用法
#其他线程对x的更新必须等到该线程对x的更新结束之后。
#但对y不受保护,因此程序的结果是不可预测的。
#pragma omp atomic
  x+=y++
简单锁
void omp_init_lock(omp_lock_t*  lock_p  /*out*/);
void omp_set_lock(omp_lock_t*  lock_p  /*in/out*/);
void omp_unset_lock(omp_lock_t*  lock_p  /*in/out*/);
void omp_destroy_lock(omp_lock_t*  lock_p  /*in/out*/);
用法
static omp_lock_t lock;   
void test11()  
{  
    omp_init_lock(&lock); // 初始化互斥锁    
  
#pragma omp parallel for    
    for (int i = 0; i < 5; ++i)     
    {    
        omp_set_lock(&lock); //获得互斥器     
        std::cout << omp_get_thread_num() << "+" << std::endl;    
        std::cout << omp_get_thread_num() << "-" << std::endl;    
        omp_unset_lock(&lock); //释放互斥器    
    }    
    omp_destroy_lock(&lock); //销毁互斥器    
}  
#pragma omp single

这样做能确保接下来的结构化代码块由线程组中的一个线程执行,而组内其他线程等待直到该线程执行结束(在代码块的最后设置一个隐式路障)

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int main()
{

#pragma omp parallel
{
int my_rank = omp_get_thread_num();
if(my_rank == 1)
{
   int x=1;
   while(x<1e9)
    {
        x+=1;
    }
}
#pragma omp single
    printf("%d \n",my_rank);
printf("----> %d \n",my_rank);

}
    return 0;
}

#pragma omp master

这样能确保线程0执行接下来的结构化代码块。然后master指令在最后不会设置隐式路障

-func omp_get_wtime

获取运行时间。

上一篇 下一篇

猜你喜欢

热点阅读