分布式学习 - MPICH编译与实践

14 篇文章 2 订阅
12 篇文章 0 订阅

背景

MPICH是一个开源的、便携式和可扩展的消息传递接口库,专门用于并行计算。它提供了一套标准的、可移植的消息传递接口,使得开发者可以在多处理器计算机上编写并行程序,实现不同节点间的通信和协调。MPICH旨在促进并行应用程序的开发和移植,为科学计算、工程模拟以及其他需要高性能计算的应用领域提供了强大支持。

MPICH旨在为高性能计算机提供通用的消息传递接口。它支持多种操作系统和硬件架构,并为不同的并行计算环境提供了高度灵活的通信库。MPICH的出现极大地促进了并行计算的发展,使得研究人员和开发者能够更加方便地构建和优化复杂的并行应用程序,提高了计算效率和性能。

整体架构

主要分为三层:

  1. MPI:符合MPI规范的接口层,除了基本的六个函数接口以外,还包括数据定义,和组管理
  2. ADI:抽象设备接口,ch指chamelon,代表着
    在这里插入图片描述

Communicators And Context IDs

在MPICH中,通信子(Communicators)和上下文ID(Context IDs)是两个关键概念,用于管理并行计算中的通信操作。

通信子(Communicators):通信子是一个抽象概念,它定义了一个进程组以及这个进程组内通信的上下文。通过创建通信子,可以将进程划分为不同的组,这些组可以有不同的通信规则和交互方式。通信子的存在允许不同组之间进行独立的通信操作,而不影响其他组的通信。通信子的创建和管理使得在并行计算中可以实现更灵活的通信模式和更高效的数据交换。

上下文ID(Context IDs):上下文ID是MPI中用于区分不同通信环境的标识符。它用于管理不同通信操作的上下文,确保并行计算中的通信操作可以独立进行,互不干扰。通过上下文ID,MPICH可以准确地识别不同的通信环境,使得在不同的通信操作之间切换时更加灵活高效。

通信原语

MPI的6个基本函数

1.MPI_Init

任何MPI程序都应该首先调用该函数,在MPI程序开始时调用即可

 MPI_Init(&argc,&argv); 

2.MPI_Finalize

任何MPI程序结束时,都需要调用该函数。切记Fortran在调用MPI_Finalize的时候,需要加个参数ierr来接收返回的值,否则计算结果可能会出问题甚至编译报错。在Fortran中ierr为integer型变量。 该函数同第一个函数,都不必深究,只需要求格式去写即可。

MPI_Finalize() //C++ 

3.MPI_COMM_RANK

int MPI_Comm_Rank(MPI_Comm comm, int *rank) 

该函数是获得当前进程的进程标识,如进程0在执行该函数时,可以获得返回值0。可以看出该函数接口有两个参数,前者为进程所在的通信域,后者为返回的进程号。通信域可以理解为给进程分组,比如有0-5这六个进程。可以通过定义通信域,来将比如[0,1,5]这三个进程分为一组,这样就可以针对该组进行“组”操作,比如规约之类的操作。这类概念会在之后的MPI进阶一章中讲解。MPI_COMM_WORLD是MPI已经预定义好的通信域,是一个包含所有进程的通信域,目前只需要用该通信域即可。

在调用该函数时,需要先定义一个整型变量如myid,不需要赋值。将该变量传入函数中,会将该进程号存入myid变量中并返回。

比如,让进程0输出Hello,让进程1输出Hi就可以写成如下方式。

#include "mpi.h"
int main(int *argc,char* argv[]) 
{ 
    int myid; 
    MPI_Init(&argc,&argv); 
    MPI_Comm_Rank(MPI_COMM_WORLD,&myid); 
    if(myid==0) 
    { 
        printf("Hello!"); 
    } 
    if(myid==1) 
    { 
        printf("Hi!"); 
    } 
    MPI_Finalize(); 
} 

4.MPI_COMM_SIZE

