前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux下MPI的安装与vscode的配置

Linux下MPI的安装与vscode的配置

作者头像
Here_SDUT
发布2022-09-19 11:28:29
9.1K1
发布2022-09-19 11:28:29
举报

零、前言

近期由于一些原因接触到了并行计算,对于这个陌生的领域我最先接触到的是MPI框架。MPI(Message Passing Interface),可以理解为是一种独立于语言的信息传递标准。目前它有两种具体的实现OpenMPI和MPICH,也就是说如果我们要使用MPI标准进行并行计算,就需要安装OpenMPI或MPICH库。本文以MPICH为例,在ubantu中安装MPI的环境,并对vscode进行配置。

一、MPI安装

1.1 前置准备

安装 mpich 之前需要安装好相应的编译器,可以通过查看是否安装了

代码语言:javascript
复制
$ gcc --version 
$ fortran --version 
$ gfortran --version
image-20220320165141580
image-20220320165141580

如果没有安装则使用 sudo apt-get install gcc 安装即可(gcc替换成你没有的编译器)。

1.2 下载MPI

可以去这里下载一个MPI的程序包,选择你要下载的版本即可,其中Platform要选择MPICH的。

image-20220320165406607
image-20220320165406607

下好了之后将其放到你要安装的目录下,强烈建议在home目录下建一个新的文件夹比如mpi来放置

1.3 安装

将下载的安装包进行解压,可以在窗口中选中右键解压,也可以 cd 到目录下用 tar xzf +文件名 来解压。

然后配置安装路径,cd到解压的文件夹,我的解压文件夹名称为mpich-3.4.3,所以我先cd mpich-3.4.3 ,然后输入./configure -prefix=/home/[username]/mpi ,其中 -prefix= 后写的是你的mpich的安装路径。

然后进行编译 make ,这一步很久(我大概用了十几分钟),需要耐心等待。

然后进行安装 make install

安装完成后添加环境变量,先输入sudo gedit ~/.bashrc ,然后会要求你输入密码,输入后会弹出一个文本框,输入下列路径:

代码语言:javascript
复制
export MPIPATH=/home/fang/mpi
export MPIPATHBIN=$MPIPATH/bin
export MPIPATHINCLUDE=$MPIPATH/include
export MPIPATHLIB=$MPIPATH/lib
export MPIPATHSHARE=$MPIPATH/share
export PATH=$PATH:$MPIPATHBIN:$MPIPATHINCLUDE:$MPIPATHLIB:$MPIPATHSHARE
image-20220320171145276
image-20220320171145276

注意: 第一行的 MPIPATH 需要写你安装的MPI的那个文件夹,其他不用改动

然后在终端中输入 source .bashrc 激活环境变量

1.4 测试

首先输入 which mpicc 可以查看你的mpich的安装路径。

然后打开终端cd进入你所下载的压缩包的解压文件夹,该路径下有个 example 文件夹,里面是mpich官方的示例代码,终端中输入:

代码语言:javascript
复制
mpirun -np 10 ./examples/cpi
mpiexec -n 4 ./examples/cpi
image-20220320171754484
image-20220320171754484

就完成了!

二、运行MPICH

3.1 命令行大法

如果用C++编写则用第一条,如果用C编写则用第二条,其中xxx是你要编译的文件名,yyy是你编译完成后生成的exe文件的文件名

代码语言:javascript
复制
mpic++ xxx.cpp -o yyy
mpigcc xxx.c -o yyy

然后运行可执行文件,需要先cd到可执行文件的路径下,yyy 是你的可执行文件夹名字,千万不能漏掉 ./, 前面的参数 4 表示分配4个进程并行运行

代码语言:javascript
复制
mpirun -np 4 ./yyy

3.2 vscode配置

使用code runner插件运行,进入插件设置页,然后点击 在settings.json中编辑,自动进入settings.json 文件

image-20220320172544098
image-20220320172544098

文件结构如下所示:

image-20220320172707492
image-20220320172707492

可以复制我的配置:

代码语言:javascript
复制
{
    "code-runner.runInTerminal": true,
    "files.autoSave": "afterDelay",
    "code-runner.executorMap": {
        "javascript": "node",
        "java": "cd $dir && javac $fileName && java $fileNameWithoutExt",
        "c": "cd $dir && gcc $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
        //"cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
        "cpp" : "cd $dir && mpic++ $fileName -o /home/fang/code/papercode/exe/$fileNameWithoutExt &&cd $dir && mpirun -np 4 /home/fang/code/papercode/exe/$fileNameWithoutExt",
        "objective-c": "cd $dir && gcc -framework Cocoa $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
        "php": "php",
        "python": "python -u",
        "perl": "perl",
        "perl6": "perl6",
        "ruby": "ruby",
        "go": "go run",
        "lua": "lua",
        "groovy": "groovy",
        "powershell": "powershell -ExecutionPolicy ByPass -File",
        "bat": "cmd /c",
        "shellscript": "bash",
        "fsharp": "fsi",
        "csharp": "scriptcs",
        "vbscript": "cscript //Nologo",
        "typescript": "ts-node",
        "coffeescript": "coffee",
        "scala": "scala",
        "swift": "swift",
        "julia": "julia",
        "crystal": "crystal",
        "ocaml": "ocaml",
        "r": "Rscript",
        "applescript": "osascript",
        "clojure": "lein exec",
        "haxe": "haxe --cwd $dirWithoutTrailingSlash --run $fileNameWithoutExt",
        "rust": "cd $dir && rustc $fileName && $dir$fileNameWithoutExt",
        "racket": "racket",
        "scheme": "csi -script",
        "ahk": "autohotkey",
        "autoit": "autoit3",
        "dart": "dart",
        "pascal": "cd $dir && fpc $fileName && $dir$fileNameWithoutExt",
        "d": "cd $dir && dmd $fileName && $dir$fileNameWithoutExt",
        "haskell": "runhaskell",
        "nim": "nim compile --verbosity:0 --hints:off --run",
        "lisp": "sbcl --script",
        "kit": "kitc --run",
        "v": "v run",
        "sass": "sass --style expanded",
        "scss": "scss --style expanded",
        "less": "cd $dir && lessc $fileName $fileNameWithoutExt.css",
        "FortranFreeForm": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
        "fortran-modern": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
        "fortran_fixed-form": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
        "fortran": "cd $dir && gfortran $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt"
    },
    "window.zoomLevel": 2,
    "explorer.confirmDelete": false
}

