python - Cython 比 Numpy 慢(来自 Python Cookbook 的示例)

标签 python performance numpy optimization cython

该片段来自《Python Cookbook》一书。一共有三个文件。

样本.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]

设置.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

这是为什么?

提前谢谢您。

最佳答案

我可以确认您的结果(numpy 1.15 与 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)

很难说为什么 Cookbook 的作者有其他时间:其他 numpy 版本或者可能是其他编译器。对于 np.clip,除了使用 SIMD 指令之外,没有太多改进的空间。

但是,您的 Cython 代码并不是最佳的。您可以通过声明内存 View 是连续的来改进它,即 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 问题):

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。

关于python - Cython 比 Numpy 慢(来自 Python Cookbook 的示例),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55073143/

相关文章:

python - 无法通过Python发送电子邮件

python - 如何在 scrapy 中通过 CrawlerProcess 传递自定义设置?

python - 给定列表创建无向图

python - 根据向量绘制数组中的对象

python - 将 Cython 中的 numpy 数组传递给需要动态分配数组的 C 函数

python - 获取所有请求输入值

Android 通知 FCM 与简单拉取

c++ - 该程序在 main() 上的 'return;' 之后需要很长时间才能关闭

c++ - 在这 3 种从共享内存读取链表的方法中,为什么第三快?

python - 如何在Python中像正常情况一样在两个值之间生成随机Beta?