Cython 如何使用 memcpy 从 numpy 缓冲区复制到 C 结构?

Cython how to copy from a numpy buffer to C struct using memcpy?

此示例在 Jupyter 笔记本中编译和运行并演示了该问题。

我在这行有问题,

    memcpy(line.points, <point_t *> temp.data, sizeof(point_dtype) * n)

我希望将缓冲区从 numpy 数组 temp 复制到 C 结构 line.

中的 points 缓冲区

我希望 points 的值为 1.0,但它们是垃圾,基本上是未初始化的内存。我做错了什么!?

%%cython -a

import numpy as np
cimport numpy as cnp
from libc.stdlib cimport malloc
from libc.string cimport memcpy

cdef struct point:
    cnp.float64_t x
    cnp.float64_t y
ctypedef point point_t


cdef struct line:
    point_t * points
ctypedef line line_t


point_dtype = np.dtype([
    ("x", np.float64),
    ("y", np.float64)
])

cdef line_t * make_line():
    """ Make a line of 3 points. Internally does the creation using Numpy 
        and memcpy the result to the line_t C struct.
    """

    # The number of points in the array
    n = 3
    
    cdef cnp.ndarray points = np.empty(n, dtype=point_dtype)
    points["x"] = 0.0
    points["y"] = 0.0

    # Dynamically allocate a line C struct
    line = <line_t*> malloc( sizeof(line_t) )

    # Dynamically allocate space for "n" points
    line.points = <point_t*> malloc( sizeof(point_t) * n)

    # In this toy example we will modify "points" in a temporary array
    # this is closer to what I'm trying to achieve in my actual code.
    temp = np.empty(n, dtype=point_dtype)
    temp[:] = points[:]
    temp["x"] += 1.0
    temp["y"] += 1.0

    # Memory copy from the array's buffer into the struct
    memcpy(line.points, <point_t *> temp.data, sizeof(point_dtype) * n)

    print(line.points[0])
    # {'x': 5e-324, 'y': 4.6451830626356e-310}
    # 
    # !!!! Expected !!!!
    # {'x': 1.0, 'y': 1.0}
    
    # Assert fails
    assert line.points[0].x == 1.0
    assert line.points[0].y == 1.0


def test_create_line():
    make_line()

实际错误是:

cdef cnp.ndarray temp = np.empty(n, dtype=point_dtype)

没有 cdef cnp.ndarray 然后 temp.data 是某种 Python 对象(不确定到底是什么),然后将其转换为 point_t* 等复制从它失败。

我认为原则上你应该将 Point 定义为 cdef packed struct Point 因为我认为 Numpy 数据是在内部打包的。在这种情况下,我认为这没有什么不同。

使用内存视图可能更好,然后您可以断言数组的 C 连续性。

cdef point_t[::1] temp_view = temp

# Memory copy from the array's buffer into the struct
memcpy(line.points, &temp_view[0], sizeof(point_dtype) * n)

在这种情况下,我不会打扰输入 temppoints,因为它确实没有任何优势。 memoryview 方法的好处在于它避免了强制转换(并且强制转换帮助掩盖了你的真正错误)并且它包括一些检查你关于 data-size/layout 和连续性的假设是否正确。