Cython内存视图可以变慢吗?

时间:2019-04-01 20:10:15

标签: python numpy cython

我有一些代码只需要对2D numpy数组的行的子集运行计算。例如,如果数组说:

[[0.5, 0.5, 0.5]
[0.9, 0.2, 0.8]
[0.9, 0.2, 0.8],
[0.9, 0.2, 0.2]]

我可能只想使用第一行和第三行。如果要排除大多数行,遍历数组比进行矢量化计算要快。

Cython可以加快迭代速度,但是一些实验似乎表明,使用较新的“ memoryviews” API会导致大量额外的分配(我认为是内存视图包装对象?),这使其速度比旧的np慢得多。 .ndarray API,在某些情况下会比普通Python慢​​。

这是一个最小的例子,说明了这一点:

在文件my_cython.pyx中:

# cython: profile=True

import numpy as np
cimport numpy as np

def cython_test1(
    np.ndarray vectors,
    np.ndarray target_vector):

    cdef Py_ssize_t i

    for i in range(1000000):
        x = np.dot(target_vector, vectors[i])

def cython_test2(
    np.float32_t[:,:] vectors,
    np.float32_t[:] target_vector):

    cdef Py_ssize_t i

    for i in range(1000000):
        x = np.dot(target_vector, vectors[i])

从ipython REPL进行基准测试:

from my_cython import cython_test1, cython_test2
import numpy as np 
vectors = np.random.random((1000000, 100)).astype(np.float32)
target_vector = np.random.random(100).astype(np.float32)
%timeit cython_test1(vectors, target_vector)
%timeit cython_test2(vectors, target_vector)

cython_test1: 462 ms ± 2.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

cython_test2: 1.23 s ± 8.82 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

这是显示问题的简化示例,我知道在这种情况下,在numpy数组的每个单个元素上依次运行点积显然是没有意义的。

在附加了探查器的情况下运行:

import pstats, cProfile 
cProfile.runctx("cython_test2(vectors, target_vector)", globals(), locals(), "Profile.prof") 
s = pstats.Stats("Profile.prof") 
s.strip_dirs().sort_stats("time").print_stats() 

结果:

1    2.070    2.070    3.263    3.263 speedy.pyx:52(cython_test2)
2000000    0.705    0.000    0.867    0.000 stringsource:995(memoryview_fromslice)
4000000    0.155    0.000    0.155    0.000 stringsource:514(__getbuffer__)
2000002    0.090    0.000    0.090    0.000 stringsource:372(__dealloc__)
2000002    0.082    0.000    0.082    0.000 stringsource:345(__cinit__)
2000000    0.081    0.000    0.081    0.000 stringsource:972(__dealloc__)
2000000    0.080    0.000    0.080    0.000 stringsource:555(__get__)
...

也许它正在为从2D矩阵访问的每个1D向量创建一个新的切片视图?如果是这样,答案可能是“在此特定实例中不要使用内存视图”。但这与文档有关,后者表示更新的API速度更快,并表明它总是更可取。

0 个答案:

没有答案
相关问题