该片段来自《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/