mpi4py Split_type 使用 openmpi 的 OMPI_COMM_TYPE_SOCKET

mpi4py Split_type using openmpi's OMPI_COMM_TYPE_SOCKET

是否可以在 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.hompi_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_SHARED0OMPI_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编译,创建一个Python文件ompi.py,值为OMPI_COMM_TYPE_SOCKET。导入它并在对 comm.Split_type():

的调用中使用它
import ompi

socketcomm = comm.Split_type(ompi.COMM_TYPE_SOCKET)

用C写一个Python模块

这有点复杂,但您可以编写一个包含 mpi.h 并将 OMPI_COMM_TYPE_SOCKET 的值导出为 Python 常量的 C 模块。参考 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 混合成一个单一的神秘语法。阅读源代码。试着弄清楚发生了什么,以及如何自己写一些东西。帮不了你。


无论您选择哪条路径,请记住,所有这些都与程序 运行 所在的系统有关,而不仅仅是程序将在其上开发的系统。