如何在Cython中动态声明2D c数组
我需要使用各种大小的2D numpy数组执行大量工作,我想将这些计算卸载到cython上。 我的想法是我的2D numpy数组将从python传递到cython,在那里它将被转换为c-array或内存视图,并用于其他c级函数的级联中进行计算。
经过一些分析后,由于一些严重的python开销,我排除了在cython中使用numpy数组。 使用内存视图更快,更容易使用,但我怀疑我可以使用c-arrays进一步加速。
这是我的问题 – 如何在没有使用设定值预定义其尺寸的情况下在cython中声明2D c数组? 例如,我可以通过这种方式从numpy创建一个c-array:
narr = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]], dtype=np.dtype("i")) cdef int c_arr[3][4]: for i in range(3): for j in range(4): c_arr[i][j] = narr[i][j]
然后将其传递给函数:
cdef void somefunction(int c_Arr[3][4]): ...
但这意味着我有一个固定的数组sizde,在我的情况下将是无用的。 所以我尝试过这样的事情:
narr = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]], dtype=np.dtype("i")) cdef int a = np.shape(narr)[0] cdef int b = np.shape(narr)[1] cdef int c_arr[a][b]: # INCORRECT - EXAMPLE ONLY for i in range(a): for j in range(b): c_arr[i][j] = narr[i][j]
打算将它传递给这样的函数:
cdef void somefunction(int a, int b, int c_Arr[a][b]): ...
但它不起作用,编译失败,错误“不允许在常量表达式中”。 我怀疑我需要以某种方式使用malloc / free吗? 我看了一下这个问题( 如何在Cython中声明2D列表 ),但它没有提供我的问题的答案。
更新:
事实certificate,如果确保在内存视图中关闭cython中的indexError检查,则内存视图可以与c数组一样快,这可以通过使用cython编译器指令来完成:
# cython: boundscheck=False
谢谢@Veedrac的小费!
你只需要停止边界检查:
with cython.boundscheck(False): thesum += x_view[i,j]
这使得速度基本上达到了标准杆。
如果你真的想要一个C数组,请尝试:
import numpy as numpy from numpy import int32 from numpy cimport int32_t numpy_array = numpy.array([[]], dtype=int32) cdef: int32_t[:, :] cython_view = numpy_array int32_t *c_integers_array = &cython_view[0, 0] int32_t[4] *c_2d_array = c_integers_array
首先你得到一个Numpy数组。 您可以使用它来获取内存视图。 然后你得到一个指向它的数据的指针,你可以将它转换成所需步幅的指针。
所以在@Veedrac的宝贵帮助之后(非常感谢!)我终于想出了一个脚本,它演示了内存视图和c-array的使用,以加速Cython中的计算。 它们都降低到相似的速度,所以我个人认为使用内存视图更容易。
下面是一个示例cython脚本,它“接受”一个numpy数组并将其转换为内存视图或c-array,然后通过c-level函数执行简单的数组求和:
# cython: boundscheck=False cimport cython import numpy as np cimport numpy as np from numpy import int32 from numpy cimport int32_t #Generate numpy array: narr = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], dtype=np.dtype("i")) cdef int a = np.shape(narr)[0] cdef int b = np.shape(narr)[1] cdef int i, j testsum = np.sum(narr) print "Test summation: np.sum(narr) =", testsum #Generate the memory view: cdef int [:,:] x_view = narr #Generate the 2D c-array and its pointer: cdef: int32_t[:, :] cython_view = narr int32_t *c_integers_array = &cython_view[0, 0] int32_t[4] *c_arr = c_integers_array def test1(): speed_test_mview(x_view) def test2(): speed_test_carray(&c_arr[0][0], a, b) cdef int speed_test_mview(int[:,:] x_view): cdef int n, i, j, thesum # Define the view: for n in range(10000): thesum = 0 for i in range(a): for j in range(b): thesum += x_view[i, j] cdef int speed_test_carray(int32_t *c_Arr, int a, int b): cdef int n, i, j, thesum for n in range(10000): thesum = 0 for i in range(a): for j in range(b): thesum += c_Arr[(i*b)+j]
然后使用ipython shell时序测试显示类似的速度:
import testlib as t Test summation: np.sum(narr) = 136 %timeit t.test1() 10000000 loops, best of 3: 46.3 ns per loop %timeit t.test2() 10000000 loops, best of 3: 46 ns per loop
哦,为了比较 – 在这个例子中使用numpy数组需要125毫秒(未显示)。