9

我需要使用各种大小的 2D numpy 数组执行大量工作,并且我想将这些计算卸载到 cython 上。这个想法是,我的 2D numpy 数组将从 python 传递到 cython,在那里它将被转换为 c 数组或内存视图,并在其他 c 级函数的级联中使用来进行计算。

经过一些分析后,由于一些严重的 python 开销,我排除了在 cython 中使用 numpy 数组。使用内存视图要快得多并且非常容易使用,但我怀疑我可以通过使用 c-arrays 来获得更多的加速。

不过,这是我的问题 - 如何在 cython 中声明 2D c 数组而不用设定值预定义其尺寸?例如,我可以通过这种方式从 numpy 创建一个 c 数组:

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]):
    ...

但这意味着我有一个固定大小的数组,在我的情况下这将是无用的。所以我尝试了这样的事情:

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 列表),但它没有为我的问题提供答案。

更新:

事实证明,如果确保为内存视图关闭 cython 中的 indexError 检查,则内存视图可以与 c 数组一样快,这可以通过使用 cython 编译器指令来完成:

# cython: boundscheck=False

感谢@Veedrac 的提示!

4

2 回答 2

10

你只需要停止做边界检查:

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 数组。你用它来获得内存视图。然后你得到一个指向它的数据的指针,你把它转换成所需步幅的指针。

于 2014-09-18T18:57:39.203 回答
1

因此,在@Veedrac 的宝贵帮助(非常感谢!)之后,我终于想出了一个脚本,该脚本演示了如何使用内存视图和 c 数组来加速 Cython 中的计算。它们都下降到相似的速度,所以我个人认为使用内存视图要容易得多。

这是一个示例 cython 脚本,它“接受”一个 numpy 数组并将其转换为内存视图或 c 数组,然后通过 c 级函数执行简单的数组求和:

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

于 2014-09-22T11:04:55.407 回答