【问题标题】:Cython how to copy from a numpy buffer to C struct using memcpy?Cython 如何使用 memcpy 从 numpy 缓冲区复制到 C 结构?
【发布时间】:2021-02-27 18:24:12
【问题描述】:

此示例在 Jupyter notebook 中编译并运行并演示了问题。

我在这一行遇到了问题,

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

我希望将缓冲区从 numpy 数组 temp 复制到 C 结构 line 中的 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()

【问题讨论】:

    标签: numpy cython memcpy


    【解决方案1】:

    实际的错误是:

    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 方法的好处在于它避免了强制转换(并且强制转换有助于掩盖您的真正错误),并且它包括一些检查,以确保您对数据大小/布局和连续性的假设是正确的。

    【讨论】:

    • 非常感谢!我被这件事难住了好几个小时。两个快速的问题:1) 你介意解释一下&amp;temp_view[0] 部分,它是“获取内存视图中第一个值的地址”,2) 我刚刚检查了 cython 文档,但它编译时似乎没有选项可以帮助解决这个问题,例如,像 "require types" 这样的 Cython 选项
    • 1) 确实是“取内存视图中第一个值的地址”。 2)我认为没有编译器选项要求输入所有内容。部分问题在于,任何时候你使用 &lt;&gt; 演员表时,你都是在说“我知道得更好”,如果你错了,那么所有的赌注都被取消了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-29
    • 1970-01-01
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    相关资源
    最近更新 更多