并行计算

2022-09-04  本文已影响0人  川人1588

并行计算机

并行计算机分类

数据与指令

图1:按指令(程序)数据的个数分类

SIMD

单条指令并行计算多条数据,如 A=A+1(备注:其中A为数组)

MISD

多条指令并行计算多条数据,如 A=(B+C)+(D-E)+(FG)(备注: B+C 、D-E、FG并行执行*)

存储方式

图2:按存储方式分类

问题并行求解

图3:问题求解过程

并行编程模型与并行语言

并行编程模型

表1:数据并行与消息传递并行模型对比

数据并行

相同的操作同时作用于不同的数据,适合SIMD、SPMD并行计算机上运行。在向量机上通过数据并行求解问题的实践也说明数据并行是可以高效地解决一大类科学与工程计算问题的。
它给编程者提供一个统一的存储地址空间,由编程语言本身提供并执行语义,如tensorflow的向量计算等就是此类并行计算。

消息传递并行

各个并行执行的部分之间通过传递消息来交换信息 协调步伐 控制执行。一般面向分布式内存,用数据并行很难实现的运算。由开发者就行计算部件之间的信息交换,控制,协调。

并行语言

并行语言实现方式

并行算法

并行算法的分类

根据运算的基本对象分类

根据进程之间的依赖关系分类

根据并行计算任务的粒度分类

并行算法的设计

MPI简介

什么是MPI

MPI的目的

目前MPI的主要实现

第一个MPI程序

#include "mpi.h"
#include <stdio.h>
void main(int argc,char* argv[])
{
    int myid, numprocs;
    int namelen;
    char processor_name[MPI_MAX_PROCESSOR_NAME];
    MPI_Init(&argc,&argv);                                              //初始化MPI框架
    MPI_Comm_rank(MPI_COMM_WORLD,&myid);    //获取本进行的id
    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);    //获取总的进程数量
    MPI_Get_processor_name(processor_name,&namelen);    //获取节点名称
    fprintf(stderr,"Hello World! Process %d of %d on %s\n", myid, numprocs, processor_name);
    MPI_Finalize();
}

编译

gcc -o mpipro mpipro.c -std=c99 -I /usr/local/openmpi4/include/ -L/usr/local/openmpi4/lib  -L/usr/lib64/openmpi3/ -lopen-rte -lopen-pal  -lmpi

运行

如果root用户执行,需要加--allow-run-as-root选项。
mpirun -n 2 mpipro
执行结果

6个接口构成的MPI子集

子集的介绍

/**1
* 描述:完成MPI框架的初始化工作
*/
MPI_Init(int argc, char* argv[]) 

/**2
* 描述:完成MPI框架的结束工作
*/
MPI_Finalize(void)

/**3
* 描述:获取当前进程的标识
* @comm:IN, 该进程所在的通信域(句柄)
* @rank:OUT, 调用进程在comm中的标识号
* @return: 
*/
int MPI_Comm_rank(MPI_Comm comm, int *rank)

/**4
* 描述:获取当前进程的标识
* @comm:IN, 该进程所在的通信域(句柄)
* @size:OUT, 通信域comm内包括的进程数
* @return: 
*/
int MPI_Comm_size(MPI_Comm comm, int *size)

/**5
* 描述:将发送缓冲区中的count个datatype数据类型的数据发送到目的进程,目的进程在通信域中的标识号
是dest,本次发送的消息标志是tag,使用这一标志就可以把本次发送的消息和本进程向同一目的进程发送的
其它消息区别开来
* @buf:IN, 发送缓冲区的起始地址
* @count:IN, 将发送的数据的个数
* @datatype: IN, 发送数据的数据类型(句柄)
* @dest: IN, 目的进程标识号(整型)
* @tag: IN, 消息标志(整数)
* @comm IN, 通信域(句柄)
*/
int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

