为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

繁星点点滴滴 2021-11-30 19:27:30
该片段来自 Python Cookbook 一书。有三个文件。样本.pyxcimport 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.pyfrom distutils.core import setupfrom Cython.Build import cythonizesetup(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.00352168083190917970.00608062744140625这是为什么?
查看完整描述

1 回答

?
胡说叔叔

TA贡献1804条经验 获得超8个赞

我可以确认你的结果(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 代码不是最佳的。您可以通过声明内存视图是连续的 iedouble[::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 容易得多(参见例如这个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。


查看完整回答
反对 回复 2021-11-30
  • 1 回答
  • 0 关注
  • 177 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信