在上一篇中我们介绍了用 Boost.Python 包装 C++ 语言 MPI 程序以供 mpi4py 调用的方法,下面我们将介绍使用 f2py 包装 Fortran MPI 程序的方法。
f2py (Fortran to Python interface generator) 是 numpy 中自带的一个 Fortran 到 Python 的接口生成工具,支持 Fortran 77/90/95,可以使用它将 Fortran 程序包装成可供 Python 调用的扩展模块。感兴趣的读者可以参考其文档,这里不多作介绍。我们只会以简单的例子展示如何使用 f2py 包装 Fortran MPI 程序以供 mpi4py 调用。
假设我们有以下 Fortran 程序文件 helloworld.f90,其中定义了子例程 sayhello,其接受一个 MPI 通信子作为参数。
! helloworld.f90 ! ! $ f2py --f90exec=mpif90 -m helloworld -c helloworld.f90 ! subroutine sayhello(comm) use mpi implicit none integer :: comm integer :: rank, size, nlen, ierr character (len=MPI_MAX_PROCESSOR_NAME) :: pname if (comm == MPI_COMM_NULL) then print *, 'You passed MPI_COMM_NULL !!!' return end if call MPI_Comm_rank(comm, rank, ierr) call MPI_Comm_size(comm, size, ierr) call MPI_Get_processor_name(pname, nlen, ierr) print *, 'Hello, World!', & ' I am process ', rank, & ' of ', size, & ' on ', pname(1:nlen), '.' end subroutine sayhello ! program main ! use mpi ! implicit none ! integer ierr ! call MPI_Init(ierr) ! call sayhello(MPI_COMM_WORLD) ! call MPI_Finalize(ierr) ! end program main
要将其编译成扩展模块 helloworld.so 以供 mpi4py 程序调用,我们可以使用类似于以下的命令一步到位:
$ f2py --f90exec=mpif90 -m helloworld -c helloworld.f90
其中 -m 指定模块名,-c 指明编译和重建扩展模块。
但是 f2py 文档中推荐使用以下两步走的方案:
首先通过以下命令产生一个 signature 文件 helloworld.pyf:
$ f2py -m helloworld -h helloworld.pyf helloworld.f90
生成的 signature 文件如下:
! -*- f90 -*- ! Note: the context of this file is case sensitive. python module helloworld ! in interface ! in :helloworld subroutine sayhello(comm) ! in :helloworld:helloworld.f90 use mpi integer :: comm end subroutine sayhello end interface end python module helloworld ! This file was auto-generated with f2py (version:2). ! See http://cens.ioc.ee/projects/f2py2e/
可以根据需要手动编辑所产生的 signature 文件(一般情况下不用作任何编辑也能正常工作)。 作为一个简单的例子,我们在 integer :: comm
这一句中加上 intent(in)
,使 helloworld.pyf 变成如下:
! -*- f90 -*- ! Note: the context of this file is case sensitive. python module helloworld ! in interface ! in :helloworld subroutine sayhello(comm) ! in :helloworld:helloworld.f90 use mpi integer intent(in) :: comm end subroutine sayhello end interface end python module helloworld ! This file was auto-generated with f2py (version:2). ! See http://cens.ioc.ee/projects/f2py2e/
然后使用以下命令生成扩展模块:
编译成功后会生成扩展模块 helloworld.so,然后就可以在我们的 mpi4py 程序中像使用其它 Python 模块一样导入该模块并调用该模块中定义的 sayhello 函数,可以向此函数传递一个 mpi4py 中定义的通信子,如 MPI.COMM_WORLD 或者其它通信子对象。例如使用以下的 test 例程:
# test.py from mpi4py import MPI import helloworld as hw null = MPI.COMM_NULL fnull = null.py2f() hw.sayhello(fnull) comm = MPI.COMM_WORLD fcomm = comm.py2f() hw.sayhello(fcomm) try: hw.sayhello(list()) except: pass else: assert 0, "exception not raised"
执行结果如下:
$ mpiexec -n 4 python test.py You passed MPI_COMM_NULL !!! Hello, World! I am process 0 of 4 on node2. You passed MPI_COMM_NULL !!! Hello, World! I am process 1 of 4 on node2. You passed MPI_COMM_NULL !!! Hello, World! I am process 2 of 4 on node2. You passed MPI_COMM_NULL !!! Hello, World! I am process 3 of 4 on node2.
以上的过程直接编译出了扩展模块,如果想得到或看看扩展模块的源文件,可以使用如下命令:
$ f2py -m helloworld helloworld.f90
该命令会生成扩展模块源码文件 helloworldmodule.c,感兴趣的读者可以打开看看。当然如果你愿意的话也可以用一个 C 编译器手动地将此源文件编译成一个扩展模块。
为了方便,我们也可以编写如下 Makefile 以简化上述操作(注意其中使用了在上一篇中介绍的 python-config 文件):
# Makefile .PHONY: default default: build test clean PYTHON = python PYTHON_CONFIG = ${PYTHON} ./python-config MPIF90 = mpif90 F2PY = f2py SO = ${shell ${PYTHON_CONFIG} --extension-suffix} .PHONY: build build: helloworld${SO} helloworld${SO}: helloworld.f90 ${F2PY} --f90exec=${MPIF90} -m helloworld -c $< MPIEXEC = mpiexec NP_FLAG = -n NP = 5 .PHONY: test test: build ${MPIEXEC} ${NP_FLAG} ${NP} ${PYTHON} test.py .PHONY: clean clean: ${RM} helloworld${SO}
编译扩展库,执行程序及清理可以分别使用如下命令:
$ make build $ make test $ make clean
以上我们介绍了用 f2py 包装 Fortran 语言 MPI 程序以供 mpi4py 调用的方法,可以看到包装 C, C++,Fortran 等其它计算机语言的 MPI 程序供 mpi4py 调用是比较容易的,其实反过来将 mpi4py 程序嵌入其它计算机语言中也不难,在下一篇中我们将介绍在 C 语言程序中嵌入 mpi4py 程序的方法。