我正在尝试使用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
是一个二维numpy.ndarray
,其中每个维度的长度可以被16整除。在纯Python中,这个函数很快 - 在我的机器上,100个调用形状image_component
的{{1}}需要80毫秒。但是,我需要将这个函数调用数千到数万次,所以我有兴趣加快它的速度。
这是我的Cython实现:
(640, 480)
Cython实现使用类型化的MemoryView来支持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实现在我的机器上需要250毫秒进行100次迭代(与以前相同的条件:image_component
是image_component
数组)。
这是我的问题:在我给出的示例中,为什么Cython无法胜过纯Python实现?
我相信我已经遵循Cython documentation for working with numpy arrays中的所有步骤,但我未能达到我期待的性能提升。
供参考,这是我的setup.py文件的样子:
(640, 480)
答案 0 :(得分:2)
性能明显较差的原因是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 x 16阵列复制到已为x
分配的内存中。它不是超慢,但还有比原始Python版本更多的工作要做。再次,通过检查函数返回值的owndata
来确认。你应该发现它是True
。
在您的情况下,您应该考虑是否需要查看数据或数据副本。
这不是Cython在我看来会有很多帮助的问题。 Cython有一些很好的加速索引单个元素,但是当你开始索引切片时,它的行为与基础Python / numpy相同(这对于这种类型的使用实际上非常有效)。
我怀疑您将原始Python代码放入Cython并将image_component
键入unsigned char[:, :]
或np.ndarray[DTYPE_pixel, ndim=2]
会获得一点点收益。您还可以通过不使用x
并直接返回列表推导来减少一小部分引用计数。除此之外,我不知道你将如何获得更多。