问题描述
是否可以在 mpi4py 中使用 openmpi 的 OMPI_COMM_TYPE_SOCKET 拆分通信器?
我已验证此方法有效:
from mpi4py import MPI
comm = MPI.COMM_WORLD
sharedcomm = comm.Split_type(MPI.COMM_TYPE_SHARED)
但这不会:
socketcomm = comm.Split_type(MPI.OMPI_COMM_TYPE_SOCKET)
也不这样做:
socketcomm = comm.Split_type(MPI.COMM_TYPE_SOCKET)
我查看了文档,但找不到任何相关信息。
解决方法
mpi4py
仅提供围绕标准 MPI 功能的包装器。 OMPI_COMM_TYPE_SOCKET
是 Open MPI 特定的拆分类型。如果您知道它的数值,您仍然可以在 mpi4py
中使用它,因为它只是 C enum
的成员:
/*
* Communicator split type constants.
* Do not change the order of these without also modifying mpif.h.in
* (see also mpif-common.h.fin).
*/
enum {
MPI_COMM_TYPE_SHARED,OMPI_COMM_TYPE_HWTHREAD,OMPI_COMM_TYPE_CORE,OMPI_COMM_TYPE_L1CACHE,OMPI_COMM_TYPE_L2CACHE,OMPI_COMM_TYPE_L3CACHE,OMPI_COMM_TYPE_SOCKET,// here
OMPI_COMM_TYPE_NUMA,OMPI_COMM_TYPE_BOARD,OMPI_COMM_TYPE_HOST,OMPI_COMM_TYPE_CU,OMPI_COMM_TYPE_CLUSTER
};
#define OMPI_COMM_TYPE_NODE MPI_COMM_TYPE_SHARED
作为 enum
的成员意味着 OMPI_COMM_TYPE_SOCKET
的实际数值取决于它在 enum
中的位置,因此可能因 Open MPI 的一个版本而异。您有多种选择。
硬编码值
这是最简单的选择。打开 mpi.h
(ompi_info --path incdir
给你它的位置),计算 OMPI_COMM_TYPE_SOCKET
在封闭的 enum
中的位置,从 0
开始,MPI_COMM_TYPE_SHARED
和硬编码价值。代码可能会因与您不同的 Open MPI 版本而中断。
解析mpi.h
阅读mpi.h
,搜索enum
定义并找到包含OMPI_COMM_TYPE_SOCKET
的定义。如果 MPI_COMM_TYPE_SHARED
是 0
,则 OMPI_COMM_TYPE_SOCKET
的值是枚举值序列中从 0 开始的索引。这在很大程度上取决于 mpi.h
中具有特定格式的代码,如果更改,很容易中断。
解析mpif.h
Fortran 接口更容易解析,因为那里的值定义为:
parameter (OMPI_COMM_TYPE_SOCKET=6)
这很容易用一个简单的正则表达式解析。问题是最新版本的 Open MPI 将 mpif.h
拆分为几个文件,然后将这些文件包含在 mpif.h
中,当前值在 mpif-constants.h
中。因此,您可能需要解析 include
语句并递归到它们引用的文件中。请注意,这些是 Fortran include
语句,而不是预处理器 #include
指令。
代码生成
编写一个小 C 程序,将 OMPI_COMM_TYPE_SOCKET
的值输出到 Python 文件,并将其作为程序设置过程的一部分运行。类似的东西:
#include <stdio.h>
#include <mpi.h>
int main (int argc,char **argv)
{
if (argc != 2)
{
printf("Usage: mkompimod /path/to/module.py\n");
return 1;
}
FILE *fh = fopen(argv[1],"w");
if (fh != NULL) {
fprintf(fh,"COMM_TYPE_SOCKET = %d\n",OMPI_COMM_TYPE_SOCKET);
fclose(fh);
}
return 0;
}
将其放入名为 mkompimod.c
的文件中。使用 mpicc -o mkompimod mkompimod.c
编译并使用 mkompimod /path/to/ompi.py
运行以创建值为 ompi.py
的 Python 文件 OMPI_COMM_TYPE_SOCKET
。导入它并在对 comm.Split_type()
的调用中使用它:
import ompi
socketcomm = comm.Split_type(ompi.COMM_TYPE_SOCKET)
用 C 编写 Python 模块
这有点复杂,但您可以编写一个包含 mpi.h
的 C 模块并将 OMPI_COMM_TYPE_SOCKET
的值导出为 Python 常量。请参阅 Python documentation 以了解如何用 C 编写扩展。
使用 CFFI 模块
CFFI 允许您构建 Python 模块,这些模块包装 C 库并为您编写所有胶水代码。将以下内容放入名为 ompi_build.py
的文件中:
from cffi import FFI
ffi = FFI()
ffi.set_source("ompi",r"#include <mpi.h>")
ffi.cdef(
r"""
const int OMPI_COMM_TYPE_HWTHREAD;
... more constants here ...
const int OMPI_COMM_TYPE_SOCKET;
... even more constants here ...
"""
)
if __name__ == "__main__":
ffi.compile(verbose=True)
像这样运行:
$ CC=mpicc python ompi_build.py
这将创建 C 模块 ompi.c
并将其编译为可加载的 DSO。然后您可以导入它并像这样访问常量:
from ompi.lib import OMPI_COMM_TYPE_SOCKET
socketcomm = comm.Split_type(OMPI_COMM_TYPE_SOCKET)
CFFI 提供与 Python 的 distutils
的集成,您可以让它在设置过程中自动构建 C 模块。
使用 Cython
这就是 mpi4py
本身的编写方式。它将 C 和 Python 融合到一个单一的神秘语法中。阅读源代码。试着弄清楚发生了什么以及如何自己写一些东西。帮不了你。
无论您选择哪条路径,请记住,所有这些都与运行程序的系统有关,而不仅仅是开发程序的系统。