考虑以下四个函数(python
,numba
,cython
和smart
),它们在给定相同的整数输入时计算相同的响应
def python(n):
total = 0
for m in range(1,n+1):
total += m
return total
from numba import jit
numba = jit(python)
cpdef int cython(int n):
cdef int total = 0
cdef int m
for m in range(1, n+1):
total += m
return total
def smart(n):
return n * (n + 1) // 2
他们的执行时间我发现
有点惊讶numba
的运行时独立于n
(cython
在n
中是线性的)numba
比smart
这立即引发了两个问题:
smart
慢?由于我不是汇编程序maven,查看生成的代码并没有真正给我一些线索,除此之外,Numba生成的中间LLVM代码仍然出现(尽管我可能误解了)包含循环...我在x64中失去了绝望,最终由此产生。 (除非有人问,否则我不会发布生成的代码,因为它们相当长。)
我在x64 Linux上,在Jupyter笔记本中运行它,所以我怀疑Cython使用的是用于编译Python的GCC 4.4.7;和llvmlite 0.20.0,这意味着LLVM 4.0.x。
我也有时间
smart_numba = jit(smart)
和
cpdef int smart_cython(int n):
return n * (n + 1) // 2
smart_numba
和numba
提供相同的时间,比smart
(纯Python)慢25% ,比{{1}慢175% }。
这是否表明Cython在高效跨越Python /低级边界方面做得非常好,而Numba做得不好?或者还有别的东西吗?
答案 0 :(得分:8)
这似乎是一个LLVM与GCC的事情 - 请参阅编译器资源管理器here中的示例,它比numba吐出的噪声更少。我在程序集中有点丢失,但相当清楚GCC输出有一个循环(jge
到.L6
)而clang输出没有。另请参阅GCC错误跟踪器上的this issue。
在我的机器上(Windows x64)numba
并不比smart
慢得多,只有大约9 ns。这个开销似乎是由于numba的类型调度机制 - 如果你通过选择一个特定的重载来消除它,那么numba版本比python版本更快
这是我的时间
In [73]: %timeit numba_sum(10000)
182 ns ± 1.69 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [74]: %timeit smart(10000)
171 ns ± 2.26 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
# pick out int64 overload
i64_numba_sum = numba_sum.get_overload((numba.int64,))
In [75]: %timeit i64_numba_sum(10000)
94 ns ± 1.41 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)