/**6
* 描述:从指定的进程source接收消息,并且该消息的数据类型和消息标识和本接收进程指定的datatype和
tag相一致,接收到的消息所包含的数据元素的个数最多不能超过count
* @buf:IN, 接收缓冲区的起始地址
* @count:IN, 将发送的数据的个数
* @datatype: IN, 发送数据的数据类型(句柄)
* @source: IN, 目的进程标识号(整型)
* @tag: IN, 消息标志(整数)
* @comm IN, 通信域(句柄)
* @status OUT,返回状态,需要调用者分配空间,包含status.MPI_SOURCE、status.MPI_TAG 和status.MPI_ERROR三个域
*/
int MPI_Recv(void* buf, int count, 
                      MPI_Datatype datatype, int source, int tag, 
                      MPI_Comm comm, MPI_Status *status)

简单的消息收发的例子

#include <mpi.h>
#include <stdio.h>
#include <string.h>
void main(int argc,char* argv[])
{
    char *message = "Hello, MPI";
    char send_buf[20];
    char recv_buf[20];
    int myid, numprocs;
    int namelen;
    MPI_Status status;
    char processor_name[MPI_MAX_PROCESSOR_NAME];
    MPI_Init(&argc,&argv);
    MPI_Comm_rank(MPI_COMM_WORLD,&myid);
    if(myid == 0)
    {
        strcpy(send_buf, message);
        MPI_Send(message, strlen(message), MPI_CHAR, 1, 99, MPI_COMM_WORLD);
    }
    if (myid == 1)
    {
        MPI_Recv(recv_buf, strlen(message), MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status);
        printf("receive message:%s\n", recv_buf);
        printf("source:%d, tag:%d, error:%d\n", status.MPI_SOURCE, status.MPI_TAG, status.MPI_ERROR);
    }
    MPI_Finalize();
}

执行结果

执行结果

MPI预定义的数据类型

预定义数据类型与c语音的关系
附加的数据类型

MPI数据类型匹配和数据转换

数据类型匹配规则

MPI消息传递过程

需要数据类型匹配的步骤

匹配的意思

匹配规则

数据转换

数据类型的转换

改变一个值的数据类型。MPI严格类型匹配,不存在类型转换。

数据表示的转换

不同异构环境数据的表示不一致,MPI需要在异构系统之间进行数据表示转换。转换原则为:整形、逻辑、字符型保持值不变,浮点型转换为目标平台上能表示的最接近的值。
如果在发送的过程中,转换操作失败,会导致发送或接收,或者两者都会失败。

MPI消息

消息格式

image.png

tag的作用

进程0
MPI_SEND( x,1,整型,1,tag1,comm) 发送消息1
MPI_SEND( y,1,整型,1,tag2,comm) 发送消息2

进程1
MPI_RECV(x,1,整型,0,tag1,comm,status)
MPI_RECV(x,1,整型,0,tag2,comm,status)

任意源与任意标识

接收者可以指定以下两个标识,接收任意源或者任意tag的消息。发送者必须指定目标和tag。

MPI通信域

MPI通信域包含两部分

简单的MPI程序示例

用MPI实现计时功能

获取机器的名字和MPI版本号

int MPI_Get_processor_name ( char *name, int *resultlen)
int MPI_Get_version(int * version, int * subversion)

是否初始化及错误退出

int MPI_Initialized(int *flag) 判断MPI_Init()是否已经执行
int MPI_Abort(MPI_Comm comm, int errorcode) 使通信域comm中的所有进程退出

数据接力传送

任意进程间相互问候

任意源和任意标识的使用

#include "mpi.h"
#include <stdio.h>
int main(int argc, char* argv[])
{
    int rank, size, i, buf[1];
    MPI_Status status;
    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
    MPI_Comm_size( MPI_COMM_WORLD, &size );
    if (rank == 0) {
        for (i=0; i<100*(size-1); i++) {
            MPI_Recv( buf, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );
            printf( "Msg=%d from %d with tag %d\n", buf[0], status.MPI_SOURCE, status.MPI_TAG );
        }
    }
    else {
        for (i=0; i<100; i++){
            buf[0]=rank+i;
            MPI_Send( buf, 1, MPI_INT, 0, i, MPI_COMM_WORLD );
        }
            
    }
    printf("process %d exit\n", rank);
    MPI_Finalize();
}

编写安全的MPI程序

MPI并行程序的两种基本模式

对等模式

主从模式

上一篇 下一篇

猜你喜欢

热点阅读