其中的cpp表示当运行的文件为c++时使用的命令,你也可以自己配置,可用的参数如下:

代码语言:javascript
复制
$workspaceRoot
$dir
$dirWithoutTrailingSlash
$fullFileName
$fileName
$fileNameWithoutExt

以我所配置的mpich的运行方式为例进行解释:

代码语言:javascript
复制
 "cpp" : "cd $dir && mpic++ $fileName -o /home/fang/code/papercode/exe/$fileNameWithoutExt &&cd $dir && mpirun -np 4 /home/fang/code/papercode/exe/$fileNameWithoutExt"
  • 首先 cd 到 dir 表示进入当前文件所在路径
  • && 表示并的关系,即只有前面的命令运行成功后才运行后面的命令。
  • 然后是 mpic++ fileName -o /home/fang/code/papercode/exe/fileNameWithoutExt,这里的 fileName 是当前需要运行的代码文件名称,fileNameWithoutExt表示不带后缀的文件名称,这一行的命令意思是将文件使用MPICH进行编译,然后存放到 /home/fang/code/papercode/exe/路径下,编译出来的文件名为
  • 最后是mpirun -np 4 /home/fang/code/papercode/exe/$fileNameWithoutExt 就是使用MPICH运行可执行文件,不在解释。

三、MPI编程框架

1.MPI_Init

任何MPI程序都应该首先调用该函数。 此函数不必深究,只需在MPI程序开始时调用即可(必须保证程序中第一个调用的MPI函数是这个函数)。

代码语言:javascript
复制
MPI_Init(&argc, &argv)

Fortran版本调用时不用加任何参数,而C和C++需要将main函数里的两个参数传进去,因此在写main函数的主程序时,应该加上这两个形参。

代码语言:javascript
复制
int main(int *argc,char* argv[]) 
{ 
    MPI_Init(&argc,&argv); 
}

2.MPI_Finalize

任何MPI程序结束时,都需要调用该函数。 该函数同第一个函数,都不必深究,只需要求格式去写即可。

代码语言:javascript
复制
MPI_Finalize() 

3.MPI_COMM_RANK

代码语言:javascript
复制
int MPI_Comm_Rank(MPI_Comm comm, int *rank) 

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

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

4.MPI_COMM_SIZE

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

代码语言:javascript
复制
MPI_COMM_SIZE(comm, size) 
int MPI_Comm_Size(MPI_Comm, int *size) 

5.MPI_SEND

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

代码语言:javascript
复制
call MPI_SEND(buf, count, datatype, dest, tag, comm) 
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成对出现。

代码语言:javascript
复制
call MPI_RECV(buf, count, datatype, source, tag, comm,status) 
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_SOURCEstatus.MPI_TAGstatus.MPI_ERROR的方式调用这三个信息。这三个信息分别返回的值是所收到数据发送源的进程号,该消息的tag值和接收操作的错误代码。

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

7. MPI_Barrier

该函数为一个阻塞函数

代码语言:javascript
复制
 MPI_Barrier(MPI_Comm comm);

填入的参数为通信域,当进程执行该函数并且属于该通信域时,则停止执行进入等待状态,当该通信域的所有进程都执行到该函数后才继续往下进行。

7. 例子

代码语言:javascript
复制
int main(int argc, char** argv) {
    MPI_Init(NULL, NULL); // MPI启动 一直到Finalize之间的都会并行执行
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); // 获取当前进程id
	if (rank == 0) {
        ......  // 对0号进程进行操作
    }else if(rank == 1) {
        ...... // 对1号进程进行操作
    }
    MPI_Barrier(MPI_COMM_WORLD); //等待函数,所有进程都调用这个函数后才继续往下运行
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    printf("rank%d\n", rank);
    if(rank == 1){
        ......
    }
    MPI_Finalize();// MPI结束
    return 0;
}

四、总结

这次配置MPICH的过程还是收获颇丰的,第一次领略到了用Linux安装环境的快捷与舒适,还了解vscode的很多配置原理(之前都是无脑配置的),最后还入门了一种全新的编程方式,并行计算。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-3-20 1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 零、前言
  • 一、MPI安装
    • 1.1 前置准备
      • 1.2 下载MPI
        • 1.3 安装
          • 1.4 测试
          • 二、运行MPICH
            • 3.1 命令行大法
              • 3.2 vscode配置
              • 三、MPI编程框架
                • 1.MPI_Init
                  • 2.MPI_Finalize
                    • 3.MPI_COMM_RANK
                      • 4.MPI_COMM_SIZE
                        • 5.MPI_SEND
                          • 6.MPI_RECV
                            • 7. MPI_Barrier
                              • 7. 例子
                              • 四、总结
                              相关产品与服务
                              GPU 云服务器
                              GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档