对于纯 numpy 代码,使用 numba 的收益在哪里?

     2023-02-16     111

关键词:

【中文标题】对于纯 numpy 代码,使用 numba 的收益在哪里?【英文标题】:Where are the gains using numba coming from for pure numpy code? 【发布时间】:2017-11-28 21:01:37 【问题描述】:

我想了解使用 Numba 在 for 循环中加速纯 numpy 代码时的收益来自哪里。是否有任何分析工具可以让您查看jitted 函数?

演示代码(如下)只是使用非常基本的矩阵乘法来为计算机提供工作。观察到的收益来自:

    更快的loop, 在编译过程中被jit拦截的numpy函数重铸,或者 jit 的开销更少,因为 numpy 通过包装函数将执行外包给低级库,例如 LINPACK
%matplotlib inline
import numpy as np
from numba import jit
import pandas as pd

#Dimensions of Matrices
i = 100 
j = 100

def pure_python(N,i,j):
    for n in range(N):
        a = np.random.rand(i,j)
        b = np.random.rand(i,j)
        c = np.dot(a,b)

@jit(nopython=True)
def jit_python(N,i,j):
    for n in range(N):
        a = np.random.rand(i,j)
        b = np.random.rand(i,j)
        c = np.dot(a,b)

time_python = []
time_jit = []
N = [1,10,100,500,1000,2000]
for n in N:
    time = %timeit -oq pure_python(n,i,j)
    time_python.append(time.average)
    time = %timeit -oq jit_python(n,i,j)
    time_jit.append(time.average)

df = pd.DataFrame('pure_python' : time_python, 'jit_python' : time_jit, index=N)
df.index.name = 'Iterations'
df[["pure_python", "jit_python"]].plot()

生成以下图表。

【问题讨论】:

我认为 Numba 可以识别 np.random.randnp.dot。 (如果没有,我认为它不会让您在 nopython 模式下使用它们。) 确实如此。根据文档,numba 支持它们。 numba.pydata.org/numba-doc/dev/reference/numpysupported.html。我主要好奇代码拦截是如何工作的,以及这是否是上例中的收益来源。 您能添加一些设置信息吗?在 Win 64、python 3.5、numba 0.33 上,我只有适度的加速(10-15%) 当然。我在 Linux Mint 18、Linux Kernel 4.4.0-45-generic、python 3.5、numba 0.30.1、Intel Xeon CPU E5-1620 @ 3.6Ghz x 4 据我所知,答案是 1) 和 2)。 numba 将函数编译为 c 代码。因此,它显着加快了循环解析,并以显着的python 开销加速了numpy 函数(通常通过剥离该开销并强制显式数据排序 - 即没有axis 关键字,没有einsum,没有@大多数构造函数上的 987654341@ 参数(random.rand 是一个例外)...所有这些事情都可以在现在更快的 for 循环中显式完成) 【参考方案1】:

TL:DR 随机和循环得到加速,但矩阵乘法除了小矩阵大小之外没有。在较小的矩阵/循环大小下,似乎有可能与 python 开销有关的显着加速。在 N 较大时,矩阵乘法开始占主导地位,而 jit 的帮助不大

函数定义,为简单起见使用方阵。

from IPython.display import display
import numpy as np
from numba import jit
import pandas as pd

#Dimensions of Matrices
N = 1000

def py_rand(i, j):
    a = np.random.rand(i, j)

jit_rand = jit(nopython=True)(py_rand)

def py_matmul(a, b):
    c = np.dot(a, b)

jit_matmul = jit(nopython=True)(py_matmul)

def py_loop(N, val):
    count = 0
    for i in range(N):
        count += val     


jit_loop = jit(nopython=True)(py_loop)      

def pure_python(N,i,j):
    for n in range(N):
        a = np.random.rand(i,j)
        b = np.random.rand(i,j)
        c = np.dot(a,a)

jit_func = jit(nopython=True)(pure_python)

时间:

df = pd.DataFrame(columns=['Func', 'jit', 'N', 'Time'])
def meantime(f, *args, **kwargs):
    t = %timeit -oq -n5 f(*args, **kwargs)
    return t.average


for N in [10, 100, 1000, 2000]:
    a = np.random.randn(N, N)
    b = np.random.randn(N, N)

    df = df.append('Func': 'jit_rand', 'N': N, 'Time': meantime(jit_rand, N, N), ignore_index=True)
    df = df.append('Func': 'py_rand', 'N': N, 'Time': meantime(py_rand, N, N), ignore_index=True)

    df = df.append('Func': 'jit_matmul', 'N': N, 'Time': meantime(jit_matmul, a, b), ignore_index=True)
    df = df.append('Func': 'py_matmul', 'N': N, 'Time': meantime(py_matmul, a, b), ignore_index=True)

    df = df.append('Func': 'jit_loop', 'N': N, 'Time': meantime(jit_loop, N, 2.0), ignore_index=True)
    df = df.append('Func': 'py_loop', 'N': N, 'Time': meantime(py_loop, N, 2.0), ignore_index=True)

    df = df.append('Func': 'jit_func', 'N': N, 'Time': meantime(jit_func, 5, N, N), ignore_index=True)
    df = df.append('Func': 'py_func', 'N': N, 'Time': meantime(pure_python, 5, N, N), ignore_index=True)

df['jit'] = df['Func'].str.contains('jit')
df['Func'] = df['Func'].apply(lambda s: s.split('_')[1])
df.set_index('Func')
display(df)

结果:

    Func    jit     N   Time
0   rand    True    10  1.030686e-06
1   rand    False   10  1.115149e-05
2   matmul  True    10  2.250371e-06
3   matmul  False   10  2.199343e-06
4   loop    True    10  2.706000e-07
5   loop    False   10  7.274286e-07
6   func    True    10  1.217046e-05
7   func    False   10  2.495837e-05
8   rand    True    100 5.199217e-05
9   rand    False   100 8.149794e-05
10  matmul  True    100 7.848071e-05
11  matmul  False   100 2.130794e-05
12  loop    True    100 2.728571e-07
13  loop    False   100 3.003743e-06
14  func    True    100 6.739634e-04
15  func    False   100 1.146594e-03
16  rand    True    1000    5.644258e-03
17  rand    False   1000    8.012790e-03
18  matmul  True    1000    1.476098e-02
19  matmul  False   1000    1.613211e-02
20  loop    True    1000    2.846572e-07
21  loop    False   1000    3.539849e-05
22  func    True    1000    1.256926e-01
23  func    False   1000    1.581177e-01
24  rand    True    2000    2.061612e-02
25  rand    False   2000    3.204709e-02
26  matmul  True    2000    9.866484e-02
27  matmul  False   2000    1.007234e-01
28  loop    True    2000    3.011143e-07
29  loop    False   2000    7.477454e-05
30  func    True    2000    1.033560e+00
31  func    False   2000    1.199969e+00

看起来 numba 正在优化循环,所以我不会费心将它包含在比较中

情节:

def jit_speedup(d):
    py_time = d[d['jit'] == False]['Time'].mean()
    jit_time = d[d['jit'] == True]['Time'].mean()
    return py_time / jit_time 

import seaborn as sns
result = df.groupby(['Func', 'N']).apply(jit_speedup).reset_index().rename(columns=0: 'Jit Speedup')
result = result[result['Func'] != 'loop']
sns.factorplot(data=result, x='N', y='Jit Speedup', hue='Func')

因此,对于 5 次重复的循环,jit 可以相当稳定地加快速度,直到矩阵乘法变得足够昂贵,以使其他开销相比之下变得微不足道。

【讨论】:

您可能对修复def py_loop(): 代码感兴趣,因为原来的代码主要是一个O( 1 ) 缩放(从概念上讲,可能是一个被忽视的快捷方式/屏蔽变量错误,独立于 N ),它使实验偏向于返回总是只是 ~ 163 - 294 [ns] 处理持续时间(这证实了只是“恒定”的普通呼叫签名处理开销 + JMP / RET 持续时间,不是任何N-times 循环代码执行)。 不,你必须返回一个值 - 一个“那里”产生的值,否则 JIT 编译器分析看不出有任何明显的原因使硅循环如此多次通过“静默”代码,如果它什么都不返回... 更好地设计numba.jit(...) test-payload 更仔细,否则你再次诉诸不测试任何合理的O /P. 我最初对此很担心,但由于没有一个函数具有恒定的时间缩放(它们都没有返回),我认为 numba jit 实际上并没有完全优化代码。我可能错了。我不想将时间与返回、类型转换等混为一谈。

尝试使用 numba 循环 numpy 数组时出错

...你好!我正在尝试使用numba来加快numpy数组的循环。我的代码:@nb.njitdef_complicated(heat_list_,raw_array_,x_min_,x_step_,y_min_,y_step_): 查看详情

各种 numpy 花式索引方法的性能,也与 numba

...withnumba【发布时间】:2018-02-1223:20:44【问题描述】:因为对于我的程序,Numpy数组的快速索引是非常必要的,而且考虑到性能,花哨的索引并没有很好的声誉,所以我决定进行一些测试。尤其是Numba发展很快,我尝试了哪些方法... 查看详情

为啥在迭代 NumPy 数组时 Cython 比 Numba 慢得多?

】为啥在迭代NumPy数组时Cython比Numba慢得多?【英文标题】:WhyisCythonsomuchslowerthanNumbawheniteratingoverNumPyarrays?为什么在迭代NumPy数组时Cython比Numba慢得多?【发布时间】:2019-04-0918:43:36【问题描述】:在遍历NumPy数组时,Numba似乎比Cy... 查看详情

使用 numba 无法获得与 numpy 元素矩阵乘法相同的值

】使用numba无法获得与numpy元素矩阵乘法相同的值【英文标题】:Can\'tgetsamevaluesasnumpyelementwisematrixmultiplicationusingnumba【发布时间】:2018-03-2218:01:20【问题描述】:我一直在玩numba并尝试实现一个简单的元素矩阵乘法。使用“vectoriz... 查看详情

强化学习技巧五:numba提速python程序(代码片段)

numba是一款可以将python函数编译为机器代码的JIT编译器,经过numba编译的python代码(仅限数组运算),其运行速度可以接近C或FORTRAN语言。numba使用情况使用numpy数组做大量科学计算时使用for循环时1.numba使用导入numpy... 查看详情

Numba:@jit 中的并行标志在我的带有 numpy 2D 数组的代码中不起作用

】Numba:@jit中的并行标志在我的带有numpy2D数组的代码中不起作用【英文标题】:Numba:parallelflagin@jitdoesnotworkinmycodewithnumpy2Darrays【发布时间】:2022-01-1703:48:59【问题描述】:首先,我问了这个问题Numba:Whyguvectorizeissoslow?。当我发现... 查看详情

为啥这个 numba 代码比 numpy 代码慢 6 倍?

】为啥这个numba代码比numpy代码慢6倍?【英文标题】:Whythisnumbacodeis6xslowerthannumpycode?为什么这个numba代码比numpy代码慢6倍?【发布时间】:2018-11-1212:11:56【问题描述】:下面的代码运行2s有什么原因吗,defeuclidean_distance_square(x1,x2)... 查看详情

为啥在这里 numba 比 numpy 快?

】为啥在这里numba比numpy快?【英文标题】:Whyisnumbafasterthannumpyhere?为什么在这里numba比numpy快?【发布时间】:2014-11-1501:16:57【问题描述】:我不明白为什么numba在这里击败了numpy(超过3倍)。我在这里进行基准测试时是否犯了... 查看详情

numba 不编译带有 numpy 数组的函数