该函数是获取该通信域内的总进程数,如果通信域为MP_COMM_WORLD,即获取总进程数,使用方法和MPI_COMM_RANK相近。

int MPI_Comm_Size(MPI_Comm, int *size) 

5.MPI_SEND

该函数为发送函数,用于进程间发送消息,如进程0计算得到的结果A,需要传给进程1,就需要调用该函数。

int MPI_Send(type* buf, int count, MPI_Datatype, int dest, int tag, MPI_Comm comm) 

这些参数均为传入的参数,其中buf为你需要传递的数据的起始地址,比如你要传递一个数组A,长度是5,则buf为数组A的首地址。count即为长度,从首地址之后count个变量。datatype为变量类型,注意该位置的变量类型是MPI预定义的变量类型,比如需要传递的是C++的int型,则在此处需要传入的参数是MPI_INT,其余同理。dest为接收的进程号,即被传递信息进程的进程号。tag为信息标志,同为整型变量,发送和接收需要tag一致,这将可以区分同一目的地的不同消息。比如进程0给进程1分别发送了数据A和数据B,tag可分别定义成0和1,这样在进程1接收时同样设置tag0和1去接收,避免接收混乱。

6.MPI_RECV

该函数为MPI的接收函数,需要和MPI_SEND成对出现。

int MPI_Recv(type* buf, int count, MPI_Datatype, int source, int tag, MPI_Comm comm, MPI_Status *status) 

参数和MPI_SEND大体相同,不同的是source这一参数,这一参数标明从哪个进程接收消息。最后多一个用于返回状态信息的参数status。

在C和C++中,status的变量类型为MPI_Status,分别有三个域,可以通过status.MPI_SOURCE,status.MPI_TAG和status.MPI_ERROR的方式调用这三个信息。这三个信息分别返回的值是所收到数据发送源的进程号,该消息的tag值和接收操作的错误代码。

SEND和RECV需要成对出现,若两进程需要相互发送消息时,对调用的顺序也有要求,不然可能会出现死锁或内存溢出等比较严重的问题。

Example

发送和接收这两个函数参数过多,初学可能看不懂部分参数的意义以及使用方法,在学了这六个函数之后,可以看下简单的例子,这个例子就把这六个函数都使用上了。

MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);        //得到的变量myid即为当前的进程号
//假设要求和的数组为A={[1,1,1,1],[2,2,2,2]}
if(myid==0)
{
    memset(A,1,sizeof(int));   //将数组A全赋值为1
}
else if (myid==1)
{
    memset(A,2,sizeof(int));   //将数组A全赋值为2
}
//以上部分是将数组的两行分别存储到进程0和进程1上
for(int i=0;i<4;i++)
{
    s=s+A[i];
}
if(myid==1)
{
    MPI_Send(s,1,MPI_INT,0,99,MPI_COMM_WORLD);
    //将求和结果s发送到进程0
}
if(myid==0)
{
    MPI_Recv(s1,1,MPI_INT,1,99,MPI_COMM_WORLD,&status);
    //用s1这个变量来存储从进程1发送来的求和结果
    s=s+s1;
}
printf("%d",&s);
MPI_Finalize();

下载release编译

源码编译

准备工作:

git clone https://github.com/pmodels/mpich.git
bash
cd mpich
git submodule update --init
sudo apt install libtool-bin autoconf automake gfortran  
sh ./autogen.sh

开始编译:

./configure --prefix=/home/<USERNAME>/mpich-install 2>&1 | tee c.txt
# ./configure --prefix=/data1/jilan.li/mpich-install 2>&1 | tee c.txt

sudo make
sudo make install

添加环境

sudo vim ~/.bashrc

export MPI_ROOT=/home/<USERNAME>/mpich-install
export PATH=$MPI_ROOT/bin:$PATH
export MANPATH=$MPI_ROOT/man:$MANPATH

sh ~/.bashrc

