Передача multiprocessing.RawArray в функцию C ++ - PullRequest
1 голос
/ 10 марта 2020

My Python приложение создает массив, совместно используемый процессами, используя multiprocessing.RawArray. Теперь, чтобы ускорить вычисления, я хочу изменить этот массив из функции C ++. Как безопасно передать указатель на базовую память в функцию C ++, которая принимает аргумент void *?

Функция определена в файле pxd как:

cdef extern from 'lib/lib.hpp':
    void fun(void *buffer)

Моя наивная попытка до сих пор:

buffer = multiprocessing.RawArray(ctypes.c_ubyte, 10000)
clib.fun(ctypes.cast(self.queue_obj_buffer, ctypes.c_void_p))

Сбой компиляции Cython со следующей ошибкой: Cannot convert Python object to 'void *' Я также попытался ctypes.addressof с похожими результатами.

Я понимаю, что я потребуется метод для запроса этого указателя от каждого участвующего процесса индивидуально, потому что эта же область памяти будет отображаться по-разному в адресных пространствах процесса. Но это не проблема, пока я просто изо всех сил пытаюсь получить указатель вообще. Должен ли я вообще использовать другой подход и распределять разделяемую память изнутри C ++, или можно делать то, что я делаю?

Ответы [ 2 ]

3 голосов
/ 10 марта 2020

multiprocessing.RawArray - это ctypes.Array, поэтому адрес базового буфера можно получить через ctypes.addressof. Этот адрес можно интерпретировать как void *. Вот пример:

%%cython
# a small function for testing purposes:
cdef extern from *:
    """
    unsigned char get_first(void *ptr){
       unsigned char *ptr_as_ubytes = (unsigned char *)ptr;
       return ptr_as_ubytes[0];
    }
    """
    unsigned char get_first(void *ptr)


import ctypes
def first_element(buffer):
    cdef size_t ptr_address = ctypes.addressof(buffer) # size_t is big enough to hold the address
    return get_first(<void*> ptr_address)

Использование <void*>ctypes.addressof(buffer) не будет работать, потому что у Cython нет средств для автоматического c преобразования PyObject в void * - (менее читаемый) oneliner будет <void*><size_t> ctypes.addressof(buffer):

  • Cython может преобразовать Python -объект в необработанное size_t (или любое целое) C -значение.
  • a size_t C -значение можно интерпретировать как void * в C -языке.

Вот небольшой тест функциональности вышеприведенного примера:

import multiprocessing
import ctypes
buffer = multiprocessing.RawArray(ctypes.c_ubyte, 10000)
buffer[0]=42
first_element(buffer)
# 42

Если сигнатура C -функции ожидает не void *, а, например, непрерывную память типа unsigned char, то подход от @ oz1 более безопасен, поскольку он не только защищает данные от неверной интерпретации, но и также автоматически проверяет, что буфер является непрерывным и имеет правильное количество измерений (делается с помощью ввода unsigned char[::1]).

2 голосов
/ 10 марта 2020

RawArray должен иметь протокольный буфер , тогда легко получить базовый указатель, поскольку Cython имеет хорошую поддержку для него через представление памяти , следующий код должен работать :

%%cython

import ctypes
from multiprocessing.sharedctypes import RawArray

ctypedef unsigned char ubyte

cdef void func(void* buffer, int size):
    cdef ubyte *buf = <ubyte*>buffer
    cdef int i
    for i in range(size):
        buf[i] += 1


def test():
    cdef ubyte[::1] view = RawArray(ctypes.c_ubyte, [1,2,3,4,5])
    func(<void*>&view[0], len(view))
    print(list(view))

test()  # [2, 3, 4, 5, 6]

По вашим описаниям, вы должны взглянуть на поддержку Cython для параллелизма совместно используемой памяти

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...