Python/Cython:类存储在numpy数组中的开销



这个缓慢的代码可以通过改变结构来改进,但是有时很难解决这个问题。我认为,原因来自于存储在数组中的类。我听说内存视图用于链接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()

非常感谢您的建议!

你可以做一些小事,应该会有所帮助。你真正想要加速的代码行是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

相关内容

  • 没有找到相关文章

最新更新