当numba有效吗?

问题描述 投票:1回答:3

我知道numba会产生一些开销,在某些情况下(非密集计算)它会变得比纯python慢​​。但我不知道的是在哪里划线。是否可以使用算法复杂度的顺序来确定在哪里?

例如,添加两个数组(~O(n))短于此代码中的5纯python更快:

def sum_1(a,b):
    result = 0.0
    for i,j in zip(a,b):
            result += (i+j)
    return result

@numba.jit('float64[:](float64[:],float64[:])')
def sum_2(a,b):
    result = 0.0
    for i,j in zip(a,b):
            result += (i+j)
    return result

# try 100
a = np.linspace(1.0,2.0,5)
b = np.linspace(1.0,2.0,5)
print("pure python: ")
%timeit -o sum_1(a,b)
print("\n\n\n\npython + numba: ")
%timeit -o sum_2(a,b)

更新:我正在寻找的是类似于qazxsw poi的指南:

“一般指导原则是针对不同的数据大小和算法选择不同的目标。”cpu“目标适用于小数据大小(大约小于1KB)和低计算强度算法。它具有最少的开销。”并行“目标适用于中等数据大小(大约小于1MB)。线程增加了一个小延迟。”cuda“目标适用于大数据大小(大约1MB)和高计算强度算法。转移内存到从GPU中增加了很多开销。“

python python-3.x performance numba
3个回答
3
投票

当numba变得有效时,很难划清界限。但是,有些指标可能无效:

  • 如果你不能将herejit一起使用 - 只要你不能在nopython模式下编译它,你要么尝试编译太多,要么不会明显加快。
  • 如果你不使用数组 - 当你处理传递给numba函数的列表或其他类型时(除了其他numba函数),numba需要复制这些会产生很大的开销。
  • 如果已经有一个NumPy或SciPy函数可以做到这一点 - 即使numba对于短数组来说明显更快,对于更长的数组来说几乎总是那么快(你也可能很容易忽略一些常见的边缘情况)。

还有另一个原因,你可能不想使用numba,因为它比其他解决方案“快一点”:Numba函数必须提前编译或在第一次调用时编译,在某些情况下编译将会即使你称之为数百次,也要慢得多。此外,编译时间加起来:numba导入速度慢,编译numba函数也增加了一些开销。如果导入开销增加1-10秒,则刮掉几毫秒是没有意义的。

numba的安装也很复杂(至少没有conda)所以如果你想分享你的代码,那么你就有了“非常依赖”。


您的示例缺乏与NumPy方法和高度优化的纯Python版本的比较。我添加了一些比较函数并做了一个基准测试(使用我的库nopython=True):

simple_benchmark

import numpy as np import numba as nb from itertools import chain def python_loop(a,b): result = 0.0 for i,j in zip(a,b): result += (i+j) return result @nb.njit def numba_loop(a,b): result = 0.0 for i,j in zip(a,b): result += (i+j) return result def numpy_methods(a, b): return a.sum() + b.sum() def python_sum(a, b): return sum(chain(a.tolist(), b.tolist())) from simple_benchmark import benchmark, MultiArgument arguments = { 2**i: MultiArgument([np.zeros(2**i), np.zeros(2**i)]) for i in range(2, 17) } b = benchmark([python_loop, numba_loop, numpy_methods, python_sum], arguments, warmups=[numba_loop]) %matplotlib notebook b.plot()

是的,对于小型阵列,numba函数速度最快,但对于较长的阵列,NumPy解决方案会稍微快一些。 Python解决方案速度较慢,但​​“更快”的替代方案已经比原始提议的解决方案快得多。

在这种情况下,我只是简单地使用NumPy解决方案,因为它简短,可读且快速,除非你处理大量短阵列并且多次调用函数 - 那么numba解决方案会明显更好。


2
投票

如果你不确切知道显式输入和输出声明的结果是什么让让numba决定它。根据您的输入,您可能想要使用enter image description here。 (标量输出,连续输入数组)。如果使用跨步输入调用显式声明的函数,它将失败,如果您将Numba完成工作,它将只是重新编译。如果不使用'float64(float64[::1],float64[::1])',也无法使用SIMD,因为它会改变结果的精度。

计算至少4个部分和(256位向量)并计算这些部分和的总和在这里是优选的(Numpy也不计算朴素和)。

使用MSeiferts优秀基准实用程序的示例

fastmath=True

import numpy as np import numba as nb from itertools import chain def python_loop(a,b): result = 0.0 for i,j in zip(a,b): result += (i+j) return result @nb.njit def numba_loop_zip(a,b): result = 0.0 for i,j in zip(a,b): result += (i+j) return result #Your version with suboptimal input and output (prevent njit compilation) declaration @nb.jit('float64[:](float64[:],float64[:])') def numba_your_func(a,b): result = 0.0 for i,j in zip(a,b): result += (i+j) return result @nb.njit(fastmath=True) def numba_loop_zip_fastmath(a,b): result = 0.0 for i,j in zip(a,b): result += (i+j) return result @nb.njit(fastmath=True) def numba_loop_fastmath_single(a,b): result = 0.0 size=min(a.shape[0],b.shape[0]) for i in range(size): result += a[i]+b[i] return result @nb.njit(fastmath=True,parallel=True) def numba_loop_fastmath_multi(a,b): result = 0.0 size=min(a.shape[0],b.shape[0]) for i in nb.prange(size): result += a[i]+b[i] return result #just for fun... single-threaded for small arrays, #multithreaded for larger arrays @nb.njit(fastmath=True,parallel=True) def numba_loop_fastmath_combined(a,b): result = 0.0 size=min(a.shape[0],b.shape[0]) if size>2*10**4: result=numba_loop_fastmath_multi(a,b) else: result=numba_loop_fastmath_single(a,b) return result def numpy_methods(a, b): return a.sum() + b.sum() def python_sum(a, b): return sum(chain(a.tolist(), b.tolist())) from simple_benchmark import benchmark, MultiArgument arguments = { 2**i: MultiArgument([np.zeros(2**i), np.zeros(2**i)]) for i in range(2, 19) } b = benchmark([python_loop, numba_loop_zip, numpy_methods,numba_your_func, python_sum,numba_loop_zip_fastmath,numba_loop_fastmath_single,numba_loop_fastmath_multi,numba_loop_fastmath_combined], arguments, warmups=[numba_loop_zip,numba_loop_zip_fastmath,numba_your_func,numba_loop_fastmath_single,numba_loop_fastmath_multi,numba_loop_fastmath_combined]) %matplotlib notebook b.plot()

请注意,使用Resultsnumba_loop_fastmath_multi仅在某些特殊情况下推荐使用。更常见的是,这样一个简单的函数是另一个问题的一部分,可以更有效地并行化(起始线程有一些开销)


1
投票

运行此代码可以在我的机器上加速~6倍:

numba_loop_fastmath_combined(a,b)

Python:3.31μs,numba:589 ns。

至于你的问题我真的认为这与复杂性没有关系,它可能主要取决于你正在做的操作类型。另一方面,您仍然可以绘制python / numba比较,以查看给定函数的移位发生的位置。

© www.soinside.com 2019 - 2024. All rights reserved.