将 multiprocessing.RawArray 传递给 C++ 函数

Passing multiprocessing.RawArray to a C++ function

我的 Python 应用程序使用 multiprocessing.RawArray 创建了一个在进程之间共享的数组。现在为了加快计算速度,我想从 C++ 函数中修改这个数组。将指向底层内存的指针传递给接受 void * 参数的 C++ 函数的安全方法是什么?

该函数在 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++ 中分配共享内存,还是可以做我正在做的事情?

RawArray 应该有一个 buffer protocal, then it's easy to get the underlying pointer, since Cython has a good support for it via memory view,下面的代码应该可以工作:

%%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对shared memory parallelism

的支持

multiprocessing.RawArray 是一个 ctypes.Array, so the address of the underlying buffer can be obtained via 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 无法将 PyObject 自动转换为 void * -(可读性较差的)oneliner 将是 <void*><size_t> ctypes.addressof(buffer):

  • Cython 可以将 Python 对象转换为原始 size_t(或任何整数)C 值。
  • a size_t C 值在 C 语言中可以重新解释为 void *

下面是对上述示例功能的小测试:

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] 来完成)。