如何在Cython中动态声明2D c数组

时间:2014-09-18 16:12:04

标签: c numpy cython dynamic-arrays memoryview

我需要使用各种大小的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]):
    ...

但它不起作用,编译失败并出现错误"不允许在常量表达式中使用#34;。我怀疑我需要以某种方式使用malloc / free吗?我看了一下这个问题(How to declare 2D list in Cython),但它没有解决我的问题。

UPDATE:

事实证明,如果确保在内存视图中关闭cython中的indexError检查,那么内存视图可以和c-array一样快,这可以通过使用cython编译器指令来完成:

# cython: boundscheck=False

感谢@Veedrac提示!

2 个答案:

答案 0 :(得分:7)

你只需要停止边界检查:

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 = <int32_t[4] *>c_integers_array

首先你得到一个Numpy数组。您可以使用它来获取内存视图。然后你得到一个指向它的数据的指针,你可以将它转换成所需步幅的指针。

答案 1 :(得分:1)

所以在@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 = <int32_t[4] *>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毫秒(未显示)。