简体   繁体   English

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

[英]Passing multiprocessing.RawArray to a C++ function

My Python application creates an array shared between processes using multiprocessing.RawArray .我的 Python 应用程序使用multiprocessing.RawArray创建一个在进程之间共享的数组。 Now to speed up computation I want to modify this array from within a C++ function.现在为了加速计算,我想从 C++ 函数中修改这个数组。 What is a safe way to pass a pointer to the underlying memory to a C++ function that accepts a void * argument?将指向底层内存的指针传递给接受void *参数的 C++ 函数的安全方法是什么?

The function is defined in a pxd file as:该函数在pxd文件中定义为:

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

My naive attempt so far:到目前为止我天真的尝试:

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

This fails Cython compilation with the following error: Cannot convert Python object to 'void *' I also tried ctypes.addressof with similar results.这导致 Cython 编译失败,并出现以下错误: Cannot convert Python object to 'void *'我也尝试过ctypes.addressof ,结果类似。

I do understand that I will need a method to query this pointer from every participating process individually, because this same region of memory will be mapped differently in process address spaces.我确实明白我需要一种方法来分别从每个参与的进程中查询这个指针,因为这个相同的内存区域将在进程地址空间中以不同的方式映射。 But this is not an issue, so far I'm just struggling to get the pointer at all.但这不是问题,到目前为止,我只是在努力获取指针。 Should I use a different approach altogether and allocate shared memory from within C++, or is it okay to do what I am doing?我应该完全使用不同的方法并从 C++ 内部分配共享内存,还是可以做我正在做的事情?

multiprocessing.RawArray is a ctypes.Array , so the address of the underlying buffer can be obtained via ctypes.addressof . multiprocessing.RawArray是一个ctypes.Array ,所以底层缓冲区的地址可以通过ctypes.addressof获得。 This address can be reinterpreted as void * .该地址可以重新解释为void * Here is an example:下面是一个例子:

%%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)

Using <void*>ctypes.addressof(buffer) won't work, because Cython has no means for automatic conversion of a PyObject to void * - the (less readable) oneliner would be <void*><size_t> ctypes.addressof(buffer) :使用<void*>ctypes.addressof(buffer)将不起作用,因为 Cython 无法将PyObject自动转换为void * -(可读性较差)oneliner 将是<void*><size_t> ctypes.addressof(buffer)

  • Cython can convert a Python-object to a raw size_t (or any integer) C-value. Cython 可以将 Python 对象转换为原始size_t (或任何整数)C 值。
  • a size_t C-value can be reinterpreted as void * in C-language.在 C 语言中, size_t C 值可以重新解释为void *

Here is a small test of above example's functionality:这是对上述示例功能的一个小测试:

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

If the signature of the C-function isn't expecting a void * but for example continuous memory of type unsigned char , so the approach from @oz1 is safer, as it not only protects data from being wrongly reinterpreted but also automatically checks that the buffer is continuous and has the right number of dimensions (done via typing as unsigned char[::1] ).如果 C 函数的签名不需要void *而是例如unsigned char类型的连续内存,那么来自 @oz1 的方法更安全,因为它不仅可以保护数据不被错误地重新解释,而且还自动检查缓冲区是连续的并且具有正确的维数(通过键入unsigned char[::1] )。

RawArray should have a buffer protocal , then it's easy to get the underlying pointer, since Cython has a good support for it via memory view , the following code should work: RawArray应该有一个缓冲区 protocal ,然后很容易获得底层指针,因为 Cython 通过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]

By your descriptions, you should have a look at Cython's support for shared memory parallelism根据您的描述,您应该看看 Cython 对共享内存并行的支持

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM