【问题标题】:mpi4py Split_type using openmpi's OMPI_COMM_TYPE_SOCKETmpi4py Split_type 使用 openmpi 的 OMPI_COMM_TYPE_SOCKET
【发布时间】:2021-01-25 16:28:50
【问题描述】:

是否可以在 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)

我查看了文档,但找不到任何关于此的内容。

【问题讨论】:

    标签: mpi mpi4py


    【解决方案1】:

    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_SHARED0,则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 运行以创建一个Python 文件ompi.py,其值为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 允许您构建包装 C 库的 Python 模块并为您编写所有胶水代码。将以下内容放入名为 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 混合成一个单一的神秘语法。阅读源代码。试着弄清楚发生了什么以及如何自己写一些东西。帮不了你。


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

    【讨论】:

    • 非常有趣! MPI-4(现已批准!)将允许更多的拆分类型,但它们将取决于实现。
    猜你喜欢
    • 2017-10-07
    • 1970-01-01
    • 2014-10-27
    • 1970-01-01
    • 1970-01-01
    • 2012-01-05
    • 2018-09-26
    • 2018-08-21
    • 1970-01-01
    相关资源
    最近更新 更多