我正在尝试使用Cython来加速一些纯Python代码。以下是Python的原始代码:
import numpy as np
def image_to_mblocks(image_component):
    img_shape = np.shape(image_component)
    v_mblocks = img_shape[0] // 16
    h_mblocks = img_shape[1] // 16
    x = image_component
    x = [x[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:] for i in range(v_mblocks) for j in range(h_mblocks)]
    return x参数image_component是一个二维Python值,其中每个维度的长度都可以被16整除。在纯numpy.ndarray中,这个函数很快--在我的机器上,使用image_component of shape (640, 480)调用100次需要80ms。然而,我需要调用这个函数数千到数万次,所以我有兴趣提高它的速度。
下面是我的Cython实现:
import numpy as np
cimport numpy as np
cimport cython
ctypedef unsigned char DTYPE_pixel
cpdef np.ndarray[DTYPE_pixel, ndim=3] image_to_mblocks(unsigned char[:, :] image_component):
    cdef int i
    cdef int j
    cdef int k = 0
    cdef int v_mblocks = image_component.shape[0] / 16
    cdef int h_mblocks = image_component.shape[1] / 16
    cdef np.ndarray[DTYPE_pixel, ndim=3] x = np.empty((v_mblocks*h_mblocks, 16, 16), dtype=np.uint8)
    for j in range(h_mblocks):
        for i in range(v_mblocks):
            x[k] = image_component[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:]
            k += 1
    return x为了支持image_component切片,Cython实现使用了类型化的MemoryView。这个Cython实现在我的机器上进行100次迭代需要250ms(与之前相同的条件:image_component是一个(640, 480)数组)。
这是我的问题:在我给出的例子中,为什么Cython的性能无法超过纯Python实现?
我相信我已经遵循了Cython documentation for working with numpy arrays中的所有步骤,但是我没有达到我所期望的性能提升。
作为参考,下面是我的setup.py文件的样子:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
import numpy
extensions = [
    Extension('proto_mpeg_computation', ['proto_mpeg_computation.pyx'],
          include_dirs=[numpy.get_include()]
          ),
]
setup(
   name = "proto_mpeg_x",
   ext_modules = cythonize(extensions)
)发布于 2017-04-21 23:51:17
性能明显下降的原因是Cython版本正在复制数据,而原始版本正在创建对现有数据的引用。
这条线
x[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:]在原始x数组上创建一个视图(即,如果更改x,则视图也将更改)。您可以通过检查从owndata函数返回的数组元素上的numpy标志为False来确认这一点。这个操作非常便宜,因为它所做的就是存储一个指针和一些形状/步幅信息。
在Cython版本中,您需要
x[k] = image_component[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:]这需要将一个16x16的数组复制到已经为x分配的内存中。它并不是非常慢,但与原始Python版本相比,它有更多的工作要做。同样,通过检查函数返回值上的owndata来确认。您应该会发现它是True。
在您的情况下,您应该考虑是否需要数据的视图或数据的副本。
在我看来,这不是Cython能帮上大忙的那种问题。Cython在索引单个元素时有一些很好的速度,但是当你开始索引切片时,它的行为方式与基本Python/numpy相同(对于这种类型的使用,这实际上是非常有效的)。
我怀疑您可以将原始Python代码放入Cython中,然后输入unsigned char[:, :]或np.ndarray[DTYPE_pixel, ndim=2]作为image_component。你也可以通过不使用x而直接返回列表理解来减少少量的引用计数。除此之外,我看不出你能有多大收获。
https://stackoverflow.com/questions/43532259
复制相似问题