Cython与NumPy数组memoryview相比C数组的性能较差(更差)

3

我在基准测试中得到了一个相当奇怪的结果。

这些都是不同风味的冒泡排序实现,而在n = 10 ^ 4时最快的方法是将Python列表内部转换为C数组。相比之下,黄线对应的代码使用了具有memoryview的NumPy数组。我预计结果会相反。我(和同事们)多次重复了基准测试,总是得到相同的结果。也许有人知道这里发生了什么...

enter image description here

在绘图中的黑线对应于以下代码:
%%cython
cimport cython
from libc.stdlib cimport malloc, free

def cython_bubblesort_clist(a_list):
    """ 
    The Cython implementation of bubble sort with internal
    conversion between Python list objects and C arrays.

    """
    cdef int *c_list
    c_list = <int *>malloc(len(a_list)*cython.sizeof(int))
    cdef int count, i, j # static type declarations
    count = len(a_list)

    # convert Python list to C array
    for i in range(count):
        c_list[i] = a_list[i]

    for i in range(count):
        for j in range(1, count):
            if c_list[j] < c_list[j-1]:
                c_list[j-1], c_list[j] = c_list[j], c_list[j-1]

    # convert C array back to Python list
    for i in range(count):
        a_list[i] = c_list[i]

    free(c_list)
    return a_list

将粉色线添加到此代码中:
%%cython
import numpy as np
cimport numpy as np
cimport cython
def cython_bubblesort_numpy(long[:] np_ary):
    """ 
    The Cython implementation of bubble sort with NumPy memoryview.

    """
    cdef int count, i, j # static type declarations
    count = np_ary.shape[0]

    for i in range(count):
        for j in range(1, count):
            if np_ary[j] < np_ary[j-1]:
                np_ary[j-1], np_ary[j] = np_ary[j], np_ary[j-1]

    return np.asarray(np_ary)

1
在你的基准测试中,你将深拷贝部分包含在计时记录中。虽然对于每个测试来说都是相同的,但为什么不将其移出计时调用呢?为什么你期望numpy在这种情况下更快呢?它的优势不在于访问每个元素,而在于执行整个数组操作。 - Midnighter
1
你看过以下指南了吗,在这里和之后 - Midnighter
3
添加这些装饰器可以获得相同的性能。 @cython.boundscheck(False) @cython.wraparound(False) - M4rtini
谢谢,那个有效 :) - user2489252
2个回答

3

如上面的评论所建议的,我添加了装饰器。

%%cython
import numpy as np
cimport numpy as np
cimport cython
@cython.boundscheck(False) 
@cython.wraparound(False)
cpdef cython_bubblesort_numpy(long[:] np_ary):
    """ 
    The Cython implementation of bubble sort with NumPy memoryview.

    """
    cdef int count, i, j # static type declarations
    count = np_ary.shape[0]

    for i in range(count):
        for j in range(1, count):
            if np_ary[j] < np_ary[j-1]:
                np_ary[j-1], np_ary[j] = np_ary[j], np_ary[j-1]

    return np.asarray(np_ary)

而且现在的结果更符合我的预期 :)

在这里输入图片描述


1

值得尝试对代码进行微小改动,以查看是否能进一步改善:

cpdef cython_bubblesort_numpy(long[::1] np_ary):
    # ...

这告诉cython np_ary是一个C连续数组,嵌套的for循环中生成的代码可以进一步通过此信息进行优化。
这段代码不接受非连续数组作为参数,但使用numpy.ascontiguousarray()处理相对容易。

谢谢!但是它无法编译,似乎不喜欢 if np_ary[j] < np_ary[j-1]: 这一行,我有什么遗漏吗? - user2489252
编译错误信息是什么,您使用的Cython版本是多少?我能够在这里成功编译。 - lothario
版本0.20.1,我把复制的内容发布在这里:http://pastebin.com/BL1Vj9ic 但我感觉我漏掉了什么... - user2489252
没关系,我不知道为什么,但我刚刚发现我的代码中有一个 long[::-1] 而不是 long[::1] ;) - user2489252
基准测试的结果表明,它确实使其更快,但在样本大小为10^6时甚至没有提高1%。 - user2489252

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接