which mpicc 查看位置信息

mpichversion 查看版本信息,出现版本号说明安装成功

MPICH Version:          3.3.2
MPICH Release date:     Tue Nov 12 21:23:16 CST 2019
MPICH Device:           ch3:nemesis
MPICH configure:        --prefix=/home/jilan.li/anaconda3 --disable-dependency-tracking --enable-cxx --enable-fortran --disable-wrapper-rpath
MPICH CC:       x86_64-conda_cos6-linux-gnu-cc -I/home/jilan.li/anaconda3/include -march=nocona -mtune=haswell -ftree-vectorize -fPIC -fstack-protector-strong -fno-plt -O2 -ffunction-sections -pipe -I/home/jilan.li/anaconda3/include -fdebug-prefix-map=/tmp/build/80754af9/mpich-mpi_1575396103289/work=/usr/local/src/conda/mpich-3.3.2 -fdebug-prefix-map=/home/jilan.li/anaconda3=/usr/local/src/conda-prefix  -O2
MPICH CXX:      x86_64-conda_cos6-linux-gnu-c++ -I/home/jilan.li/anaconda3/include -fvisibility-inlines-hidden -std=c++17 -fmessage-length=0 -march=nocona -mtune=haswell -ftree-vectorize -fPIC -fstack-protector-strong -fno-plt -O2 -ffunction-sections -pipe -I/home/jilan.li/anaconda3/include -fdebug-prefix-map=/tmp/build/80754af9/mpich-mpi_1575396103289/work=/usr/local/src/conda/mpich-3.3.2 -fdebug-prefix-map=/home/jilan.li/anaconda3=/usr/local/src/conda-prefix -O2
MPICH F77:      x86_64-conda_cos6-linux-gnu-gfortran -I/home/jilan.li/anaconda3/include -fopenmp -march=nocona -mtune=haswell -ftree-vectorize -fPIC -fstack-protector-strong -fno-plt -O2 -ffunction-sections -pipe -I/home/jilan.li/anaconda3/include -fdebug-prefix-map=/tmp/build/80754af9/mpich-mpi_1575396103289/work=/usr/local/src/conda/mpich-3.3.2 -fdebug-prefix-map=/home/jilan.li/anaconda3=/usr/local/src/conda-prefix -O2
MPICH FC:       x86_64-conda_cos6-linux-gnu-gfortran -I/home/jilan.li/anaconda3/include -fopenmp -march=nocona -mtune=haswell -ftree-vectorize -fPIC -fstack-protector-strong -fno-plt -O2 -ffunction-sections -pipe -I/home/jilan.li/anaconda3/include -fdebug-prefix-map=/tmp/build/80754af9/mpich-mpi_1575396103289/work=/usr/local/src/conda/mpich-3.3.2 -fdebug-prefix-map=/home/jilan.li/anaconda3=/usr/local/src/conda-prefix -O2
MPICH Custom Information:

测试

hellow.c


/*
 * Copyright (C) by Argonne National Laboratory
 *     See COPYRIGHT in top-level directory
 */

#include <stdio.h>
#include "mpi.h"

int main(int argc, char *argv[])
{
    int rank;
    int size;

    MPI_Init(0, 0);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    printf("Hello world from process %d of %d\n", rank, size);
    MPI_Finalize();
    return 0;
}

编译

mpicc -o hellow hellow.c

运行

mpiexec -n 4 ./hellow

如果出现:/bin/mpicc: line 285: x86_64-conda_cos6-linux-gnu-cc: command not found
则:conda install gxx_linux-64 (保证在activate某个env的情况下)

mpiexec -n 2 cpi -i
Process 0 of 1 is on server-207
pi is approximately 3.1415926544231341, Error is 0.0000000008333410
wall clock time = 0.000044
Process 0 of 1 is on server-207
pi is approximately 3.1415926544231341, Error is 0.0000000008333410
wall clock time = 0.000023
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

canmoumou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值