纯python上的Numba与numpy python上的Numpa
使用numba比使用纯python生成的程序快得多:纯python上的Numba与numpy python上的Numpa,python,performance,numpy,numba,Python,Performance,Numpy,Numba,使用numba比使用纯python生成的程序快得多: 现在看来,纯python上的numba甚至(大多数时候)比numpy python更快,例如 根据numba的说法,在纯python代码上使用的速度比在使用numpy的python代码上使用的速度快。这通常是真的吗?为什么 在本文中,我们解释了为什么纯python上的numba比numpy python更快:numba比numpy看到更多的代码,并且有更多的方法来优化代码,numpy只看到一小部分 这回答了我的问题吗?在使用nump
这回答了我的问题吗?在使用numpy时,我是否会妨碍numba充分优化代码,因为numba被迫使用numpy例程,而不是找到一种更为优化的方法?我曾希望numba会意识到这一点,如果这是无益的,就不要使用numpy程序。然后它将使用numpy例程,只是这是一个改进(毕竟numpy经过了很好的测试)。毕竟在回答具体问题之前,让我们先搞清楚一些事情:
-
< L> >我只考虑Nopython代码,对象模式代码通常比纯Python/Nuffy等价物慢。
- 对于这个问题,我将忽略numba GPU的功能——很难将GPU上运行的代码与CPU上运行的代码进行比较
- 当您在numba函数中调用NumPy函数时,实际上并不是在调用NumPy函数。numba支持的一切都在numba中重新实现。这适用于NumPy函数,也适用于numba中的Python数据类型!因此,numba函数内部和外部的Python/NumPy实现细节可能不同,因为它们是完全不同的函数/类型
- Numba生成使用LLVM编译的代码。Numba不是魔术,它只是一个优化编译器的包装器,在Numba中内置了一些优化李>
numexpr
可以优化多个链式NumPy函数调用。目前,它要么是快速手动迭代(cython/numba),要么是使用表达式树(numexpr)优化链式NumPy调用。也许在一个图书馆里不可能同时做这两件事——我不知道
Numba和Cython在小型阵列和快速手动迭代阵列方面非常出色。NumPy/SciPy非常棒,因为它们提供了大量复杂的功能,可以完成各种开箱即用的任务。Numexpr非常适合链接多个NumPy函数调用。在某些情况下,Python比这些工具都快
根据我的经验,如果您编写不同的工具,您可以从中获得最佳效果。不要将自己局限于一种工具。在回答具体问题之前,让我们先了解一些事情:
-
< L> >我只考虑Nopython代码,对象模式代码通常比纯Python/Nuffy等价物慢。
- 对于这个问题,我将忽略numba GPU的功能——很难将GPU上运行的代码与CPU上运行的代码进行比较
- 当您在numba函数中调用NumPy函数时,实际上并不是在调用NumPy函数。numba支持的一切都在numba中重新实现。这适用于NumPy函数,也适用于numba中的Python数据类型!因此,numba函数内部和外部的Python/NumPy实现细节可能不同,因为它们是完全不同的函数/类型
- Numba生成使用LLVM编译的代码。Numba不是魔术,它只是一个优化编译器的包装器,在Numba中内置了一些优化李>
#only for single-threaded numpy test
import os
os.environ["OMP_NUM_THREADS"] = "1"
import numba as nb
import numpy as np
a=np.random.rand(100_000_000)
b=np.random.rand(100_000_000)
c=np.random.rand(100_000_000)
d=np.random.rand(100_000_000)
#Numpy version
#every expression is evaluated on its own
#the summation algorithm (Pairwise summation) isn't equivalent to the algorithm I used below
def Test_np(a,b,c,d):
return np.sum(a+b*2.+c*3.+d*4.)
#The same code, but for Numba (results and performance differ)
@nb.njit(fastmath=False,parallel=True)
def Test_np_nb(a,b,c,d):
return np.sum(a+b*2.+c*3.+d*4.)
#the summation isn't fused, aprox. the behaiviour of Test_np_nb for
#single threaded target
@nb.njit(fastmath=False,parallel=True)
def Test_np_nb_eq(a,b,c,d):
TMP=np.empty(a.shape[0])
for i in nb.prange(a.shape[0]):
TMP[i]=a[i]+b[i]*2.+c[i]*3.+d[i]*4.
res=0.
for i in nb.prange(a.shape[0]):
res+=TMP[i]
return res
#The usual way someone would implement this in Numba
@nb.njit(fastmath=False,parallel=True)
def Test_nb(a,b,c,d):
res=0.
for i in nb.prange(a.shape[0]):
res+=a[i]+b[i]*2.+c[i]*3.+d[i]*4.
return res
#single-threaded
%timeit res_1=Test_nb(a,b,c,d)
178 ms ± 1.28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res_2=Test_np(a,b,c,d)
2.72 s ± 118 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res_3=Test_np_nb(a,b,c,d)
562 ms ± 5.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res_4=Test_np_nb_eq(a,b,c,d)
612 ms ± 6.08 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
#single-threaded
#parallel=True
#nb.parfor.sequential_parfor_lowering = True
%timeit res_1=Test_nb(a,b,c,d)
188 ms ± 5.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit res_3=Test_np_nb(a,b,c,d)
184 ms ± 817 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit res_4=Test_np_nb_eq(a,b,c,d)
185 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
#multi-threaded
%timeit res_1=Test_nb(a,b,c,d)
105 ms ± 3.08 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res_2=Test_np(a,b,c,d)
1.78 s ± 75.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res_3=Test_np_nb(a,b,c,d)
102 ms ± 686 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit res_4=Test_np_nb_eq(a,b,c,d)
102 ms ± 1.48 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
#single-threaded
res_1=Test_nb(a,b,c,d)
499977967.27572954
res_2=Test_np(a,b,c,d)
499977967.2756622
res_3=Test_np_nb(a,b,c,d)
499977967.2756614
res_4=Test_np_nb_eq(a,b,c,d)
499977967.2756614
#multi-threaded
res_1=Test_nb(a,b,c,d)
499977967.27572465
res_2=Test_np(a,b,c,d)
499977967.2756622
res_3=Test_np_nb(a,b,c,d)
499977967.27572465
res_4=Test_np_nb_eq(a,b,c,d)
499977967.27572465