】numba不编译带有numpy数组的函数【英文标题】:numbadoesnotcompileafunctionwithnumpyarrays【发布时间】:2020-10-2710:37:50【问题描述】:这是我的功能def_hargreaves_samani_02(r0,im,tmax,tmin,tavg):"""r0andimarenumpy1Darraydtype=int32tmax,tmin,tavgarenumpy1Darra 查看详情

没有 Numpy 的矩阵求逆

...linalg.inv的情况下反转矩阵。原因是我正在使用Numba来加速代码,但不支持numpy.linalg.inv,所以我想知道是否可以使用“经典”Python代码反转矩阵。使用numpy.linalg.inv的示例代码如下所示:importnumpyas 查看详情

Python numpy:无法将 datetime64[ns] 转换为 datetime64[D](与 Numba 一起使用)

...ythonnumpy:无法将datetime64[ns]转换为datetime64[D](与Numba一起使用)【英文标题】:Pythonnumpy:cannotconvertdatetime64[ns]todatetime64[D](tousewithNumba)【发布时间】:2015-11-0206:33:44【问题描述】:我想将一个日期时间数组传递给一个Numba函数(它... 查看详情

比较 Python、Numpy、Numba 和 C++ 的矩阵乘法

】比较Python、Numpy、Numba和C++的矩阵乘法【英文标题】:ComparingPython,Numpy,NumbaandC++formatrixmultiplication【发布时间】:2016-07-3107:36:59【问题描述】:在我正在处理的程序中,我需要重复将两个矩阵相乘。由于其中一个矩阵的大小,此... 查看详情

使用numba加速opencvpython视频流代码。提升6.5倍性能(代码片段)

...实际效果如何呢?本文将通过实例代码来比较,对于OpenCV显示与处理视频流的代码,未优化前,与Numba优化后的速度来进行对比分析。步骤1:项目要求OpenCV中的视频帧都是由NumPy数组表示的图像。在此示例中&#x... 查看详情

与纯 Python 代码相比,Numba njit 编译器会导致计算不同的数字?

】与纯Python代码相比,Numbanjit编译器会导致计算不同的数字?【英文标题】:NumbanjitcompilercausescomputesdifferentnumberscomparedtoplainPythoncode?【发布时间】:2021-12-2914:05:47【问题描述】:我在Python中遇到了Numba的njit工具的问题。我注意... 查看详情

与 Python+Numba LLVM/JIT 编译的代码相比,Julia 的性能

】与Python+NumbaLLVM/JIT编译的代码相比,Julia的性能【英文标题】:JuliaperformancecomparedtoPython+NumbaLLVM/JIT-compiledcode【发布时间】:2015-06-1510:15:03【问题描述】:到目前为止我看到的Julia的性能基准,例如在http://julialang.org/,将Julia与... 查看详情

为啥 np.hypot 和 np.subtract.outer 与香草广播相比非常快?使用 Numba 并行加速 numpy 进行距离矩阵计算

】为啥np.hypot和np.subtract.outer与香草广播相比非常快?使用Numba并行加速numpy进行距离矩阵计算【英文标题】:Whynp.hypotandnp.subtract.outerveryfastcomparedtovanillabroadcast?UsingNumbaforspeedupnumpyinparallelfordistancematrixcalculation为什么np.hypot和np.subtr 查看详情

使用数组进行 Numpy 过滤

...705:05:01【问题描述】:我知道之前有人问过这个问题,但对于我的特定用例似乎没有任何内容。我有一个numpy数组obs,它代表彩色图像,形状为(252,288,3)。我想将不是纯黑色的每个像素都转换为纯白色。我尝试过的是obs[obs!=[0,0,0]]... 查看详情

在python中使用numba自引用类似结构的对象

...能快。在尝试了像concurrent.futures这样的多个选项之后,它对于我来说仍然有点太慢了,但我一直在尝试使用Numba来让它更快。有一小部 查看详情