在 C 语言中嵌入 mpi4py 程序
在上一篇中我们介绍了用 f2py 包装 Fortran 语言 MPI 程序以供 mpi4py 调用的方法,可以看到包装 C, C++,Fortran 等其它计算机语言的 MPI 程序供 mpi4py 调用是比较容易的,其实反过来将 mpi4py 程序嵌入其它计算机语言中也不难,下面我们将介绍在 C 语言程序中嵌入 mpi4py 程序的方法。
Python 与 C 之间的互操作在底层都是通过 Python/C API 实现的,要在 C 语言中嵌入 mpi4py 程序也是通过 Python/C API 的相关函数实现的。最简单的方法是使用函数 PyRun_SimpleString 直接执行一段 mpi4py 程序代码,不过需要注意的是要首先包含 mpi.h 和 Python.h 头文件,并初始化 MPI 和 Python 环境,等执行完 Python 程序后,记得要释放 MPI 和 Python 环境。举例如下:
/* helloworld.c */
#include <mpi.h>
#include <Python.h>
const char helloworld[] = \
"from mpi4py import MPI \n"
"hwmess = 'Hello, World! I am process %d of %d on %s.' \n"
"myrank = MPI.COMM_WORLD.Get_rank() \n"
"nprocs = MPI.COMM_WORLD.Get_size() \n"
"procnm = MPI.Get_processor_name() \n"
"print (hwmess % (myrank, nprocs, procnm)) \n"
"";
int main(int argc, char *argv[])
{
MPI_Init(&argc, &argv);
Py_Initialize();
PyRun_SimpleString(helloworld);
MPI_Finalize(); /* MPI should be finalized */
Py_Finalize(); /* after finalizing Python */
return 0;
}
可以使用类似下面的命令编译和生成可执行程序 helloworld (注意将其中的路径改成你的系统中实际的路径):
$ mpicc -I/path/to/python/include/python2.7 -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -o helloworld helloworld.c -L/path/to/python/lib/python2.7/config -lpthread -ldl -lutil -lm -lpython2.7 -Xlinker -export-dynamic
执行结果如下:
$ mpiexec -n 4 ./helloworld
Hello, World! I am process 1 of 4 on node2.
Hello, World! I am process 3 of 4 on node2.
Hello, World! I am process 2 of 4 on node2.
Hello, World! I am process 0 of 4 on node2.
也可以将要嵌入的 mpi4py 程序放入单独的文件,如 helloworld.py,然后使用 PyRun_SimpleFile 函数来执行它,如下:
# helloworld.py
from mpi4py import MPI
hwmess = 'Hello, World! I am process %d of %d on %s.'
myrank = MPI.COMM_WORLD.Get_rank()
nprocs = MPI.COMM_WORLD.Get_size()
procnm = MPI.Get_processor_name()
print (hwmess % (myrank, nprocs, procnm))
/* helloworld.c */
#include <mpi.h>
#include <Python.h>
int main(int argc, char *argv[])
{
const char *fname = "./helloworld.py";
FILE *fp;
MPI_Init(&argc, &argv);
Py_Initialize();
fp = fopen(fname, "r");
PyRun_SimpleFile(fp, fname);
MPI_Finalize(); /* MPI should be finalized */
Py_Finalize(); /* after finalizing Python */
return 0;
}
为了方便,我们也可以编写如下 Makefile 以简化编译操作(注意其中使用了在上一篇中介绍的 python-config 文件):
# Makefile
.PHONY: default build test clean
default: build test clean
PYTHON = python
PYTHON_CONFIG = ${PYTHON} ./python-config
MPICC = mpicc
CFLAGS = ${shell ${PYTHON_CONFIG} --cflags}
LDFLAGS = ${shell ${PYTHON_CONFIG} --ldflags}
build: helloworld
helloworld: helloworld.c
${MPICC} ${CFLAGS} -o $@ $< ${LDFLAGS}
MPIEXEC = mpiexec
NP_FLAG = -n
NP = 5
test: build
${MPIEXEC} ${NP_FLAG} ${NP} ./helloworld
clean:
${RM} -r helloworld
编译扩展库,执行程序及清理可以分别使用如下命令:
$ make build
$ make test
$ make clean
以上非常简单地介绍了在 C 语言中嵌入 mpi4py 程序的方法,更多的内容可以参考 Python/C API 文档。
前面我们所给出的各个例程一般都是在单台计算机上直接使用 mpiexec 或 mpirun 执行的,但是在实际应用中,对规模比较大的高性能计算任务,一般会提交到集群或超级计算机平台上进行计算。集群系统具有低成本、高性能的特性,提供了强大的批处理和并行计算能力,代表了高性能计算机发展的新方向。在集群或者超级计算机平台上,一般不能随意地直接以 mpiexec 或 mpirun 运行我们的并行计算程序,而必须通过其上提供的作业管理系统来提交计算任务。作为集群系统软件的重要组成部分,集群作业管理系统可以根据用户的需求,统一管理和调度集群的软硬件资源,保证用户作业公平合理地共享集群资源,提高系统利用率和吞吐率。下面我们将简要地介绍几个常用的集群作业管理系统:PBS,LSF 和 SLURM。在下一篇中我们首先简要介绍 PBS 作业管理系统。