【问题标题】:Cython is slower than Numpy (example from Python Cookbook)Cython 比 Numpy 慢(来自 Python Cookbook 的示例)
【发布时间】:2019-07-31 01:43:57
【问题描述】:

sn-p 来自 Python Cookbook。共有三个文件。

sample.pyx

cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)

cpdef clip(double[:] a, double min, double max, double[:] out):

    if min > max:
        raise ValueError('min must be <= max')

    if a.shape[0] != out.shape[0]:
        raise ValueError('input and output arrays must be the same size!')

    for i in range(a.shape[0]):
        if a[i] < min:
            out[i] = min
        elif a[i] > max:
            out[i] = max
        else:
            out[i] = a[i]

setup.py

from distutils.core import setup

from Cython.Build import cythonize

setup(ext_modules=cythonize("sample.pyx"))

main.py 作为测试文件

b = np.random.uniform(-10, 10, size=1000000)

a = np.zeros_like(b)


since = time.time()
np.clip(b, -5, 5, a)
print(time.time() - since)

since = time.time()
sample.clip(b, -5, 5, a)
print(time.time() - since)

令人惊讶的是,Numpy 的运行速度比 Cython 代码快 2 倍,而本书则相反。 我机器上的性能是:

0.0035216808319091797
0.00608062744140625

这是为什么呢?

提前谢谢你。

【问题讨论】:

  • 如果您将性能数据包含在记录中,那就太好了。
  • 在代码上运行分析器,看看它在哪里花费时间。
  • 如果可能的话,请你给我更多关于探查器的细节吗?@KlausD.
  • 我个人使用kernprof 进行逐行分析。

标签: python performance numpy optimization cython


【解决方案1】:

我可以确认你的结果(numpy 1.15 vs Cython 0.28.3 + gcc-5.4):

>>>  %timeit sample.clip(b, -5, 5, a)
20.5 ms ± 230 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>>  %timeit np.clip(b, -5, 5, a)
11.7 ms ± 312 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

很难说,为什么食谱的作者得到了其他时间:其他 numpy 版本或其他编译器。在np.clip 的情况下,除了使用 SIMD 指令外,没有太大的改进空间。

但是,您的 Cython 代码不是最优的。您可以通过声明内存视图是连续的,即double[::1] 而不是double[:] 来改进它。这会产生一个 cythonized C 代码,它更容易为编译器优化(有关更多信息,请参阅SO-question):

cpdef clip2(double[::1] a, double min, double max, double[::1] out):
   ....

>>>  %timeit sample.clip2(b, -5, 5, a)
11.1 ms ± 69.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

这大约与 numpy 版本一样快。

但是,为了获得最佳结果,我建议 Numba:使用 Numba 比使用 Cython 更容易获得更好的性能(例如,参见 this SO-question):

import numba as  nb  
@nb.njit
def nb_clip(a, min, max, out):

    if min > max:
        raise ValueError('min must be <= max')

    if a.shape[0] != out.shape[0]:
        raise ValueError('input and output arrays must be the same size!')

    for i in range(a.shape[0]):
        if a[i] < min:
            out[i] = min
        elif a[i] > max:
            out[i] = max
        else:
            out[i] = a[i]

 ...
 %timeit nb_clip(b, -5, 5, a)
 4.7 ms ± 333 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numba 和原始 Cython 版本之间的性能差异在于,在这种特殊情况下,clang(这是 Numba 用于编译的)能够生成比 gcc 更好的汇编程序。当我在 Cython 中切换到 clang-5.0 时,我可以匹配(甚至略微击败)Numba。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    • 2014-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 2013-06-06
    相关资源
    最近更新 更多