Как передать коммуникатор MPI с python на C через cython?

Я пытаюсь обернуть функцию C, принимая дескриптор коммуникатора MPI_Comm в качестве параметра через cython. В результате я хочу иметь возможность вызывать функцию из python, передавая ей объект mpi4py.MPI.Comm. Мне интересно, как сделать преобразование из mpi4py.MPI.Comm в MPI_Comm.

Для демонстрации я использую простую функцию типа «Hello World!»:

helloworld.h:

#ifndef HELLOWORLD
#define HELLOWORLD
#include <mpi.h>

void sayhello(MPI_Comm comm);

#endif

helloworld.c:

#include <stdio.h>
#include "helloworld.h"

void sayhello(MPI_Comm comm){
    int size, rank;
    MPI_Comm_size(comm, &size);
    MPI_Comm_rank(comm, &rank);
    printf("Hello, World! "
           "I am process %d of %d.\n",
           rank, size);
}

Теперь я хочу вызвать эту функцию из python следующим образом:

from_python.py:

import mpi4py
import helloworld_wrap

helloworld_wrap.py_sayhello(mpi4py.MPI.COMM_WORLD)

Это означает, что mpirun -np 4 python2 from_python.py должен давать что-то вроде:

Привет, мир! Я процесс 0 из 4.
Привет, мир! Я процесс 1 из 4.
Привет, мир! Я процесс 2 из 4.
Привет, мир! Я процесс 3 из 4.

Но если я попытаюсь добиться этого с помощью cython следующим образом:

helloworld_wrap.pyx:

cimport mpi4py.MPI as MPI
cimport mpi4py.libmpi as libmpi

cdef extern from "helloworld.h":
   void sayhello(libmpi.MPI_Comm comm)

def py_sayhello(MPI.Comm comm):
    sayhello(comm)

а также:

setup.py:

import os
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

mpi_compile_args = os.popen("mpicc --showme:compile").read().strip().split(' ')
mpi_link_args    = os.popen("mpicc --showme:link").read().strip().split(' ')

ext_modules=[
    Extension("helloworld_wrap",
              sources            = ["helloworld_wrap.pyx", "helloworld.c"],
              language           = 'c',
              extra_compile_args = mpi_compile_args,
              extra_link_args    = mpi_link_args,
          )
]

setup(
  name = "helloworld_wrap",
  cmdclass = {"build_ext": build_ext},
  ext_modules = ext_modules
)

Я получаю следующее сообщение об ошибке:

helloworld_wrap.pyx:8:13: невозможно преобразовать объект Python в «MPI_Comm»

указывая, что mpi4py.MPI.Comm не может быть преобразовано в MPI_Comm. Итак, как я могу преобразовать mpi4py.MPI.Comm в MPI_Comm, чтобы моя оболочка заработала?


person jotasi    schedule 27.07.2018    source источник


Ответы (1)


Преобразование довольно простое, так как mpi4py.MPI.Comm-объект внутренне хранит дескриптор MPI_Comm как элемент ob_mpi1. Следовательно, если изменить последнюю строку helloworld_wrap.pyx, чтобы передать comm.ob_mpi вместо comm, модуль скомпилируется и будет работать так, как задумано:

helloworld_wrap.pyx:

cimport mpi4py.MPI as MPI
cimport mpi4py.libmpi as libmpi

cdef extern from "helloworld.h":
   void sayhello(libmpi.MPI_Comm comm)

def py_sayhello(MPI.Comm comm):
    sayhello(comm.ob_mpi)

Удивительно, но я не нашел никакой документации по этому поводу, а понял это только при изучении источники mpi4py.MPI.Comm. Я не уверен, что это предполагаемый способ справиться с этим, но я не мог заставить его работать иначе.


1 Фактически, большинство, если не все объекты в mpi4py.MPI, которые моделируют соответствующий дескриптор MPI в C, содержат соответствующий дескриптор как элемент ob_mpi.

person jotasi    schedule 27.07.2018
comment
Кроме того, по моему опыту, mpi4py в значительной степени недостаточно документировано, и часто требуется заглянуть в исходный код, чтобы понять, как все работает. - person Zulan; 27.07.2018
comment
@Zulan У меня тоже такое чувство. Я подумал, что по крайней мере задокументирую это здесь. - person jotasi; 27.07.2018