Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/346.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 键入MemoryView时Cython性能不佳_Python_Numpy_Cython - Fatal编程技术网

Python 键入MemoryView时Cython性能不佳

Python 键入MemoryView时Cython性能不佳,python,numpy,cython,Python,Numpy,Cython,我正在尝试使用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 *

我正在尝试使用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\u组件
是一个二维
numpy.ndarray
,其中每个维度的长度都可以被16整除。在纯Python中,此函数速度很快——在我的机器上,使用
image\u组件
(640480)
调用100次需要80毫秒。但是,我需要调用此函数数千到数万次,因此我有兴趣加快调用速度

以下是我的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
Cython实现使用类型化的,以便支持对
image\u组件的切片。这个Cython实现在我的机器上需要250毫秒的迭代时间(与之前相同的条件:
image\u组件
是一个
(640480)
阵列)

这里是我的问题:在我给出的示例中,为什么Cython不能超越纯Python实现

我相信我已经遵循了中的所有步骤,但是我没有实现我期望的性能提升

以下是我的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)
)

性能显著下降的原因是Cython版本正在复制数据,而原始版本正在创建对现有数据的引用

线路

x[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:]
在原始
x
数组上创建视图(即,如果更改
x
,则视图也将更改)。您可以通过检查Python函数返回的数组元素上的numpy
owndata
标志为
False
来确认这一点。这个操作非常便宜,因为它只存储一个指针和一些形状/步幅信息

在Cython版本中,您可以

x[k] = image_component[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:]
这需要将16×16阵列复制到已分配给
x
的内存中。它不是非常慢,但比原始Python版本有更多的工作要做。再次通过检查函数返回值上的
owndata
进行确认。您应该发现它是
真的

在您的情况下,您应该考虑是否需要查看数据或数据副本。


在我看来,这并不是Cython能帮上大忙的问题。Cython在索引单个元素时有一些很好的速度,但是当您开始索引切片时,它的行为方式与基本Python/numpy相同(对于这种类型的使用,它实际上非常有效)


我猜想将原始Python代码放到Cython中,并以
无符号字符[:,:]
np.ndarray[DTYPE\u pixel,ndim=2]
的形式键入
image\u component
会有一点好处。您也可以通过不使用
x
直接返回列表来减少一点点引用计数。除此之外,我看不出您可以获得多少。

性能明显较差的原因是Cython版本正在复制数据,而原始版本正在创建对现有数据的引用

线路

x[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:]
在原始
x
数组上创建视图(即,如果更改
x
,则视图也将更改)。您可以通过检查Python函数返回的数组元素上的numpy
owndata
标志为
False
来确认这一点。这个操作非常便宜,因为它只存储一个指针和一些形状/步幅信息

在Cython版本中,您可以

x[k] = image_component[i * 16:(i + 1) * 16:, j * 16:(j + 1) * 16:]
这需要将16×16阵列复制到已分配给
x
的内存中。它不是非常慢,但比原始Python版本有更多的工作要做。再次通过检查函数返回值上的
owndata
进行确认。您应该发现它是
真的

在您的情况下,您应该考虑是否需要查看数据或数据副本。


在我看来,这并不是Cython能帮上大忙的问题。Cython在索引单个元素时有一些很好的速度,但是当您开始索引切片时,它的行为方式与基本Python/numpy相同(对于这种类型的使用,它实际上非常有效)


我猜想将原始Python代码放到Cython中,并以
无符号字符[:,:]
np.ndarray[DTYPE\u pixel,ndim=2]
的形式键入
image\u component
会有一点好处。您也可以通过不使用
x
直接返回列表来减少一点点引用计数。除此之外,我看不出你能得到多少。

看看Cython吐出来的C代码通常是有帮助的(如果恼人的话)。你可能会发现它犯了一个错误,没有把循环变成真正的C循环,数组索引变成真正的C数组索引操作,等等。一旦你知道Cython->C编译器出了什么问题,你就知道该玩什么了。例如,我发现具有范围的复杂循环结构有时无法成为真正的C循环,将它们更改为旧的Pyrex样式的范围循环将强制进行正确的优化。类型化MemoryView应该加快单元素索引。我不希望切片速度加快。(奇怪的是,你有一个明显的慢下来。我希望它是类似的)额外的检查步骤:使用Cython的注释功能查看什么是正确的Cython化,使用decorators删除边界检查,使用
cdef double[::1]
类型声明。查看Cython吐出的C代码通常是有用的(如果恼人的话)。你可能会发现它犯了一个错误,没有把循环变成真正的C循环,数组索引变成真正的C数组索引操作,等等。一旦你知道Cython->C编译器出了什么问题,你就知道该玩什么了。例如,我发现具有范围的复杂循环结构有时无法成为真正的C循环,将它们更改为旧的Pyrex样式的范围循环会强制进行正确的优化