上一篇實現了一個簡單的C++函數對python的擴展,但是其實現方法需要對C/C++擴展python有很深的瞭解,對我而言目前的學習成本過高,因此有了這一篇博客:
swig是一個包裝和接口生成器工具,可以爲C/C++程序構建生成各種腳本語言的調用接口。
使用 SWIG 包裝 C/C++ 程序是比較簡單的,只需要編寫一個 .i 接口文件,在其中聲明調用接口和完成所需的類型映射即可。如要了解更多,可參考完整的 SWIG 使用文檔,此處我們只以以下簡單的例子來展示用 SWIG 包裝 C 語言 MPI 程序以供 mpi4py 調用的方法。SWIG 的 .i 接口文件有點類似於 C/C++ 的頭文件,最簡單的包裝 C 語言方法是直接將一段 C 程序代碼放入 .i 接口文件的 %{ %} 之間,並在外面完成類型映射及聲明要導出的函數接口。
我們將此函數放入接口文件 helloworld.i 的 %{ %} 之間,並在下面聲明其函數原型,因爲其參數爲一個 MPI_Comm 類型的通信子,還需要指明一個由 mpi4py 中的通信子到 MPI_Comm 類型的映射,這個映射在 mpi4py 軟件中包含的 mpi4py.i 中定義,因此需要 include mpi4py/mpi4py.i。
因爲我的需求是擴展mpi通信,所以索性這次的例子先直接上手一個mpi點對點通信的簡單測試。
完整的接口文件如下:
/* hello.i */
%module hello
%{
#define MPICH_SKIP_MPICXX 1
#define OMPI_SKIP_MPICXX 1
#include <mpi.h>
#include <stdio.h>
int sayhello(MPI_Comm comm) {
int size, rank;
int value;
MPI_Status status;
char pname[MPI_MAX_PROCESSOR_NAME]; int len;
if (comm == MPI_COMM_NULL) {
printf("You passed MPI_COMM_NULL !!!\n");
return -1;
}
MPI_Comm_size(comm, &size);
MPI_Comm_rank(comm, &rank);
MPI_Get_processor_name(pname, &len);
pname[len] = 0;
printf("Hello, World! I am process %d of %d on %s.\n",
rank, size, pname);
int x;
if(rank == 0){
value = 2;
MPI_Send(&value, 1, MPI_INT, 1, 0, comm);
}
else{
MPI_Recv(&value, 1, MPI_INT, 0, 0, comm, &status);
}
return value;
}
%}
%include mpi4py/mpi4py.i
%mpi4py_typemap(Comm, MPI_Comm);
int sayhello(MPI_Comm comm);
使用 swig 工具和以上的接口文件就可以產生兩個包裝文件:hello_wrap.c 和 hello.py,命令如下(注意將其中的頭文件路徑改成你的系統中實際的路徑):
$ swig -python -I/path/to/python/lib/python3.8/site-packages/mpi4py/include -o hello_wrap.c hello.i
生成的 helloworld_wrap.c 文件是對 sayhello 函數的一個包裝,而生成的 helloworld.py 中有如下語句:
...
import _hello
...
def sayhello(*args):
return _hello.sayhello(*args)
sayhello = _hello.sayhello
由此可見我們還需要由 hello_wrap.c 編譯出一個名稱爲 _hello.so 的擴展庫才能被 Python 導入和使用,編譯所用的命令同我們在上一篇中介紹的命令幾乎一致,此次我們使用mpi進行編譯,如下(注意將其中的頭文件路徑改成你的系統中實際的路徑):
mpicc -I/.../anaconda3/include/python3.8 -I/.../anaconda3/lib/python3.8/site-packages/mpi4py/include -o _hello.so hello_wrap.c -fPIC -shared -lpthread -ldl -lutil -lm -lpython2.7
在編譯時需要給出相應的庫,如果不知道自己的自己的路徑可以通過在命令行輸入如下命令來獲取依賴庫的位置:
python -c "import sysconfig; print( sysconfig.get_path('include') )"
python -c "import mpi4py; print( mpi4py.get_include() )"
下面我們寫一個test.py來進行簡單的測試。
import hello as ho
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
x = ho.sayhello(comm)
print("rank is %d, x= %d"%(rank,x))
測試結果如下,嗯簡單實現成功了。
但是這種編譯方式對於真正的項目開發而言還是有些繁瑣,所以接下將近一步學習相應的擴展過程。
本文大部分內容借鑑自:
https://www.jianshu.com/p/558d4f3e4bfb
https://www.jianshu.com/p/104fd3775fed
感謝大佬的分享,這個博主詳細的寫了很多關於mpi4py的東西,從最基礎的開始到後面的擴展部分,想快速學習mpi4py的童鞋可以去看看。