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