一、MPI
1. 关于MPI
MPI(Message Passing Interface)是一个信息传递应用程序接口,可以用于并行计算。MPI有多种实现版本,如MPICH, CHIMP以及OpenMPI。其中最常见的的是MPICH和OpenMPI,本文使用的是MPICH。
MPICH官网:
OpenMPI官网:
2. 软硬件准备
机器准备:
3个节点(dellnode1、dellnode2、dellnode3)
操作系统:
Centos 6.5 64位
依赖软件:
该安装过程需要c、c++等编译器,否则安装过程中会出现如下错误:
若出现该错误,只需按照提示信息安装对应的编译器即可
3. 系统环境配置
为减小配置对整个系统的影响,我们在 root 用户下新建一个名为“mpiuser”的用户,然后将要安装的的软件都安装在该用户目录下。
sudo useradd -m mpiusersudo passwd mpiusersudo adduser mpiuser sudo
之后注销当前用户,返回登陆界面。在登陆界面中选择刚创建的 mpiuser 用户进行登陆。
4. 下载并安装MPI
进入MPICH官网(),点击“Download MPICH”
选择对应的版本,点击Download下的http即可下载。
3.2版可直接点击如下链接进行下载:
接下来将下载的压缩包拷贝到Linux系统下,在mpiuser目录下新建一个mpich目录
cd /home/mpiusermkdir mpich
切换到压缩包目录,使用如下命令进行安装:
tar -xzvf mpich-3.2.1.tar.gz -C /home/mpiuser/mpich/cd /home/mpiuser/mpich/mpich-3.2.1/./configure --prefix=/home/mpiuser/mpich #运行后出现"Configuration completed."即配置成功#可能会提示你需要安装各种编译器#gcc编译器:sudo yum install gcc#g++编译器:sudo yum install gcc-c++#gfortran编译器:sudu yum install gcc-gfortranmake && make install #若出现问题,请使用root权限安装
安装后加入环境变量
vim /home/mpiuser/.bashrc
添加如下内容(注意要与自己实际的安装目录相对应,不然后面会无法识别mpi的相关命令)
PATH=$PATH:/home/mpiuser/mpich/binexport PATH
执行source命令
source /home/mpiuser/.bashrc
二、单节点测试
切换到example目录下
cd ~/mpich/mpich3.2.1/examples/
执行
mpirun -n 5 ./cpi
若输出结果如下,则单节点已经配置成功(注意在可执行文件cpi前面加上 ./ ,不然会报 “HYDU_create_process (./utils/launch/launch.c:75): execvp error on file cpi (No such file or directory)” 的错误)
如果出现以下报错,只需要修改/etc/hosts文件,添加一行“127.0.0.1 hostname”(hostname为你自己的主机名)
三、多节点配置
1、host配置(所有节点)
vim /etc/hosts
source /etc/hosts
可以互ping来测试主机名修改是否成功(如使用 “ping dellnode2” )
2、集群机器上面需要配置ssh登录权限
生成公钥,并发送给dellnode2节点
cd ~/.ssh/ # 若没有该目录,请先执行一次ssh localhostssh-keygen -t rsa # 会有提示,接着连按3次回车cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys #将自己的公钥追加到authorized_keys里
用ssh登录到dellnode2、dellnode3上面执行相同的操作,并将产生的公钥发送给dellnode1
cd ~/.ssh/ # 若没有该目录,请先执行一次ssh localhostssh-keygen -t rsa # 会有提示,接着连按3次回车scp ./id_rsa.pub mpiuser@dellnode1:~/.ssh/node2_id_rsa.pub#避免名字重复加上对应节点的前缀,节点3就加node3
将发送过来的公钥追加到authorized_keys
cat ~/.ssh/node2_id_rsa.pub >> ~/.ssh/authorized_keyscat ~/.ssh/node3_id_rsa.pub >> ~/.ssh/authorized_keys
修改文件权限并将authorized_keys文件发送给另外2个节点
chmod 600 ~/.ssh/authorized_keys #可能有时不修改也不影响无密登录但还是建议修改scp ./authorized_keys mpiuser@dellnode2:~/.ssh/authorized_keysscp ./authorized_keys mpiuser@dellnode3:~/.ssh/authorized_keys
把"~/.ssh/"目录下的"node1_id_rsa.pub"等文件删除掉
rm ~/.ssh/node1_id_rsa.pub #同上rm ~/.ssh/node2_id_rsa.pub #同上
验证ssh无密登录,如下3个节点节可以任意实现无密登录
如果无法成功,请检验追加的公钥是否正确,若还是不行请确保ssh服务开启
sudo vim /etc/ssh/sshd_config
去掉注释:
RSAAuthentication yes # 启用 RSA 认证
PubkeyAuthentication yes # 启用公钥私钥配对认证方式
AuthorizedKeysFile .ssh/authorized_keys # 公钥文件路径(和上面生成的文件同)
/bin/systemctl restart sshd.service
3、在其他机器上面安装mpich
可以使用scp直接复制(要保证其他环境已经搭好,如各种编译器,否者可能无法正常编译运行程序),也可以对每台机器进行压缩包的解压安装。
scp -r /home/mpiuser/mpich mpiuser@dellnode2:/home/mpiuser/
配置每台机器的环境变量
4. 新建一个servers文件,内容如下:
dellnode1:3 #运行3个进程dellnode2:3 #运行3个进程dellnode3:3 #运行3个进程
执行下面命令,并指定servers文件
mpirun -n 9 -f ./servers ./mpich-3.2.1/examples/cpi #在examples目录下
若输出如下则代表环境配置成功
四、mpi程序演示
演示程序:蒙特卡洛方法计算圆周率(参考:)
其思想是假设我们向一个正方形的标靶上随机投掷飞镖,靶心在正中央,标靶的长和宽都是2 。同时假设有一个圆与标靶内切。圆的半径是1,面积是π的平方。如果击中点在标靶上是均匀分布的(我们总会击中正方形),那么飞镖击中圆的数量近似满足等式,飞镖落在圆内的次数/飞镖落在标靶内的总次数=π/4,环包含的面积与正方形面积的比值是π/4。环所包含的面积与正方形面积的比值是π/4。我们可以用这个公式和随机数产生器来估计π的值。
新建 “pi.c” 源文件,其代码如下:
#include#include #include #include #include void read_num(long long int *num_point,int my_rank,MPI_Comm comm);void compute_pi(long long int num_point,long long int* num_in_cycle,long long int* local_num_point,int comm_sz,long long int *total_num_in_cycle,MPI_Comm comm,int my_rank);int main(int argc,char** argv){ long long int num_in_cycle,num_point,total_num_in_cycle,local_num_point; int my_rank,comm_sz; double begin,end; MPI_Comm comm; MPI_Init(NULL,NULL);//初始化 comm=MPI_COMM_WORLD; MPI_Comm_size(comm,&comm_sz);//得到进程总数 MPI_Comm_rank(comm,&my_rank);//得到进程编号 read_num(&num_point,my_rank,comm);//读取输入数据 begin=MPI_Wtime(); compute_pi(num_point,&num_in_cycle,&local_num_point,comm_sz,&total_num_in_cycle,comm,my_rank); end=MPI_Wtime(); if(my_rank==0){ printf("Elapsing time: %fs\n",end-begin); } MPI_Finalize(); return 0;}void read_num(long long int* num_point,int my_rank,MPI_Comm comm){ if(my_rank==0){ printf("please input num in sqaure \n"); scanf("%lld",num_point); } MPI_Bcast(num_point,1,MPI_LONG_LONG,0,comm);}void compute_pi(long long int num_point,long long int* num_in_cycle,long long int* local_num_point,int comm_sz,long long int *total_num_in_cycle,MPI_Comm comm,int my_rank){ *num_in_cycle=0; *local_num_point=num_point/comm_sz; double x,y,distance_squared; srand(time(NULL)); for(long long int i=0;i< *local_num_point;i++){ x=(double)rand()/(double)RAND_MAX; x=x*2-1; y=(double)rand()/(double)RAND_MAX; y=y*2-1; distance_squared=x*x+y*y; if(distance_squared<=1) *num_in_cycle=*num_in_cycle+1; } MPI_Reduce(num_in_cycle,total_num_in_cycle,1,MPI_LONG_LONG,MPI_SUM,0,comm); if(my_rank==0){ double pi=(double)*total_num_in_cycle/(double)num_point*4; printf("the estimate value of pi is %lf\n",pi); }}
在pi.c同目录下新建servers文件,内容如下
dellnode1:1 #运行1个进程dellnode2:1 #运行1个进程
编译、运行
mpicc -std=c99 -o pi.out pi.c #编译scp ./pi mpiuser@dellnode2:/home/mpiuser/mpich/myMPIProgram/ #拷贝可执行程序到dellnode2同路径下mpirun -f servers -n 2 ./pi.out #运行程序 注意加"./"
由于服务器为多核设计,所以在没有其他设置的情况下,只是简单的设置进程数为2(不使用“-f servers”参数),则2个进程都在同一个节点上运行,虽然时间确实变快了2倍,单此情况并没有利用多个节点的性能,当进程数超过逻辑cpu个数时就不能再获得加速了,而这不是我们所期望的。(如下图所示)
要使程序在不同的节点上运行,需要指定参数“-f servers”,其中“servers”是我们自己建立的一个文件,用来指定每个节点运行的进程数等参数。同时为了保证其他节点(如dellnode2)能够正常运行程序,需要保证在与dellnode1可执行文件相同路径下有同样的可执行程序(使用scp拷贝或建立NFS共享目录,详情见写下一章),否则会报如下找不到文件的错误。
正确的运行方式如下图:
可以看到,当取点的个数为10亿个时,单节点要运行44秒,使用2个节点并行只需22秒。
五、NFS共享目录安装配
(参考:)
如果不进行这一步mpi程序也可以正常运行,但是需要保证在每个节点上的相同路径下都有可执行文件,所以每次都要把可执行文件进行远程拷贝,非常麻烦!
服务端:dellnode1(10.10.1.1)
客户端:dellnode2(10.10.1.2)、dellnode3(10.10.1.3)
1.服务端安装配置
安装nfs-utils和rpcbind
sudo yum -y install nfs-utils rpcbind
创建共享目录:
sudo mkdir /mpiShareDir
配置/etc/exports
sudo vim /etc/exports
#参数说明#rw:read-write,可读写;#ro:read-only,只读;#sync:同步写入(文件同时写入硬盘和内存),适用在通信比较频繁且实时性比较高的场合#async:异步写入(文件先写入内存,稍候再写入硬盘),性能较好(速度快),适合超大或者超多文件的写入,但有数据丢失的风险,比如突然断电等情况;#注意:除非特別有需要,否则不建议使用 async。如果沒有指定 sync 或 async,NFS 服务器在启动的时候会印出警告信息。#no_root_squash:NFS客户端连接服务端时如果使用的是 root 的话,那么对服务端分享的目录也使用 root 权限。不安全!#root_squash:把客户端 root 身份的 UID/GID (0/0) 对应到服务端的 nobody 用户去,即服务端使用 nobody 用户来操作共享目录;#all_squash:不论NFS客户端连接服务端时使用什么用户,对服务端分享的目录来说都是拥有匿名用户权限;#anonuid:匿名用户的UID值,通常是nobody或nfsnobody,可以在此处自行设定;#anongid:匿名用户的GID值。
启动服务,并设置开机启动
sudo service rpcbind startsudo service nfs startsudo chkconfig --level 2345 rpcbind onsudo chkconfig --level 2345 nfs on
2.客户端配置
安装nfs-utils和rpcbind
sudo yum -y install nfs-utils rpcbind
创建目录
sudo mkdir /mpiShareDir
查看服务端共享目录
showmount -e 10.10.1.1
挂载共享目录到本地,并测试读写:
sudo mount -t nfs 10.10.1.1:/mpiShareDir /mpiShareDir#最开始,我将共享目录放在/home/mpiuser下的mpiShareDir文件夹内,虽然mpiShareDir的权限为777#但是由于其父目录(即用户目录mpiuser)的权限为700,所以挂载时遇到服务器拒绝访问的问题#于是我将共享目录放在根目录下,并将其权限设置为777,否则需要增加用户目录的访问权限cd /mpiShareDir && touch test
设置开机自动挂载:
sudo vim /etc/fstab
10.10.1.1:/mpiShareDir /mpiShareDir nfs defaults 0 0
上述步骤完成后,以后只需将mpi程序放在mpiShareDir目录下就可以直接运行,而不需要使用远程拷贝。