我需要使用各种大小的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),但它没有解决我的问题。
事实证明,如果确保在内存视图中关闭cython中的indexError检查,那么内存视图可以和c-array一样快,这可以通过使用cython编译器指令来完成:
# cython: boundscheck=False
感谢@Veedrac提示!
答案 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毫秒(未显示)。