Numba vs Cython循环优化

时间:2017-12-06 21:05:21

标签: python cython numba

考虑以下四个函数(pythonnumbacythonsmart),它们在给定相同的整数输入时计算相同的响应

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

他们的执行时间我发现

有点惊讶
  1. numba的运行时独立于ncythonn中是线性的)
  2. numbasmart
  3. 这立即引发了两个问题:

    1. 为什么Numba,但不是Cython,能够把它变成一个恒定时间的算法?
    2. 鉴于Numba 设法将其转换为常量时间算法,为什么它比纯Python常量时间函数smart慢?
    3. 由于我不是汇编程序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_numbanumba提供相同的时间,比smart(纯Python)慢25% ,比{{1}慢175% }。

      这是否表明Cython在高效跨越Python /低级边界方面做得非常好,而Numba做得不好?或者还有别的东西吗?

1 个答案:

答案 0 :(得分:8)

  1. 这似乎是一个LLVM与GCC的事情 - 请参阅编译器资源管理器here中的示例,它比numba吐出的噪声更少。我在程序集中有点丢失,但相当清楚GCC输出有一个循环(jge.L6)而clang输出没有。另请参阅GCC错误跟踪器上的this issue

  2. 在我的机器上(Windows x64)numba并不比smart慢得多,只有大约9 ns。这个开销似乎是由于numba的类型调度机制 - 如果你通过选择一个特定的重载来消除它,那么numba版本比python版本更快

  3. 这是我的时间

    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)