【问题标题】:Python/Cython: Overhead with classes stored in numpy arraysPython/Cython:存储在 numpy 数组中的类的开销
【发布时间】:2014-06-14 00:36:30
【问题描述】:

这种缓慢的代码可以通过改变结构来改善,但有时很难解决。我认为原因来自存储在数组中的类。我听说内存视图用于链接 python 和 c 数组,但我对此还是很陌生(只有一些 python 知识)。

有没有办法有效地做到以下几点?

一个示例类:

cdef class ClassWithAdditionFunction:
    cdef double value

    def __init__(self, double value):
        self.value = value

    cpdef add_one(self):
        self.value += 1

一个缓慢的函数:

cdef unsigned long int i, ii
cdef unsigned long int loops = pow(10, 8)
cdef double value

addition_classes = np.array([None] * 10)

for i in range(len(addition_classes)):
    addition_classes[i] = ClassWithAdditionFunction(value=0)

for i in range(loops/10):
    for ii in range(10):
        addition_classes[ii].add_one()

非常感谢您的任何建议!

【问题讨论】:

    标签: python performance function optimization cython


    【解决方案1】:

    您可以做一些小事,应该会有所帮助。真正要加速的代码行是addition_classes[ii].add_one()。如果您使用cython -a 查看幕后实际发生的事情,您会看到您正在调用 Pyx_GetItemInt,然后是 PyObject_GetAttr,然后是 PyObject_Call。您希望构建代码以避免这 3 个调用。

    为了避免 GetItem 调用,您需要使用 numpy 的缓冲区接口或内存视图。这告诉 cython 数组的结构,并允许它更有效地从数组中提取项目。在下面的示例中,我使用了内存视图。如果您执行类似操作,请确保该数组实际上是一个充满 ClassWithAdditionFunction 实例的数组,否则您可能会遇到段错误。

    为避免 GetAttr 调用,请声明一个 ClassWithAdditionFunction 类型的变量并对该变量进行方法调用,这样 cython 就知道该变量具有可用于更快调用的方法的编译版本。

    最后,您已经使用 cpdef 方法定义了 add_one,但我建议您也添加一个返回类型。通常我们可以只放 void,但是因为这是一个 cpdef 函数而不是一个 cdef 函数,所以你可以使用 int 来代替。

    如果你把所有这些放在一起,它应该看起来像:

    import numpy as np
    cimport cython
    
    cdef class ClassWithAdditionFunction:
        cdef double value
    
        def __init__(self, double value):
            self.value = value
    
        cpdef int add_one(self):
            self.value += 1
            return 0
    
    @cython.boundscheck(False)
    @cython.wraparound(False)
    def main():
    
        cdef:
            unsigned long int i, ii, loops = 10 ** 6
            ClassWithAdditionFunction addInstance
            double value, y
    
        addition_classes = np.array([None] * 10)
        cdef ClassWithAdditionFunction[:] arrayview = addition_classes
    
        for i in range(len(addition_classes)):
            addition_classes[i] = ClassWithAdditionFunction(value=0)
    
        for i in range(loops/10):
            for ii in range(10):
                addInstance = arrayview[ii]
                addInstance.add_one()
    
        return None
    

    【讨论】:

    • 这出奇的快!非常感谢!
    • @user3465201 您可以点击左侧箭头接受答案...
    猜你喜欢
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多