贡献者: addis
sudo apt install libmpich-dev
,自动选择的版本是 3.3a2
。安装完如果找不到 mpiexec
,就退出 shell 再重新打开。
/etc/localhost
里面! 确保可以 ping,然后用 mpiexec -np 4 -host s0,s1,s2,s3 ./quad_mpi
,不能用 localhost
注意 ./
不能少。
mpiexec --version
,目前是 4.1.x,mpicxx --version
只会给出 g++ 的版本号。
sudo apt install openmpi-bin libopenmpi-dev
然而 apt 的版本是非常老的,2.x.x
,目前的版本是 4.1.4
。笔者用 2.x.x
在多台机器上从来没成功过。其实 4.1.4
也没有成功12。
https://www.open-mpi.org/software/ompi/v4.1/
里面的压缩包链接,用 wget
下载,然后解压如 tar -xzvf openmpi-4.1.4.tar.gz
(要几分钟)。进入解压文件夹,./configure
,make all -j20
,sudo make install
即可。如果 mpicxx --version
说找不到 so 文件,就用 sudo ldconfig
即可。
sudo make uninstall
卸载
mpiexec -np 2 --host server1,server2 ./可执行文件
sudo ./install.sh
安装,过程和 MKL 差不多
~/.bashrc
中添加路径,即在文件最后加入命令 source /opt/intel/compilers_and_libraries_2020.1.217/linux/mpi/intel64/bin/mpivars.sh
mpicxx --help
,如果进入帮助页面就说明成功了
mpicxx
或者 mpigxx
都是 g++ 的一个 wraper,和 g++ 一样使用即可。如果编译和链接分开也都要用。
# include <mpi.h>
mpiexec -np 4 ./main.x
指定进程数量。mpirun
也基本一样,但并不在 MPI 标准中。运行这个命令后,所有进程就都开始了。
mpiexec -np 4 ./main.x [参数1] [参数2] < 输入文件
即可。每一个进程都会获得完全一样的参数和独立的标准输入(不存在抢读输入文件的情况)。
MPI_Init(&argc, &argv);
开始 MPI,MPI_Finalize();
结束。初始化会占用一些时间,可以留到程序中真的需要用到 MPI 的时候才使用,在这之前每个进程可以各自执行自己的事情(但是无法获取节点编号,所以一般在 init 前的计算都是完全相同的)。
MPI_Comm_size(MPI_Comm comm, &size);
获取指定 communicator 的进程数
MPI_Comm_rank(MPI_Comm comm, &rank);
获取当前进程的 id,称为 rank。
int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
其中 dest
是进程 id
,tag
是一个编号用于区分不同的消息(只需要区分来源和目标相同的多条消息),comm
一般是 MPI_COMM_WORLD
(所有进程)。
MPI_Send
执行完以后,修改 buf
是安全的。
MPI_Datatype
有 MPI_CHAR
,MPI_UNSIGNED_CHAR
,MPI_INT
,MPI_UNSIGNED
,MPI_LONG
,MPI_LONG_LONG
,MPI_FLOAT
,MPI_DOUBLE
,MPI_LONG_DOUBLE
,MPI_COMPLEX
,MPI_REAL16
,MPI_COMPLEX32
。
count
是 buf
中有几个数据,即数组的长度。
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
,接收信息。status
可以用于获取更多信息,其他参数同理。注意如果没有收到信息就会一直等待。
MPI_Send
和 MPI_Send
之前还是必须要 MPI_Bcast
让所有的节点都知道接收者是谁。如果信息量不大,还不如把信息和接收者一起直接广播。例如每个节点有一个数,要找到最大的数所在的节点并通知它,就直接用下面的 MPI_Allreduce
和 MPI_MAXLOC
即可。这样编程起来也会简单的多。
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
从所有进程调用,把所有进程中的 buffer 同步为 root
的。这相当于分别对每个进程用 MPI_Send()
和 MPI_Recv()
。注意如果 root 节点是运行时才确定的,那所有节点都要事先知道 root 是谁(要不然怎么确定自己要不要发送呢?)。
double MPI_Wtime()
返回当前进程的时间(从任意起点开始,单位秒)。
int MPI_Reduce(const void* send_buffer, void* receive_buffer, int count, MPI_Datatype datatype, MPI_Op operation, int root, MPI_Comm communicator);
把每个进程中的某个变量(或数组)相加或相乘等,赋值给 id 为 root
的某个变量(或数组,即每个元素分别相加)。其中 operation
可以是 MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD
int MPI_Allreduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
和 MPI_Reduce
一样,但结果会返回到所有进程。
int MPI_Barrier(MPI_Comm comm)
可以同步所有的进程到某个点。
Scatter()
(未完成)
Gather()
(未完成)
MPI_Allgather(&my_number, 1, MPI_DOUBLE, all_numbers, 1, MPI_DOUBLE, MPI_COMM_WORLD);
其中 my_number
是双精度数字,all_numbers
是双精度数组的指针。
~/.ssh/config
或者 host 文件(给每个 ip 一个名称),设置免密码 ssh。
mpirun -np 5 -host localhost,worker1,worker2,... ./可执行文件
。其中 localhost
是本机,worker1,worker2
是 ssh config 中的其他机器的名称。如果不指定 -host
,所有进程将在本机运行。
-np
指定的是进程。
mpirun
依赖 ssh
在其他主机运行进程等,但在真正的通讯中一般使用 MPI 自己的通讯协议(基于 TCP/IP
或 InfiniBand 等)
slots
):mpiexec -np 12 -machinefile machines.txt ./main.x
localhost slots=2
machine1 slots=4
machine2 slots=4
machine3 slots=2
其中也可以不包含 localhost
,那么所有进程将远程运行。
1. ^ WARNING: Open MPI accepted a TCP connection from what appears to be a another Open MPI process but cannot find a corresponding process entry for that peer.
2. ^ WARNING: Open MPI failed to TCP connect to a peer MPI process. This should not happen. ... connect() to 10.0.2.4:1024 failed