如何在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毫秒(未显示)。