我知道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中增加了很多开销。“
当numba变得有效时,很难划清界限。但是,有些指标可能无效:
jit
一起使用 - 只要你不能在nopython模式下编译它,你要么尝试编译太多,要么不会明显加快。还有另一个原因,你可能不想使用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解决方案会明显更好。
如果你不确切知道显式输入和输出声明的结果是什么让让numba决定它。根据您的输入,您可能想要使用。 (标量输出,连续输入数组)。如果使用跨步输入调用显式声明的函数,它将失败,如果您将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()
请注意,使用或numba_loop_fastmath_multi
仅在某些特殊情况下推荐使用。更常见的是,这样一个简单的函数是另一个问题的一部分,可以更有效地并行化(起始线程有一些开销)
运行此代码可以在我的机器上加速~6倍:
numba_loop_fastmath_combined(a,b)
Python:3.31μs,numba:589 ns。
至于你的问题我真的认为这与复杂性没有关系,它可能主要取决于你正在做的操作类型。另一方面,您仍然可以绘制python / numba比较,以查看给定函数的移位发生的位置。