赞
踩
OpenMp
用于共享内存并行系统的多线程程序设计的一套编译指令
共享内存:统一内存访问(多个CPU用同一个内存),非统一内存访问(有多个内存空间
Fork和Join:开始时是主线程,遇到Fork时,创建或唤醒多个子线程进入并行程序,并行结束后,通过Join汇合到主线程中
编译制导命令:所有的编制指令格式为
#pragma omp directive-name [clause[ [,] clause] ... ] new-line
//directive-name是特定的指令名,指明需要OpenMP完成的动作
//clause是从句,是可选项,用于作为对指定指令的进一步说明
命令1 parallel指令:
#pragma omp parallel构造一个并行块,在它之下的命令是并行运行的,若它构造的并行块多于一条指令需用一对大括号括起来
#pragma omp for
必须与parallel同时使用
#pragma omp parallel for或者
#pragma omp parallel{
#pragma omp for
}
命令3 section和sections指令:
用于使各个线程执行不同的工作,每个section必须是一个结构化的代码块,每个section仅被一个线程执行一次。
用法:#pragma omp parallel sections [clause[[,] clause]…]
{
[#pragma omp section]
structured block
[#pragma omp section]
structured block
……
}
命令4 single指令:
在并行块中用于让紧随其后的语句串行执行
#pragma omp single
从句:对并行区域的变量进行限定
(1)private 数据属性从句:
#pragma omp parallel for private(j)
将一个或多个变量声明为线程的私有变量。
每个线程都有它自己的变量私有副本,其他线程无法访问。
即使在并行区域外有同名的共享变量,共享变量在并行区域内不起任何作用,并且并行区域内不会操作到外面的共享变量。
并行区域内的private变量和并行区域外同名的变量没有存储关联,存储空间不重叠。
如果需要继承原有共享变量的值,则应使用firstprivate子句。
如果需要在退出并行区域时将私有变量最后的值赋值给对应的共享变量,则可使用lastprivate子句。
(2)share数据属性:所有线程是共享变量,当多个线程同时对它操作存在数据竞争会造成错误,数据不加private限定时默认是共享变量。
//默认是shared
#include <stdio.h>
int main(){
int i, j;
#pragma omp parallel for
for (i = 0; i < 2; i++){
for (j = 0; j < 5; j++) //j共享,线程1只执行了1次
printf("i=%d, j=%d from id=%d.\n", i, j, omp_get_thread_num());
}
return 0;
}
输出:
i=0, j=0 from id=0.
i=0, j=1 from id=0.
i=0, j=2 from id=0.
i=0, j=3 from id=0.
i=0, j=4 from id=0.
i=1, j=0 from id=1.
j是共享的,当0号线程里的j变化了的话,1号线程里的J也会变影响程序
改进:
#pragma omp parallel for private(j)
for (i = 0; i < 2; i++){
for (j = 0; j < 5; j++)
printf("i=%d, j=%d from id=%d.\n", i, j, omp_get_thread_num());
}
j是每个线程私有的,0号线程的j变化不会影响1号里的值
(3)reduction子句
为每个线程创建一个reduction变量的私有拷贝,在OpenMP区域结束时
(parallel结束时)把每个线程私有拷贝的值进行某种指定操作迭代运算,并
赋值给原来的变量???(原来的变量应该是拷贝的副本的原来吧)
语法: reduction(operator:list)
operator:+ * - & ^ | && || max min
运行:
(1)设置线程数量。
在OpenMP中,有三种方法可以设定线程个数,按照优先级从高到低分别是:
编译制导的num_threads()子句。如:#pragma omp parallel num_threads(8)
在并行区域外,使用运行库例程omp_set_num_threads()设定并行区域中使用。如: omp_set_num_threads(10);
环境变量OMP_NUM_THREADS。如: export OMP_NUM_THREADS = 3
(2)获取正在使用的线程数量和编号。
int omp_get_num_threads(void):返回当前并行区域中的线程数量。
int omp_get_thread_num(void):返回值当前并行区域中,当前线程在线程组中的编号。这个编号从0开始。
(3)获取程序可使用的CPU核心数。
int omp_get_num_procs(void):返回值为当前程序可以使用的CPU核数。
(4)获取墙上时间。
double omp_get_wtime(void):返回值是以秒为单位的墙上时间。在并行区域开始前和结束后分别调用该函数,并求取两次返回值的差,便可计算出并行执行的时间。
MPI
MPI是消息传递接口
点到点通信,和集群通信;
组成:头文件,主函数带两个参数,第一行初始化init,计算和通信操作,结束MPI Finalize进行清理
通信域:基本单元是进程 process若干个进程包含在一起的封装
MPI_COMM_WORLD默认缺省的通信域,预定义好的,程序启动时,所有进程在这里面
基本函数:
Get the rank of the calling process in group
int MPI_Comm_rank(MPI_Comm comm, int *rank);
得到进程编号,第一个参数通信域编号
Get the size in a communicator
int MPI_Comm_size(MPI_Comm comm, int *size);得到当前执行的程序进程号,
和获得进程个数
提前将定义一个MPI_Comm comm变量,代替MPI_COMM_WORLD
MPI编译:mpicc basic.c -o basic
执行:mpirun -np 3 ./basic (3表示启动3个进程)
点到点通信:把一个消息从一个函数发送到另一个函数from source to dest
两个进程必须在同一个通信域里,通过编号rank值进行识别。
发送函数:MPI_Send(void *data, int count, MPI_Datatype datatype, int des,comm )发送数据的指针,数据个数,数据类型,进程编号,标签
接收函数:
MPI_Recv(
void* data,
int count,
MPI_Datatype datatype,
int source,
int tag,
MPI_Comm comm,
MPI_Status* status)
MPI_Recv()发送与接收标签一样才能被正常接收,多了一个参数
MPI_Status* status用来获得消息数据,结构体存储是否成功,多长等等
MPI datatype | C equivalent |
MPI_SHORT | short int |
MPI_INT | int |
MPI_LONG | long int |
MPI_LONG_LONG | long long int |
MPI_UNSIGNED_CHAR | unsigned char |
MPI_UNSIGNED_SHORT | unsigned short int |
MPI_UNSIGNED | unsigned int |
MPI_UNSIGNED_LONG | unsigned long int |
MPI_UNSIGNED_LONG_LONG | unsigned long long int |
MPI_FLOAT | float |
MPI_DOUBLE | double |
MPI_LONG_DOUBLE | long double |
MPI_BYTE | char |
集群通信 :集群通信函数,一个进程的消息发送给所有进程,一个通信域里的所有进程都参与运算。集群通信函数一定要确保所有进程都执行
广播(Broadcast):一对多,让其他进程也有跟进程里的消息
int MPI_Bcast (void * buffer, int count, MPI_Datatype datatype, int root, comm)
要广播消息的起始地址,广播消息的数量每个进程的数量,数据类型,谁来广播,广播的通信域
收集(Gather):多对一,其他进程里的消息收集到一个进程里
int MPI_Gather( const void* sendbuf,int sendcount,
MPI_Datatype sendtype,
void* recvbuf,
int recvcount,
MPI_Datatype recvtype,
int root,
MPI_Comm comm)
确保所有进程发送的数据类型和个数一致,recvcount指每个进程发送数据的数目,收集后在根进程里的顺序按照进程号排。
广播2.0(Scatter): 类似Broadcast,区别是Broadcast是把根进程里同样的一个值复制四份放到四个进程里,而Scatter是把根进程里四个不同数据放到四个不同进程里》
int MPI_Scatter (
void * sendbuf , // pointer to send buffer
int sendcount , // items to send per process每个进程
MPI_Datatype sendtype , // type of send buffer data
void * recvbuf , // pointer to receive buffer
int recvcount , // number of items to receive
MPI_Datatype recvtype , // type of receive buffer data
int root , // rank of sending process
MPI_Comm comm ) // MPI communicator to use
Reduce(类比OpenMP里的Reduction)即在集合过程中不仅进行消息传递同时进行运算。
MPI_Reduce(
void* send_data, // address of send buffer
void* recv_data, // address of receive buffer
int count, // number of elements in send buffer
MPI_Datatype datatype,// data type
MPI_Op op, // reduce operation运算操作
int root, // rank of root process要存入的进程号
MPI_Comm communicator)// communicator
OpenMP和 MPI混合
OpenMP 是在线程上进行并行
MPI是在进程上进行并行
实际应用时是使用计算机集群,有很多计算,有很多CPU的核,在不同计算节点上使用MPI,在同一个计算节点上使用CPU的核要用MPI
np当前进程上线程的个数
iam 当前进程上线程的编号
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。